agdar/notifications/views.py
Marwan Alwali a4665842c9 update
2025-11-23 10:58:07 +03:00

960 lines
34 KiB
Python

"""
Notifications views for the Tenhal Multidisciplinary Healthcare Platform.
This module contains views for notification management including:
- Message dashboard with statistics
- Message list and detail views
- Template management (CRUD)
- Bulk messaging interface
- Analytics and reports
"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q, Count, Avg
from django.http import JsonResponse, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.views import View
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from django.contrib import messages
from datetime import timedelta
import json
import csv
from core.mixins import (
TenantFilterMixin,
RolePermissionMixin,
AuditLogMixin,
HTMXResponseMixin,
SuccessMessageMixin,
PaginationMixin,
)
from core.models import User, Patient
from .models import MessageTemplate, Message, NotificationPreference, MessageLog
from .forms import (
MessageTemplateForm,
MessageFilterForm,
BulkMessageForm,
TestTemplateForm,
MessageRetryForm,
BroadcastNotificationForm,
)
# ============================================================================
# Dashboard Views
# ============================================================================
class MessageDashboardView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin, View):
"""
Message dashboard with statistics and overview.
Features:
- Key metrics (total sent, success rate, etc.)
- Recent messages
- Charts for delivery rates
- Quick actions
"""
allowed_roles = [User.Role.ADMIN, User.Role.FRONT_DESK]
def get(self, request):
"""Display dashboard."""
tenant = request.user.tenant
# Get date range (default: last 7 days)
days = int(request.GET.get('days', 7))
since = timezone.now() - timedelta(days=days)
# Get statistics
stats = self._get_statistics(tenant, since)
# Get recent messages
recent_messages = Message.objects.filter(
tenant=tenant
).select_related('template').order_by('-created_at')[:10]
# Get failed messages that can be retried
failed_messages = Message.objects.filter(
tenant=tenant,
status=Message.Status.FAILED,
retry_count__lt=3
).count()
context = {
'stats': stats,
'recent_messages': recent_messages,
'failed_messages_count': failed_messages,
'days': days,
}
return render(request, 'notifications/dashboard.html', context)
def _get_statistics(self, tenant, since):
"""Calculate messaging statistics."""
messages_qs = Message.objects.filter(tenant=tenant, created_at__gte=since)
total = messages_qs.count()
# Count by status
sent = messages_qs.filter(status=Message.Status.SENT).count()
delivered = messages_qs.filter(status=Message.Status.DELIVERED).count()
failed = messages_qs.filter(status=Message.Status.FAILED).count()
queued = messages_qs.filter(status=Message.Status.QUEUED).count()
# Calculate success rate
success_rate = 0
if total > 0:
successful = messages_qs.filter(
status__in=[Message.Status.DELIVERED, Message.Status.READ]
).count()
success_rate = round((successful / total) * 100, 1)
# Count by channel
by_channel = {}
for channel in Message.Channel:
count = messages_qs.filter(channel=channel.value).count()
by_channel[channel.value] = count
# Get daily breakdown for chart
daily_stats = []
for i in range(7):
date = timezone.now().date() - timedelta(days=6-i)
day_messages = messages_qs.filter(created_at__date=date)
daily_stats.append({
'date': date.strftime('%Y-%m-%d'),
'total': day_messages.count(),
'delivered': day_messages.filter(status=Message.Status.DELIVERED).count(),
'failed': day_messages.filter(status=Message.Status.FAILED).count(),
})
return {
'total': total,
'sent': sent,
'delivered': delivered,
'failed': failed,
'queued': queued,
'success_rate': success_rate,
'by_channel': by_channel,
'daily_stats': daily_stats,
}
# ============================================================================
# Message List and Detail Views
# ============================================================================
class MessageListView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin,
PaginationMixin, HTMXResponseMixin, ListView):
"""
Message list view with filtering and search.
Features:
- Filter by channel, status, date range, template
- Search by recipient or content
- Export to CSV
- Bulk retry failed messages
"""
model = Message
template_name = 'notifications/message_list.html'
htmx_template_name = 'notifications/partials/message_list_partial.html'
context_object_name = 'messages'
paginate_by = 25
allowed_roles = [User.Role.ADMIN, User.Role.FRONT_DESK]
def get_queryset(self):
"""Get filtered queryset."""
queryset = super().get_queryset()
# Apply search
search_query = self.request.GET.get('search', '').strip()
if search_query:
queryset = queryset.filter(
Q(recipient__icontains=search_query) |
Q(body__icontains=search_query) |
Q(subject__icontains=search_query)
)
# Apply filters
channel = self.request.GET.get('channel')
if channel:
queryset = queryset.filter(channel=channel)
status = self.request.GET.get('status')
if status:
queryset = queryset.filter(status=status)
template_id = self.request.GET.get('template')
if template_id:
queryset = queryset.filter(template_id=template_id)
date_from = self.request.GET.get('date_from')
if date_from:
queryset = queryset.filter(created_at__date__gte=date_from)
date_to = self.request.GET.get('date_to')
if date_to:
queryset = queryset.filter(created_at__date__lte=date_to)
return queryset.select_related('template').order_by('-created_at')
def get_context_data(self, **kwargs):
"""Add filter form and statistics."""
context = super().get_context_data(**kwargs)
# Add filter form
context['filter_form'] = MessageFilterForm(
self.request.GET,
tenant=self.request.user.tenant
)
# Add current filters
context['current_filters'] = {
'search': self.request.GET.get('search', ''),
'channel': self.request.GET.get('channel', ''),
'status': self.request.GET.get('status', ''),
'template': self.request.GET.get('template', ''),
'date_from': self.request.GET.get('date_from', ''),
'date_to': self.request.GET.get('date_to', ''),
}
# Add quick stats
queryset = self.get_queryset()
context['total_count'] = queryset.count()
context['failed_count'] = queryset.filter(status=Message.Status.FAILED).count()
return context
class MessageDetailView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin, DetailView):
"""
Message detail view.
Features:
- Full message details
- Delivery timeline
- Provider response
- Retry history
- Related logs
"""
model = Message
template_name = 'notifications/message_detail.html'
context_object_name = 'message'
allowed_roles = [User.Role.ADMIN, User.Role.FRONT_DESK]
def get_context_data(self, **kwargs):
"""Add related data."""
context = super().get_context_data(**kwargs)
message = self.object
# Get message logs
context['logs'] = message.logs.all().order_by('created_at')
# Check if can retry
context['can_retry'] = message.can_retry
# Get timeline events
context['timeline'] = self._build_timeline(message)
return context
def _build_timeline(self, message):
"""Build timeline of message events."""
timeline = []
# Created
timeline.append({
'timestamp': message.created_at,
'event': 'Created',
'icon': 'fa-plus-circle',
'color': 'info'
})
# Sent
if message.sent_at:
timeline.append({
'timestamp': message.sent_at,
'event': 'Sent',
'icon': 'fa-paper-plane',
'color': 'primary'
})
# Delivered
if message.delivered_at:
timeline.append({
'timestamp': message.delivered_at,
'event': 'Delivered',
'icon': 'fa-check-circle',
'color': 'success'
})
# Failed
if message.status == Message.Status.FAILED:
timeline.append({
'timestamp': message.updated_at,
'event': 'Failed',
'icon': 'fa-exclamation-circle',
'color': 'danger',
'details': message.error_message
})
return sorted(timeline, key=lambda x: x['timestamp'])
class MessageExportView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin, View):
"""Export messages to CSV."""
allowed_roles = [User.Role.ADMIN, User.Role.FRONT_DESK]
def get(self, request):
"""Export filtered messages to CSV."""
# Get filtered queryset (reuse MessageListView logic)
queryset = Message.objects.filter(tenant=request.user.tenant)
# Apply same filters as list view
search_query = request.GET.get('search', '').strip()
if search_query:
queryset = queryset.filter(
Q(recipient__icontains=search_query) |
Q(body__icontains=search_query)
)
channel = request.GET.get('channel')
if channel:
queryset = queryset.filter(channel=channel)
status = request.GET.get('status')
if status:
queryset = queryset.filter(status=status)
# Create CSV response
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = f'attachment; filename="messages_{timezone.now().strftime("%Y%m%d_%H%M%S")}.csv"'
writer = csv.writer(response)
writer.writerow([
'Date', 'Channel', 'Recipient', 'Status', 'Template',
'Sent At', 'Delivered At', 'Error Message'
])
for message in queryset.select_related('template').order_by('-created_at'):
writer.writerow([
message.created_at.strftime('%Y-%m-%d %H:%M:%S'),
message.get_channel_display(),
message.recipient,
message.get_status_display(),
message.template.name if message.template else '',
message.sent_at.strftime('%Y-%m-%d %H:%M:%S') if message.sent_at else '',
message.delivered_at.strftime('%Y-%m-%d %H:%M:%S') if message.delivered_at else '',
message.error_message
])
return response
class MessageRetryView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin,
AuditLogMixin, View):
"""Retry failed message."""
allowed_roles = [User.Role.ADMIN, User.Role.FRONT_DESK]
def post(self, request, pk):
"""Retry sending a failed message."""
from integrations.messaging_service import MessagingService
message = get_object_or_404(Message, pk=pk, tenant=request.user.tenant)
if not message.can_retry:
messages.error(request, 'This message cannot be retried.')
return redirect('notifications:message_detail', pk=pk)
# Retry message
service = MessagingService()
result = service.retry_failed_message(str(message.id))
if result['success']:
messages.success(request, 'Message retry initiated successfully!')
else:
messages.error(request, f'Retry failed: {result.get("error")}')
return redirect('notifications:message_detail', pk=pk)
# ============================================================================
# Template Management Views
# ============================================================================
class TemplateListView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin,
PaginationMixin, ListView):
"""
Template list view.
Features:
- List all templates
- Filter by channel, active status
- Quick activate/deactivate
"""
model = MessageTemplate
template_name = 'notifications/template_list.html'
context_object_name = 'templates'
paginate_by = 20
allowed_roles = [User.Role.ADMIN, User.Role.FRONT_DESK]
def get_queryset(self):
"""Get filtered queryset."""
queryset = super().get_queryset()
# Apply filters
channel = self.request.GET.get('channel')
if channel:
queryset = queryset.filter(channel=channel)
is_active = self.request.GET.get('is_active')
if is_active:
queryset = queryset.filter(is_active=is_active == 'true')
return queryset.order_by('channel', 'name')
def get_context_data(self, **kwargs):
"""Add filter options."""
context = super().get_context_data(**kwargs)
context['channel_choices'] = MessageTemplate.Channel.choices
return context
class TemplateDetailView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin, DetailView):
"""Template detail view with preview."""
model = MessageTemplate
template_name = 'notifications/template_detail.html'
context_object_name = 'template'
allowed_roles = [User.Role.ADMIN, User.Role.FRONT_DESK]
def get_context_data(self, **kwargs):
"""Add usage statistics."""
context = super().get_context_data(**kwargs)
template = self.object
# Get usage statistics
context['usage_stats'] = {
'total_sent': template.messages.count(),
'successful': template.messages.filter(
status__in=[Message.Status.DELIVERED, Message.Status.READ]
).count(),
'failed': template.messages.filter(status=Message.Status.FAILED).count(),
}
# Get recent messages using this template
context['recent_messages'] = template.messages.all().order_by('-created_at')[:5]
return context
class TemplateCreateView(LoginRequiredMixin, RolePermissionMixin, AuditLogMixin,
SuccessMessageMixin, CreateView):
"""Create new message template."""
model = MessageTemplate
form_class = MessageTemplateForm
template_name = 'notifications/template_form.html'
success_message = "Template created successfully!"
allowed_roles = [User.Role.ADMIN]
def form_valid(self, form):
"""Set tenant."""
form.instance.tenant = self.request.user.tenant
return super().form_valid(form)
def get_success_url(self):
"""Redirect to template detail."""
return reverse_lazy('notifications:template_detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
"""Add form title."""
context = super().get_context_data(**kwargs)
context['form_title'] = 'Create Message Template'
context['submit_text'] = 'Create Template'
return context
class TemplateUpdateView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin,
AuditLogMixin, SuccessMessageMixin, UpdateView):
"""Update message template."""
model = MessageTemplate
form_class = MessageTemplateForm
template_name = 'notifications/template_form.html'
success_message = "Template updated successfully!"
allowed_roles = [User.Role.ADMIN]
def get_success_url(self):
"""Redirect to template detail."""
return reverse_lazy('notifications:template_detail', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
"""Add form title."""
context = super().get_context_data(**kwargs)
context['form_title'] = f'Update Template: {self.object.name}'
context['submit_text'] = 'Update Template'
return context
class TemplateDeleteView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin,
AuditLogMixin, DeleteView):
"""Delete message template."""
model = MessageTemplate
template_name = 'notifications/template_confirm_delete.html'
success_url = reverse_lazy('notifications:template_list')
allowed_roles = [User.Role.ADMIN]
def delete(self, request, *args, **kwargs):
"""Add success message."""
messages.success(request, 'Template deleted successfully!')
return super().delete(request, *args, **kwargs)
class TemplateToggleView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin,
AuditLogMixin, View):
"""Toggle template active status."""
allowed_roles = [User.Role.ADMIN]
def post(self, request, pk):
"""Toggle is_active status."""
template = get_object_or_404(MessageTemplate, pk=pk, tenant=request.user.tenant)
template.is_active = not template.is_active
template.save()
status = 'activated' if template.is_active else 'deactivated'
messages.success(request, f'Template {status} successfully!')
return redirect('notifications:template_detail', pk=pk)
class TemplateTestView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin, View):
"""Test message template."""
allowed_roles = [User.Role.ADMIN, User.Role.FRONT_DESK]
def get(self, request):
"""Display test form."""
form = TestTemplateForm(tenant=request.user.tenant)
return render(request, 'notifications/template_test.html', {'form': form})
def post(self, request):
"""Send test message."""
from integrations.messaging_service import MessagingService
form = TestTemplateForm(request.POST, tenant=request.user.tenant)
if form.is_valid():
template = form.cleaned_data['template']
recipient = form.cleaned_data['test_recipient']
language = form.cleaned_data['language']
variables = form.cleaned_data['variables']
# Send test message
service = MessagingService()
result = service.send_from_template(
template_code=template.code,
recipient_phone=recipient,
channel=template.channel,
context=variables,
tenant_id=str(request.user.tenant.id),
language=language
)
if result['success']:
messages.success(request, f'Test message sent successfully! Message ID: {result["message_id"]}')
else:
messages.error(request, f'Failed to send test message: {result.get("error")}')
return render(request, 'notifications/template_test.html', {'form': form})
# ============================================================================
# Bulk Messaging Views
# ============================================================================
class BulkMessageView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin, View):
"""
Bulk messaging interface.
Features:
- Select recipients (all, by tags, custom list)
- Use template or custom message
- Preview before sending
- Track progress
"""
allowed_roles = [User.Role.ADMIN, User.Role.FRONT_DESK]
def get(self, request):
"""Display bulk message form."""
form = BulkMessageForm(tenant=request.user.tenant)
return render(request, 'notifications/bulk_message.html', {'form': form})
def post(self, request):
"""Process bulk message."""
from integrations.messaging_service import MessagingService
form = BulkMessageForm(request.POST, tenant=request.user.tenant)
if form.is_valid():
# Get recipients
recipients = self._get_recipients(form.cleaned_data)
if not recipients:
messages.error(request, 'No recipients found.')
return render(request, 'notifications/bulk_message.html', {'form': form})
# Get message content
channel = form.cleaned_data['channel']
use_template = form.cleaned_data['use_template']
if use_template:
template = form.cleaned_data['template']
# TODO: Implement template-based bulk sending
messages.info(request, 'Template-based bulk sending not yet implemented.')
else:
message_body = form.cleaned_data['message']
# Send bulk messages
service = MessagingService()
result = service.send_bulk_messages(
recipients=recipients,
message=message_body,
channel=channel,
tenant_id=str(request.user.tenant.id)
)
messages.success(
request,
f'Bulk send completed! Sent: {result["sent"]}, Failed: {result["failed"]}'
)
return redirect('notifications:message_list')
return render(request, 'notifications/bulk_message.html', {'form': form})
def _get_recipients(self, data):
"""Get list of recipients based on filter."""
recipient_filter = data['recipient_filter']
if recipient_filter == 'all':
# Get all patients with phone numbers
patients = Patient.objects.filter(
tenant=self.request.user.tenant,
phone__isnull=False
).exclude(phone='')
return [p.phone for p in patients]
elif recipient_filter == 'tags':
# Get patients by tags
tags = [t.strip() for t in data['tags'].split(',')]
patients = Patient.objects.filter(
tenant=self.request.user.tenant,
tags__name__in=tags,
phone__isnull=False
).exclude(phone='').distinct()
return [p.phone for p in patients]
elif recipient_filter == 'custom':
# Parse custom recipient list
recipients = data['recipients'].strip().split('\n')
return [r.strip() for r in recipients if r.strip()]
return []
# ============================================================================
# Analytics Views
# ============================================================================
class MessageAnalyticsView(LoginRequiredMixin, RolePermissionMixin, TenantFilterMixin, View):
"""
Message analytics and reports.
Features:
- Delivery rate charts
- Channel comparison
- Cost analysis
- Trend analysis
"""
allowed_roles = [User.Role.ADMIN]
def get(self, request):
"""Display analytics dashboard."""
tenant = request.user.tenant
# Get date range
days = int(request.GET.get('days', 30))
since = timezone.now() - timedelta(days=days)
# Get analytics data
analytics = self._get_analytics(tenant, since, days)
context = {
'analytics': analytics,
'days': days,
}
return render(request, 'notifications/analytics.html', context)
def _get_analytics(self, tenant, since, days):
"""Calculate analytics data."""
messages_qs = Message.objects.filter(tenant=tenant, created_at__gte=since)
# Overall statistics
total = messages_qs.count()
successful = messages_qs.filter(
status__in=[Message.Status.DELIVERED, Message.Status.READ]
).count()
# Channel breakdown
channel_stats = []
for channel in Message.Channel:
channel_messages = messages_qs.filter(channel=channel.value)
channel_total = channel_messages.count()
channel_success = channel_messages.filter(
status__in=[Message.Status.DELIVERED, Message.Status.READ]
).count()
channel_stats.append({
'channel': channel.label,
'total': channel_total,
'successful': channel_success,
'success_rate': round((channel_success / channel_total * 100), 1) if channel_total > 0 else 0
})
# Daily trend
daily_trend = []
for i in range(days):
date = timezone.now().date() - timedelta(days=days-1-i)
day_messages = messages_qs.filter(created_at__date=date)
daily_trend.append({
'date': date.strftime('%Y-%m-%d'),
'total': day_messages.count(),
'successful': day_messages.filter(
status__in=[Message.Status.DELIVERED, Message.Status.READ]
).count(),
})
# Top templates
top_templates = MessageTemplate.objects.filter(
tenant=tenant,
messages__created_at__gte=since
).annotate(
usage_count=Count('messages')
).order_by('-usage_count')[:5]
return {
'total': total,
'successful': successful,
'success_rate': round((successful / total * 100), 1) if total > 0 else 0,
'channel_stats': channel_stats,
'daily_trend': daily_trend,
'top_templates': top_templates,
}
# ============================================================================
# Notification Center Views (In-App Notifications)
# ============================================================================
class NotificationListView(LoginRequiredMixin, ListView):
"""
List all notifications for the current user.
Features:
- Show unread notifications first
- Filter by type
- Mark as read/unread
- Pagination
- Includes personal, general, and role-based notifications
"""
model = None # Will be set in get_queryset
template_name = 'notifications/notification_list.html'
context_object_name = 'notifications'
paginate_by = 20
def get_queryset(self):
"""Get notifications for current user (personal, general, and role-based)."""
from .models import Notification
queryset = Notification.get_for_user(self.request.user)
# Filter by read status
filter_type = self.request.GET.get('filter', 'all')
if filter_type == 'unread':
queryset = queryset.filter(is_read=False)
elif filter_type == 'read':
queryset = queryset.filter(is_read=True)
# Filter by notification type
notif_type = self.request.GET.get('type')
if notif_type:
queryset = queryset.filter(notification_type=notif_type)
return queryset.order_by('-created_at')
def get_context_data(self, **kwargs):
"""Add unread count and filter info."""
context = super().get_context_data(**kwargs)
from .models import Notification
context['unread_count'] = Notification.get_unread_count(self.request.user)
context['current_filter'] = self.request.GET.get('filter', 'all')
context['current_type'] = self.request.GET.get('type', '')
return context
class NotificationMarkReadView(LoginRequiredMixin, View):
"""Mark a notification as read."""
def post(self, request, pk):
"""Mark notification as read."""
from .models import Notification
notification = get_object_or_404(Notification, pk=pk, user=request.user)
notification.mark_as_read()
# Return JSON for AJAX requests
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return JsonResponse({
'success': True,
'unread_count': Notification.get_unread_count(request.user)
})
# Redirect to next URL or notification list
next_url = request.GET.get('next', 'notifications:notification_list')
return redirect(next_url)
class NotificationMarkAllReadView(LoginRequiredMixin, View):
"""Mark all notifications as read for current user."""
def post(self, request):
"""Mark all notifications as read."""
from .models import Notification
Notification.mark_all_as_read(request.user)
# Return JSON for AJAX requests
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return JsonResponse({
'success': True,
'unread_count': 0
})
messages.success(request, 'All notifications marked as read.')
return redirect('notifications:notification_list')
class NotificationUnreadCountView(LoginRequiredMixin, View):
"""Get unread notification count (for AJAX polling)."""
def get(self, request):
"""Return unread count as JSON."""
from .models import Notification
unread_count = Notification.get_unread_count(request.user)
return JsonResponse({
'unread_count': unread_count
})
class NotificationDropdownView(LoginRequiredMixin, View):
"""Get recent notifications for dropdown (AJAX)."""
def get(self, request):
"""Return recent notifications as JSON."""
from .models import Notification
notifications = Notification.get_for_user(request.user).order_by('-created_at')[:10]
data = {
'unread_count': Notification.get_unread_count(request.user),
'notifications': [
{
'id': str(n.id),
'title': n.title,
'message': n.message[:100] + '...' if len(n.message) > 100 else n.message,
'type': n.notification_type,
'is_read': n.is_read,
'created_at': n.created_at.isoformat(),
'action_url': n.action_url or '#',
}
for n in notifications
]
}
return JsonResponse(data)
class BroadcastNotificationCreateView(LoginRequiredMixin, RolePermissionMixin,
AuditLogMixin, View):
"""
Create broadcast notifications (general or role-based).
Only admins can create broadcast notifications.
"""
allowed_roles = [User.Role.ADMIN]
def get(self, request):
"""Display broadcast notification form."""
form = BroadcastNotificationForm()
return render(request, 'notifications/broadcast_notification_form.html', {
'form': form,
'form_title': 'Create Broadcast Notification',
})
def post(self, request):
"""Create broadcast notification."""
from .models import Notification
form = BroadcastNotificationForm(request.POST)
if form.is_valid():
broadcast_type = form.cleaned_data['broadcast_type']
title = form.cleaned_data['title']
message = form.cleaned_data['message']
notification_type = form.cleaned_data['notification_type']
action_url = form.cleaned_data['action_url']
if broadcast_type == 'general':
# Create general notification
notification = Notification.create_general(
title=title,
message=message,
notification_type=notification_type,
action_url=action_url
)
messages.success(
request,
f'General notification created successfully! All users will see this notification.'
)
else:
# Create role-based notification
target_roles = form.cleaned_data['target_roles']
notification = Notification.create_role_based(
roles=target_roles,
title=title,
message=message,
notification_type=notification_type,
action_url=action_url
)
role_names = ', '.join([dict(User.Role.choices).get(r, r) for r in target_roles])
messages.success(
request,
f'Role-based notification created successfully! Visible to: {role_names}'
)
return redirect('notifications:notification_list')
return render(request, 'notifications/broadcast_notification_form.html', {
'form': form,
'form_title': 'Create Broadcast Notification',
})