""" Core app views for hospital management system with comprehensive CRUD operations. """ from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.views.generic import ( TemplateView, ListView, DetailView, CreateView, UpdateView, DeleteView ) from django.http import JsonResponse from django.db.models import Count, Q from django.utils import timezone from django.contrib import messages from django.urls import reverse_lazy, reverse from accounts.models import User from datetime import timedelta from .models import ( Tenant, AuditLogEntry, SystemConfiguration, SystemNotification, IntegrationLog ) from hr.models import Department from hr.forms import DepartmentForm # Create aliases for models to match the views AuditLog = AuditLogEntry from .forms import ( TenantForm, SystemConfigurationForm, SystemNotificationForm,CoreSearchForm ) from .utils import AuditLogger # ============================================================================ # DASHBOARD AND OVERVIEW VIEWS # ============================================================================ class DashboardView(LoginRequiredMixin, TemplateView): """ Main dashboard view. """ template_name = 'core/dashboard.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tenant = self.request.user.tenant if tenant: # Get dashboard statistics context.update({ 'tenant': tenant, 'recent_audit_logs': AuditLogEntry.objects.filter( tenant=tenant ).order_by('-timestamp')[:10], 'active_notifications': SystemNotification.objects.filter( Q(tenant=tenant) | Q(tenant=None), is_active=True, start_date__lte=timezone.now(), end_date__gte=timezone.now() ).order_by('-priority', '-created_at')[:5], 'total_departments': Department.objects.filter(tenant=tenant).count(), 'total_configurations': SystemConfiguration.objects.filter( Q(tenant=tenant) | Q(tenant=None) ).count(), }) return context class TenantListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): """ List all tenants (Super admin only). """ model = Tenant template_name = 'core/tenants/tenant_list.html' context_object_name = 'tenants' paginate_by = 20 permission_required = 'core.view_tenant' def get_queryset(self): queryset = Tenant.objects.all().order_by('name') # Apply search filter search = self.request.GET.get('search') if search: queryset = queryset.filter( Q(name__icontains=search) | Q(domain__icontains=search) | Q(contact_email__icontains=search) ) # Apply status filter status = self.request.GET.get('status') if status: queryset = queryset.filter(is_active=(status == 'active')) return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['search_form'] = CoreSearchForm(self.request.GET) return context class TenantDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): """ Display tenant details. """ model = Tenant template_name = 'core/tenants/tenant_detail.html' context_object_name = 'tenant' permission_required = 'core.view_tenant' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tenant = self.object # Get tenant statistics context.update({ 'user_count': tenant.get_user_count(), 'patient_count': tenant.get_patient_count(), 'recent_audit_logs': AuditLogEntry.objects.filter( tenant=tenant ).order_by('-timestamp')[:10], 'departments': Department.objects.filter(tenant=tenant).order_by('name'), 'configurations': SystemConfiguration.objects.filter( Q(tenant=tenant) | Q(tenant=None) ).order_by('category', 'key'), }) return context class TenantCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): """ Create new tenant. """ model = Tenant form_class = TenantForm template_name = 'core/tenants/tenant_form.html' permission_required = 'core.add_tenant' success_url = reverse_lazy('core:tenant_list') def form_valid(self, form): response = super().form_valid(form) # Log tenant creation AuditLogger.log_event( tenant=None, # System level event event_type='CREATE', event_category='SYSTEM_ADMINISTRATION', action='Create Tenant', description=f'Created new tenant: {self.object.name}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'Tenant "{self.object.name}" created successfully.') return response class TenantUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): """ Update tenant information. """ model = Tenant form_class = TenantForm template_name = 'core/tenants/tenant_form.html' permission_required = 'core.change_tenant' def get_success_url(self): return reverse('core:tenant_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): response = super().form_valid(form) # Log tenant update AuditLogger.log_event( tenant=self.object, event_type='UPDATE', event_category='SYSTEM_ADMINISTRATION', action='Update Tenant', description=f'Updated tenant: {self.object.name}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'Tenant "{self.object.name}" updated successfully.') return response class TenantDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): """ Delete tenant (soft delete to inactive). """ model = Tenant template_name = 'core/tenants/tenant_confirm_delete.html' permission_required = 'core.delete_tenant' success_url = reverse_lazy('core:tenant_list') def delete(self, request, *args, **kwargs): self.object = self.get_object() # Soft delete - set to inactive self.object.is_active = False self.object.save() # Log tenant deletion AuditLogger.log_event( tenant=self.object, event_type='DELETE', event_category='SYSTEM_ADMINISTRATION', action='Deactivate Tenant', description=f'Deactivated tenant: {self.object.name}', user=request.user, content_object=self.object, request=request ) messages.success(request, f'Tenant "{self.object.name}" deactivated successfully.') return redirect(self.success_url) class AuditLogListView(LoginRequiredMixin, ListView): """ Audit log listing view. """ model = AuditLogEntry template_name = 'core/audit_logs/audit_log.html' context_object_name = 'audit_logs' paginate_by = 50 def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return AuditLogEntry.objects.none() queryset = AuditLogEntry.objects.filter(tenant=tenant) # Apply filters event_type = self.request.GET.get('event_type') if event_type: queryset = queryset.filter(event_type=event_type) event_category = self.request.GET.get('event_category') if event_category: queryset = queryset.filter(event_category=event_category) user_id = self.request.GET.get('user_id') if user_id: queryset = queryset.filter(user_id=user_id) date_from = self.request.GET.get('date_from') if date_from: queryset = queryset.filter(timestamp__gte=date_from) date_to = self.request.GET.get('date_to') if date_to: queryset = queryset.filter(timestamp__lte=date_to) return queryset.order_by('-timestamp') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tenant = getattr(self.request, 'tenant', None) if tenant: # Get filter options context.update({ 'event_types': AuditLogEntry.objects.filter( tenant=tenant ).values_list('event_type', flat=True).distinct(), 'event_categories': AuditLogEntry.objects.filter( tenant=tenant ).values_list('event_category', flat=True).distinct(), }) return context class AuditLogDetailView(LoginRequiredMixin, DetailView): """ Display detailed audit log entry. """ model = AuditLogEntry template_name = 'core/audit_logs/audit_log_detail.html' context_object_name = 'audit_log' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return AuditLogEntry.objects.none() return AuditLogEntry.objects.filter(tenant=tenant) class SystemConfigurationListView(LoginRequiredMixin, ListView): """ System configuration view. """ model = SystemConfiguration template_name = 'core/configurations/system_configuration.html' context_object_name = 'configurations' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) queryset = SystemConfiguration.objects.filter( Q(tenant=tenant) | Q(tenant=None), is_active=True ).order_by('category', 'key') # Apply search filter search = self.request.GET.get('search') if search: queryset = queryset.filter( Q(key__icontains=search) | Q(description__icontains=search) | Q(category__icontains=search) ) # Apply category filter category = self.request.GET.get('category') if category: queryset = queryset.filter(category=category) return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tenant = getattr(self.request, 'tenant', None) if tenant: # Group configurations by category configurations = context['configurations'] grouped_configs = {} for config in configurations: category = config.category if category not in grouped_configs: grouped_configs[category] = [] grouped_configs[category].append(config) context['grouped_configurations'] = grouped_configs # Get categories for filter context['categories'] = SystemConfiguration.objects.filter( Q(tenant=tenant) | Q(tenant=None) ).values_list('category', flat=True).distinct() return context class SystemConfigurationDetailView(LoginRequiredMixin, DetailView): """ Display system configuration details. """ model = SystemConfiguration template_name = 'core/configurations/system_configuration_detail.html' context_object_name = 'configuration' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) return SystemConfiguration.objects.filter( Q(tenant=tenant) | Q(tenant=None) ) class SystemConfigurationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): """ Create new system configuration. """ model = SystemConfiguration form_class = SystemConfigurationForm template_name = 'core/configurations/system_configuration_form.html' permission_required = 'core.add_systemconfiguration' success_url = reverse_lazy('core:system_configuration_list') def form_valid(self, form): # Set tenant form.instance.tenant = getattr(self.request, 'tenant', None) response = super().form_valid(form) # Log configuration creation AuditLogger.log_event( tenant=form.instance.tenant, event_type='CREATE', event_category='SYSTEM_ADMINISTRATION', action='Create Configuration', description=f'Created configuration: {self.object.key}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'Configuration "{self.object.key}" created successfully.') return response class SystemConfigurationUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): """ Update system configuration. """ model = SystemConfiguration form_class = SystemConfigurationForm template_name = 'core/configurations/system_configuration_form.html' permission_required = 'core.change_systemconfiguration' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) return SystemConfiguration.objects.filter( Q(tenant=tenant) | Q(tenant=None) ) def get_success_url(self): return reverse('core:system_configuration_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): response = super().form_valid(form) # Log configuration update AuditLogger.log_event( tenant=self.object.tenant, event_type='UPDATE', event_category='SYSTEM_ADMINISTRATION', action='Update Configuration', description=f'Updated configuration: {self.object.key}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'Configuration "{self.object.key}" updated successfully.') return response class SystemConfigurationDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): """ Delete system configuration. """ model = SystemConfiguration template_name = 'core/configurations/system_configuration_confirm_delete.html' permission_required = 'core.delete_systemconfiguration' success_url = reverse_lazy('core:system_configuration_list') def get_queryset(self): tenant = getattr(self.request, 'tenant', None) return SystemConfiguration.objects.filter(tenant=tenant) # Only tenant-specific configs can be deleted def delete(self, request, *args, **kwargs): self.object = self.get_object() # Log configuration deletion AuditLogger.log_event( tenant=self.object.tenant, event_type='DELETE', event_category='SYSTEM_ADMINISTRATION', action='Delete Configuration', description=f'Deleted configuration: {self.object.key}', user=request.user, content_object=self.object, request=request ) messages.success(request, f'Configuration "{self.object.key}" deleted successfully.') return super().delete(request, *args, **kwargs) class SystemNotificationListView(LoginRequiredMixin, ListView): """ List system notifications. """ model = SystemNotification template_name = 'core/notifications/notification_list.html' context_object_name = 'notifications' paginate_by = 20 def get_queryset(self): tenant = getattr(self.request, 'tenant', None) queryset = SystemNotification.objects.filter( Q(tenant=tenant) | Q(tenant=None) ).order_by('-created_at') # Apply filters status = self.request.GET.get('status') if status == 'active': queryset = queryset.filter(is_active=True) elif status == 'inactive': queryset = queryset.filter(is_active=False) priority = self.request.GET.get('priority') if priority: queryset = queryset.filter(priority=priority) search = self.request.GET.get('search') if search: queryset = queryset.filter( Q(title__icontains=search) | Q(message__icontains=search) ) return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['priority_choices'] = SystemNotification.PRIORITY_CHOICES return context class SystemNotificationDetailView(LoginRequiredMixin, DetailView): """ Display notification details. """ model = SystemNotification template_name = 'core/notifications/notification_detail.html' context_object_name = 'notification' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) return SystemNotification.objects.filter( Q(tenant=tenant) | Q(tenant=None) ) class SystemNotificationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): """ Create new system notification. """ model = SystemNotification form_class = SystemNotificationForm template_name = 'core/notifications/notification_form.html' permission_required = 'core.add_systemnotification' success_url = reverse_lazy('core:notification_list') def form_valid(self, form): # Set tenant and creator form.instance.tenant = getattr(self.request, 'tenant', None) form.instance.created_by = self.request.user response = super().form_valid(form) # Log notification creation AuditLogger.log_event( tenant=form.instance.tenant, event_type='CREATE', event_category='SYSTEM_ADMINISTRATION', action='Create Notification', description=f'Created notification: {self.object.title}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'Notification "{self.object.title}" created successfully.') return response class SystemNotificationUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): """ Update system notification. """ model = SystemNotification form_class = SystemNotificationForm template_name = 'core/notifications/notification_form.html' permission_required = 'core.change_systemnotification' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) return SystemNotification.objects.filter( Q(tenant=tenant) | Q(tenant=None) ) def get_success_url(self): return reverse('core:notification_detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): response = super().form_valid(form) # Log notification update AuditLogger.log_event( tenant=self.object.tenant, event_type='UPDATE', event_category='SYSTEM_ADMINISTRATION', action='Update Notification', description=f'Updated notification: {self.object.title}', user=self.request.user, content_object=self.object, request=self.request ) messages.success(self.request, f'Notification "{self.object.title}" updated successfully.') return response class SystemNotificationDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): """ Delete system notification. """ model = SystemNotification template_name = 'core/notifications/notification_confirm_delete.html' permission_required = 'core.delete_systemnotification' success_url = reverse_lazy('core:notification_list') def get_queryset(self): tenant = getattr(self.request, 'tenant', None) return SystemNotification.objects.filter(tenant=tenant) # Only tenant notifications can be deleted def delete(self, request, *args, **kwargs): self.object = self.get_object() # Log notification deletion AuditLogger.log_event( tenant=self.object.tenant, event_type='DELETE', event_category='SYSTEM_ADMINISTRATION', action='Delete Notification', description=f'Deleted notification: {self.object.title}', user=request.user, content_object=self.object, request=request ) messages.success(request, f'Notification "{self.object.title}" deleted successfully.') return super().delete(request, *args, **kwargs) class IntegrationLogListView(LoginRequiredMixin, ListView): """ List integration logs. """ model = IntegrationLog template_name = 'core/integration_logs/integration_log_list.html' context_object_name = 'logs' paginate_by = 50 def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return IntegrationLog.objects.none() queryset = IntegrationLog.objects.filter(tenant=tenant).order_by('-timestamp') # Apply filters log_level = self.request.GET.get('log_level') if log_level: queryset = queryset.filter(log_level=log_level) component = self.request.GET.get('component') if component: queryset = queryset.filter(component__icontains=component) date_from = self.request.GET.get('date_from') if date_from: queryset = queryset.filter(timestamp__gte=date_from) date_to = self.request.GET.get('date_to') if date_to: queryset = queryset.filter(timestamp__lte=date_to) return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tenant = getattr(self.request, 'tenant', None) if tenant: context.update({ 'log_levels': IntegrationLog.objects.filter( tenant=tenant ).values_list('log_level', flat=True).distinct(), 'components': IntegrationLog.objects.filter( tenant=tenant ).values_list('component', flat=True).distinct(), }) return context class IntegrationLogDetailView(LoginRequiredMixin, DetailView): """ Display integration log details. """ model = IntegrationLog template_name = 'core/integration_logs/integration_log_detail.html' context_object_name = 'log' def get_queryset(self): tenant = getattr(self.request, 'tenant', None) if not tenant: return IntegrationLog.objects.none() return IntegrationLog.objects.filter(tenant=tenant) @login_required def dashboard_stats(request): """ HTMX view for dashboard statistics. """ tenant = getattr(request, 'tenant', None) if not tenant: return JsonResponse({'error': 'No tenant found'}, status=400) # Calculate statistics now = timezone.now() today = now.date() week_ago = now - timedelta(days=7) stats = { 'total_users': tenant.get_user_count(), 'total_patients': tenant.get_patient_count(), 'total_departments': Department.objects.filter(tenant=tenant, is_active=True).count(), 'audit_logs_today': AuditLogEntry.objects.filter( tenant=tenant, timestamp__date=today ).count(), 'audit_logs_week': AuditLogEntry.objects.filter( tenant=tenant, timestamp__gte=week_ago ).count(), 'active_notifications': SystemNotification.objects.filter( Q(tenant=tenant) | Q(tenant=None), is_active=True, start_date__lte=now, end_date__gte=now ).count(), 'total_configurations': SystemConfiguration.objects.filter( Q(tenant=tenant) | Q(tenant=None), is_active=True ).count(), } return render(request, 'core/partials/dashboard_stats.html', {'stats': stats}) @login_required def audit_log_search(request): """ HTMX view for audit log search. """ tenant = getattr(request, 'tenant', None) if not tenant: return JsonResponse({'error': 'No tenant found'}, status=400) search_query = request.GET.get('search', '') queryset = AuditLogEntry.objects.filter(tenant=tenant) if search_query: queryset = queryset.filter( Q(action__icontains=search_query) | Q(description__icontains=search_query) | Q(user_email__icontains=search_query) | Q(object_repr__icontains=search_query) ) audit_logs = queryset.order_by('-timestamp')[:20] return render(request, 'core/partials/audit_log_list.html', { 'audit_logs': audit_logs }) @login_required def system_notifications(request): """ HTMX view for system notifications. """ tenant = getattr(request, 'tenant', None) if not tenant: return JsonResponse({'error': 'No tenant found'}, status=400) notifications = SystemNotification.objects.filter( Q(tenant=tenant) | Q(tenant=None), is_active=True ).filter( start_date__lte=timezone.now() ).filter( Q(end_date__gte=timezone.now()) | Q(end_date=None) ).order_by('-priority', '-created_at') return render(request, 'core/partials/system_notifications.html', { 'notifications': notifications }) @login_required def dismiss_notification(request, notification_id): """ HTMX view to dismiss a notification. """ tenant = getattr(request, 'tenant', None) if not tenant: return JsonResponse({'error': 'No tenant found'}, status=400) notification = get_object_or_404( SystemNotification, notification_id=notification_id, is_dismissible=True ) # Log the dismissal AuditLogger.log_event( tenant=tenant, event_type='UPDATE', event_category='SYSTEM_ADMINISTRATION', action='Dismiss Notification', description=f'User dismissed notification: {notification.title}', user=request.user, content_object=notification, request=request ) return JsonResponse({'status': 'dismissed'}) @login_required def tenant_info(request): """ HTMX view for tenant information. """ tenant = getattr(request, 'tenant', None) if not tenant: return JsonResponse({'error': 'No tenant found'}, status=400) return render(request, 'core/partials/tenant_info.html', { 'tenant': tenant }) @login_required def system_health(request): """ HTMX view for system health status. """ tenant = getattr(request, 'tenant', None) if not tenant: return JsonResponse({'error': 'No tenant found'}, status=400) # Calculate system health metrics now = timezone.now() hour_ago = now - timedelta(hours=1) health_data = { 'database_status': 'healthy', # Would check actual database health 'cache_status': 'healthy', # Would check cache health 'recent_errors': AuditLogEntry.objects.filter( tenant=tenant, event_type='ERROR', timestamp__gte=hour_ago ).count(), 'system_load': 'normal', # Would check actual system load 'last_updated': now.strftime('%Y-%m-%d %H:%M:%S'), 'active_departments': Department.objects.filter( tenant=tenant, is_active=True ).count(), 'total_configurations': SystemConfiguration.objects.filter( Q(tenant=tenant) | Q(tenant=None), is_active=True ).count(), } return render(request, 'core/partials/system_health.html', { 'health_data': health_data }) @login_required def activate_notification(request, pk): """ Activate a system notification. """ tenant = getattr(request, 'tenant', None) if not tenant: messages.error(request, 'No tenant found.') return redirect('core:notification_list') notification = get_object_or_404( SystemNotification, pk=pk, tenant=tenant ) notification.is_active = True notification.save() # Log activation AuditLogger.log_event( tenant=tenant, event_type='UPDATE', event_category='SYSTEM_ADMINISTRATION', action='Activate Notification', description=f'Activated notification: {notification.title}', user=request.user, content_object=notification, request=request ) messages.success(request, f'Notification "{notification.title}" activated successfully.') return redirect('core:system_notification_detail', pk=pk) @login_required def deactivate_notification(request, pk): """ Deactivate a system notification. """ tenant = getattr(request, 'tenant', None) if not tenant: messages.error(request, 'No tenant found.') return redirect('core:notification_list') notification = get_object_or_404( SystemNotification, pk=pk, tenant=tenant ) notification.is_active = False notification.save() # Log deactivation AuditLogger.log_event( tenant=tenant, event_type='UPDATE', event_category='SYSTEM_ADMINISTRATION', action='Deactivate Notification', description=f'Deactivated notification: {notification.title}', user=request.user, content_object=notification, request=request ) messages.success(request, f'Notification "{notification.title}" deactivated successfully.') return redirect('core:system_notification_detail', pk=pk) @login_required def reset_configuration(request, pk): """ Reset configuration to default value. """ tenant = getattr(request, 'tenant', None) if not tenant: messages.error(request, 'No tenant found.') return redirect('core:system_configuration_list') configuration = get_object_or_404( SystemConfiguration, pk=pk, tenant=tenant ) # Reset to default value old_value = configuration.value configuration.value = configuration.default_value configuration.save() # Log reset AuditLogger.log_event( tenant=tenant, event_type='UPDATE', event_category='SYSTEM_ADMINISTRATION', action='Reset Configuration', description=f'Reset configuration {configuration.key} from "{old_value}" to "{configuration.value}"', user=request.user, content_object=configuration, request=request ) messages.success(request, f'Configuration "{configuration.key}" reset to default value.') return redirect('core:system_configuration_detail', pk=pk) def tenant_stats(request): """ HTMX view for tenant statistics. """ from django.db.models import Count stats = { 'total_tenants': Tenant.objects.count(), 'active_tenants': Tenant.objects.filter(is_active=True).count(), 'inactive_tenants': Tenant.objects.filter(is_active=False).count(), 'tenants_by_type': Tenant.objects.values('tenant_type').annotate(count=Count('tenant_id')), } return render(request, 'core/partials/tenant_stats.html', {'stats': stats}) def configuration_search(request): """ HTMX view for configuration search. """ query = request.GET.get('q', '') configurations = [] if query: configurations = SystemConfiguration.objects.filter( tenant=request.user.tenant, key__icontains=query )[:10] return render(request, 'core/partials/configuration_search.html', { 'configurations': configurations, 'query': query }) def audit_log_list_htmx(request): """ HTMX view for audit log list. """ logs = AuditLog.objects.filter( tenant=request.user.tenant ).order_by('-timestamp')[:20] return render(request, 'core/partials/audit_log_list.html', { 'logs': logs }) def activate_tenant(request, pk): """ Activate a tenant. """ tenant = get_object_or_404(Tenant, tenant_id=pk) tenant.is_active = True tenant.save() messages.success(request, f'Tenant "{tenant.name}" has been activated.') return redirect('core:tenant_detail', pk=pk) def deactivate_tenant(request, pk): """ Deactivate a tenant. """ tenant = get_object_or_404(Tenant, tenant_id=pk) tenant.is_active = False tenant.save() messages.success(request, f'Tenant "{tenant.name}" has been deactivated.') return redirect('core:tenant_detail', pk=pk) def reset_system_configuration(request): """ Reset system configuration to defaults. """ if request.method == 'POST': # Reset configurations for the tenant SystemConfiguration.objects.filter( tenant=request.user.tenant ).delete() messages.success(request, 'System configuration has been reset to defaults.') return redirect('core:system_configuration_list') return render(request, 'core/configurations/reset_configuration_confirm.html') def export_audit_log(request): """ Export audit log to CSV. """ import csv from django.http import HttpResponse response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="audit_log.csv"' writer = csv.writer(response) writer.writerow(['Timestamp', 'User', 'Action', 'Object Type', 'Object ID', 'Changes']) logs = AuditLog.objects.filter( tenant=request.user.tenant ).order_by('-timestamp') for log in logs: writer.writerow([ log.timestamp, log.user.username if log.user else 'System', log.action, log.object_type, log.object_id, log.changes ]) return response class CoreSearchView(ListView): """ Generic search view for core models. """ template_name = 'core/search_results.html' context_object_name = 'results' paginate_by = 20 def get_queryset(self): query = self.request.GET.get('q', '') if not query: return [] # Search across multiple models results = [] # Search tenants tenants = Tenant.objects.filter(name__icontains=query)[:5] for tenant in tenants: results.append({ 'type': 'Tenant', 'object': tenant, 'url': reverse('core:tenant_detail', args=[tenant.tenant_id]) }) # Search departments departments = Department.objects.filter( tenant=self.request.user.tenant, name__icontains=query )[:5] for dept in departments: results.append({ 'type': 'Department', 'object': dept, 'url': reverse('core:department_detail', args=[dept.department_id]) }) return results def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['query'] = self.request.GET.get('q', '') return context def tenant_search(request): """ AJAX search for tenants. """ query = request.GET.get('q', '') tenants = [] if query: tenants = Tenant.objects.filter( name__icontains=query ).values('tenant_id', 'name', 'tenant_type')[:10] return JsonResponse({'tenants': list(tenants)}) def bulk_activate_tenants(request): """ Bulk activate tenants. """ if request.method == 'POST': tenant_ids = request.POST.getlist('tenant_ids') count = Tenant.objects.filter( tenant_id__in=tenant_ids ).update(is_active=True) messages.success(request, f'{count} tenants have been activated.') return redirect('core:tenant_list') def bulk_deactivate_tenants(request): """ Bulk deactivate tenants. """ if request.method == 'POST': tenant_ids = request.POST.getlist('tenant_ids') count = Tenant.objects.filter( tenant_id__in=tenant_ids ).update(is_active=False) messages.success(request, f'{count} tenants have been deactivated.') return redirect('core:tenant_list') def bulk_export_audit_logs(request): """ Bulk export audit logs. """ import csv from django.http import HttpResponse if request.method == 'POST': log_ids = request.POST.getlist('log_ids') response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="selected_audit_logs.csv"' writer = csv.writer(response) writer.writerow(['Timestamp', 'User', 'Action', 'Object Type', 'Object ID', 'Changes']) logs = AuditLog.objects.filter( tenant=request.user.tenant, log_id__in=log_ids ).order_by('-timestamp') for log in logs: writer.writerow([ log.timestamp, log.user.username if log.user else 'System', log.action, log.object_type, log.object_id, log.changes ]) return response return redirect('core:audit_log_list') def validate_tenant_data(request): """ AJAX validation for tenant data. """ name = request.GET.get('name', '') errors = [] if name: if Tenant.objects.filter(name=name).exists(): errors.append('Tenant name already exists.') if len(name) < 3: errors.append('Tenant name must be at least 3 characters.') return JsonResponse({'valid': len(errors) == 0, 'errors': errors}) def get_system_status(request): """ Get system status information. """ import psutil from django.db import connection # Database status with connection.cursor() as cursor: cursor.execute("SELECT 1") db_status = "healthy" # System metrics status = { 'database': db_status, 'cpu_usage': psutil.cpu_percent(), 'memory_usage': psutil.virtual_memory().percent, 'disk_usage': psutil.disk_usage('/').percent, 'active_users': User.objects.filter(is_active=True).count(), 'total_tenants': Tenant.objects.count(), } return JsonResponse(status) def backup_configuration(request): """ Backup system configuration. """ import json from django.http import HttpResponse configurations = SystemConfiguration.objects.filter( tenant=request.user.tenant ).values('key', 'value', 'description') backup_data = { 'tenant': str(request.user.tenant.tenant_id), 'timestamp': timezone.now().isoformat(), 'configurations': list(configurations) } response = HttpResponse( json.dumps(backup_data, indent=2), content_type='application/json' ) response['Content-Disposition'] = 'attachment; filename="configuration_backup.json"' return response def restore_configuration(request): """ Restore system configuration from backup. """ import json if request.method == 'POST': backup_file = request.FILES.get('backup_file') if backup_file: try: backup_data = json.loads(backup_file.read().decode('utf-8')) # Clear existing configurations SystemConfiguration.objects.filter( tenant=request.user.tenant ).delete() # Restore configurations for config in backup_data.get('configurations', []): SystemConfiguration.objects.create( tenant=request.user.tenant, key=config['key'], value=config['value'], description=config.get('description', '') ) messages.success(request, 'Configuration has been restored successfully.') return redirect('core:system_configuration_list') except (json.JSONDecodeError, KeyError) as e: messages.error(request, f'Invalid backup file: {e}') else: messages.error(request, 'Please select a backup file.') return render(request, 'core/restore_configuration.html') # Department Views # class DepartmentListView(LoginRequiredMixin, ListView): # """ # List departments. # """ # model = Department # template_name = 'core/department_list.html' # context_object_name = 'departments' # paginate_by = 20 # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return Department.objects.none() # # queryset = Department.objects.filter(tenant=tenant).order_by('name') # # # Apply search filter # search = self.request.GET.get('search') # if search: # queryset = queryset.filter( # Q(name__icontains=search) | # Q(description__icontains=search) | # Q(location__icontains=search) # ) # # # Apply status filter # status = self.request.GET.get('status') # if status: # queryset = queryset.filter(is_active=(status == 'active')) # # return queryset # # # class DepartmentDetailView(LoginRequiredMixin, DetailView): # """ # Display department details. # """ # model = Department # template_name = 'core/department_detail.html' # context_object_name = 'department' # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return Department.objects.none() # return Department.objects.filter(tenant=tenant) # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # department = self.object # # # Get department statistics # context.update({ # 'employee_count': department.current_staff_count, # 'recent_activity': AuditLogEntry.objects.filter( # tenant=department.tenant, # object_id=str(department.pk), # content_type__model='department' # ).order_by('-timestamp')[:10], # }) # # return context # # # class DepartmentCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """ # Create new department. # """ # model = Department # form_class = DepartmentForm # template_name = 'core/department_form.html' # permission_required = 'core.add_department' # success_url = reverse_lazy('core:department_list') # # def form_valid(self, form): # # Set tenant # form.instance.tenant = getattr(self.request, 'tenant', None) # response = super().form_valid(form) # # # Log department creation # AuditLogger.log_event( # tenant=form.instance.tenant, # event_type='CREATE', # event_category='SYSTEM_ADMINISTRATION', # action='Create Department', # description=f'Created department: {self.object.name}', # user=self.request.user, # content_object=self.object, # request=self.request # ) # # messages.success(self.request, f'Department "{self.object.name}" created successfully.') # return response # # # class DepartmentUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """ # Update department. # """ # model = Department # form_class = DepartmentForm # template_name = 'core/department_form.html' # permission_required = 'core.change_department' # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return Department.objects.none() # return Department.objects.filter(tenant=tenant) # # def get_success_url(self): # return reverse('core:department_detail', kwargs={'pk': self.object.pk}) # # def form_valid(self, form): # response = super().form_valid(form) # # # Log department update # AuditLogger.log_event( # tenant=self.object.tenant, # event_type='UPDATE', # event_category='SYSTEM_ADMINISTRATION', # action='Update Department', # description=f'Updated department: {self.object.name}', # user=self.request.user, # content_object=self.object, # request=self.request # ) # # messages.success(self.request, f'Department "{self.object.name}" updated successfully.') # return response # # # class DepartmentDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """ # Delete department (soft delete to inactive). # """ # model = Department # template_name = 'core/department_confirm_delete.html' # permission_required = 'core.delete_department' # success_url = reverse_lazy('core:department_list') # # def get_queryset(self): # tenant = getattr(self.request, 'tenant', None) # if not tenant: # return Department.objects.none() # return Department.objects.filter(tenant=tenant) # # def delete(self, request, *args, **kwargs): # self.object = self.get_object() # # # Check if department has employees # if self.object.get_employee_count() > 0: # messages.error(request, 'Cannot delete department with active employees.') # return redirect('core:department_detail', pk=self.object.pk) # # # Soft delete - set to inactive # self.object.is_active = False # self.object.save() # # # Log department deletion # AuditLogger.log_event( # tenant=self.object.tenant, # event_type='DELETE', # event_category='SYSTEM_ADMINISTRATION', # action='Deactivate Department', # description=f'Deactivated department: {self.object.name}', # user=request.user, # content_object=self.object, # request=request # ) # # messages.success(request, f'Department "{self.object.name}" deactivated successfully.') # return redirect(self.success_url) # # import json # # from django.contrib.contenttypes.models import ContentType # from django.shortcuts import render, redirect, get_object_or_404 # from django.views.generic import ( # ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView # ) # from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin # from django.contrib.auth.decorators import login_required, permission_required # from django.http import JsonResponse, HttpResponse, HttpResponseRedirect # from django.urls import reverse, reverse_lazy # from django.db.models import Q, Count, Sum, Avg, F, ExpressionWrapper, DateTimeField # from django.utils import timezone # from django.utils.translation import gettext_lazy as _ # from django.contrib import messages # from django.core.paginator import Paginator # from django.views.decorators.http import require_POST, require_GET # from django.views.decorators.csrf import csrf_protect # from django.forms import modelform_factory # from django.conf import settings # # from accounts.models import User # from .models import ( # Tenant, Department, AuditLogEntry, SystemConfiguration, # SystemNotification, NotificationDismissal, IntegrationLog # ) # from .forms import ( # TenantForm, DepartmentForm, SystemConfigurationForm, SystemNotificationForm, # NotificationDismissForm, IntegrationLogFilterForm, CoreSearchForm # ) # # # class DashboardView(LoginRequiredMixin, TemplateView): # """Main dashboard view for the core app.""" # template_name = 'core/dashboard.html' # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # user = self.request.user # tenant = getattr(user, 'tenant', None) # # # Default to last 30 days for stats # thirty_days_ago = timezone.now() - timezone.timedelta(days=30) # # # System health summary # context['system_health'] = { # 'active_tenants': Tenant.objects.filter(is_active=True).count(), # 'total_departments': Department.objects.count(), # 'system_notifications': SystemNotification.objects.filter(is_active=True,start_date__lte=timezone.now()).count(), # } # # # Recent activities (audit log) # if user.is_superuser: # # For superusers, show system-wide logs # context['recent_activities'] = AuditLogEntry.objects.all().order_by('-timestamp')[:10] # elif tenant: # # For tenant users, show tenant-specific logs # context['recent_activities'] = AuditLogEntry.objects.filter( # tenant=tenant # ).order_by('-timestamp')[:10] # else: # context['recent_activities'] = [] # # # Active notifications for user # context['notifications'] = [] # active_notifications = SystemNotification.objects.filter( # is_active=True, # start_date__lte=timezone.now(), # ) # # # Filter to show relevant notifications to this user # for notification in active_notifications: # if notification.is_visible_to_user(user): # context['notifications'].append(notification) # # # Integration health # if user.is_superuser or (user.is_staff and user.has_perm('core.view_integrationlog')): # # Calculate success rates for integrations # context['integration_health'] = [] # # integrations = IntegrationLog.objects.filter( # timestamp__gte=thirty_days_ago # ).values('integration_name').distinct() # # for integration in integrations: # integration_name = integration['integration_name'] # logs = IntegrationLog.objects.filter( # integration_name=integration_name, # timestamp__gte=thirty_days_ago # ) # # total = logs.count() # success = logs.filter(status='SUCCESS').count() # # if total > 0: # success_rate = (success / total) * 100 # else: # success_rate = 0 # # context['integration_health'].append({ # 'name': integration_name, # 'success_rate': success_rate, # 'total': total, # 'success': success, # 'error': total - success # }) # # return context # # # class TenantListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """View for listing all tenants.""" # model = Tenant # template_name = 'core/tenant_list.html' # context_object_name = 'tenants' # paginate_by = 20 # permission_required = 'core.view_tenant' # # def get_queryset(self): # queryset = super().get_queryset() # user = self.request.user # # # Only superusers can see all tenants # if not user.is_superuser: # # Regular users can only see their own tenant # if hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(pk=user.tenant.pk) # else: # queryset = Tenant.objects.none() # # # Apply search and filters # form = CoreSearchForm(self.request.GET) # if form.is_valid(): # search_term = form.cleaned_data.get('search') # org_type = form.cleaned_data.get('organization_type') # is_active = form.cleaned_data.get('is_active') # created_from = form.cleaned_data.get('created_from') # created_to = form.cleaned_data.get('created_to') # # if search_term: # queryset = queryset.filter( # Q(name__icontains=search_term) | # Q(code__icontains=search_term) | # Q(contact_email__icontains=search_term) | # Q(description__icontains=search_term) # ) # # if org_type: # queryset = queryset.filter(organization_type=org_type) # # if is_active: # is_active_bool = is_active == 'true' # queryset = queryset.filter(is_active=is_active_bool) # # if created_from: # queryset = queryset.filter(created_at__date__gte=created_from) # # if created_to: # queryset = queryset.filter(created_at__date__lte=created_to) # # return queryset.select_related('created_by') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['search_form'] = CoreSearchForm(self.request.GET) # return context # # # class TenantDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """View for displaying tenant details.""" # model = Tenant # template_name = 'core/tenant_detail.html' # context_object_name = 'tenant' # permission_required = 'core.view_tenant' # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # tenant = self.get_object() # # # Get departments for this tenant # context['departments'] = Department.objects.filter( # tenant=tenant # ).select_related('department_head', 'parent_department') # # # Count users by role # if hasattr(tenant, 'users'): # user_roles = tenant.users.values('role').annotate(count=Count('id')) # context['user_roles'] = user_roles # # # Get tenant-specific configurations # context['configurations'] = SystemConfiguration.objects.filter( # tenant=tenant # ).order_by('category', 'key') # # # Get recent audit logs # context['audit_logs'] = AuditLogEntry.objects.filter( # tenant=tenant # ).order_by('-timestamp')[:10] # # # Check subscription status # context['subscription_valid'] = tenant.is_subscription_valid() # # # Tenant statistics # context['active_users_count'] = tenant.get_active_users_count() # # # Departments by type # dept_by_type = Department.objects.filter( # tenant=tenant # ).values('department_type').annotate(count=Count('department_id')) # context['departments_by_type'] = dept_by_type # # return context # # # class TenantCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """View for creating a new tenant.""" # model = Tenant # form_class = TenantForm # template_name = 'core/tenant_form.html' # permission_required = 'core.add_tenant' # success_url = reverse_lazy('core:tenant_list') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = _('Create New Tenant') # return context # # def form_valid(self, form): # """Set the created_by field to the current user.""" # form.instance.created_by = self.request.user # # response = super().form_valid(form) # # # Log the action # AuditLogEntry.log_action( # user=self.request.user, # action_type='CREATE', # content_object=self.object, # request=self.request # ) # # messages.success( # self.request, # _('Tenant "{}" was created successfully.').format(self.object.name) # ) # # return response # # def form_invalid(self, form): # """Handle form validation errors.""" # messages.error( # self.request, # _('There was an error creating the tenant. Please check the form.') # ) # return super().form_invalid(form) # # # class TenantUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """View for updating a tenant.""" # model = Tenant # form_class = TenantForm # template_name = 'core/tenant_form.html' # permission_required = 'core.change_tenant' # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = _('Update Tenant') # return context # # def get_success_url(self): # """Return to the tenant detail page after update.""" # return reverse('core:tenant_detail', kwargs={'pk': self.object.pk}) # # def form_valid(self, form): # """Log the update action.""" # # Capture changes # if self.object: # changes = {} # for field in form.changed_data: # if field not in ['logo']: # Skip binary fields # old_value = getattr(self.object, field) # new_value = form.cleaned_data[field] # changes[field] = { # 'old': str(old_value), # 'new': str(new_value) # } # # response = super().form_valid(form) # # # Log the action # AuditLogEntry.log_action( # user=self.request.user, # action_type='UPDATE', # content_object=self.object, # changes=changes, # request=self.request # ) # # messages.success( # self.request, # _('Tenant "{}" was updated successfully.').format(self.object.name) # ) # # return response # # def form_invalid(self, form): # """Handle form validation errors.""" # messages.error( # self.request, # _('There was an error updating the tenant. Please check the form.') # ) # return super().form_invalid(form) # # # class TenantDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """View for deleting a tenant.""" # model = Tenant # template_name = 'core/tenant_confirm_delete.html' # permission_required = 'core.delete_tenant' # success_url = reverse_lazy('core:tenant_list') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # # # Count related objects to warn user # tenant = self.get_object() # context['departments_count'] = tenant.departments.count() # # # Count users if there's a relationship # if hasattr(tenant, 'users'): # context['users_count'] = tenant.users.count() # # return context # # def delete(self, request, *args, **kwargs): # """Override delete to log the action.""" # self.object = self.get_object() # name = self.object.name # # # Log the action before deletion # AuditLogEntry.log_action( # user=request.user, # action_type='DELETE', # content_object=self.object, # request=request # ) # # # Proceed with deletion # success_url = self.get_success_url() # self.object.delete() # # messages.success( # request, # _('Tenant "{}" was deleted successfully.').format(name) # ) # # return HttpResponseRedirect(success_url) # # # class DepartmentListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """View for listing departments.""" # model = Department # template_name = 'core/department_list.html' # context_object_name = 'departments' # paginate_by = 20 # permission_required = 'core.view_department' # # def get_queryset(self): # queryset = super().get_queryset() # user = self.request.user # # # Filter by tenant # if not user.is_superuser and hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(tenant=user.tenant) # # # Apply search and filters # form = CoreSearchForm(self.request.GET) # if form.is_valid(): # search_term = form.cleaned_data.get('search') # dept_type = form.cleaned_data.get('department_type') # is_active = form.cleaned_data.get('is_active') # created_from = form.cleaned_data.get('created_from') # created_to = form.cleaned_data.get('created_to') # # if search_term: # queryset = queryset.filter( # Q(name__icontains=search_term) | # Q(code__icontains=search_term) | # Q(description__icontains=search_term) # ) # # if dept_type: # queryset = queryset.filter(department_type=dept_type) # # if is_active: # is_active_bool = is_active == 'true' # queryset = queryset.filter(is_active=is_active_bool) # # if created_from: # queryset = queryset.filter(created_at__date__gte=created_from) # # if created_to: # queryset = queryset.filter(created_at__date__lte=created_to) # # return queryset.select_related('tenant', 'department_head', 'parent_department') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['search_form'] = CoreSearchForm(self.request.GET) # # # Get tenant list for superusers # if self.request.user.is_superuser: # context['tenants'] = Tenant.objects.filter(is_active=True) # # # Department types for quick filtering # context['department_types'] = dict(Department.DEPARTMENT_TYPE_CHOICES) # # return context # # # class DepartmentDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """View for displaying department details.""" # model = Department # template_name = 'core/department_detail.html' # context_object_name = 'department' # permission_required = 'core.view_department' # # def get_queryset(self): # """Filter by user's tenant if not superuser.""" # queryset = super().get_queryset() # user = self.request.user # # if not user.is_superuser and hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(tenant=user.tenant) # # return queryset.select_related( # 'tenant', 'department_head', 'parent_department', 'created_by' # ) # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # department = self.get_object() # # # Get sub-departments # context['sub_departments'] = department.sub_departments.all() # # # Get staff members # if hasattr(department, 'staff'): # context['staff_members'] = department.staff.all() # # # Get department hierarchy # hierarchy = [] # current = department # while current.parent_department: # hierarchy.insert(0, current.parent_department) # current = current.parent_department # context['department_hierarchy'] = hierarchy # # # Format operating hours for display # if department.operating_hours: # try: # if isinstance(department.operating_hours, str): # hours = json.loads(department.operating_hours) # else: # hours = department.operating_hours # context['operating_hours_formatted'] = hours # except json.JSONDecodeError: # context['operating_hours_formatted'] = None # # return context # # # class DepartmentCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """View for creating a new department.""" # model = Department # form_class = DepartmentForm # template_name = 'core/department_form.html' # permission_required = 'core.add_department' # # def get_form_kwargs(self): # """Pass the current user to the form.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = _('Create New Department') # return context # # def form_valid(self, form): # """Set the created_by field to the current user.""" # form.instance.created_by = self.request.user # # # Set tenant automatically for non-superusers # if not self.request.user.is_superuser and hasattr(self.request.user, 'tenant'): # form.instance.tenant = self.request.user.tenant # # response = super().form_valid(form) # # # Log the action # AuditLogEntry.log_action( # user=self.request.user, # action_type='CREATE', # content_object=self.object, # request=self.request # ) # # messages.success( # self.request, # _('Department "{}" was created successfully.').format(self.object.name) # ) # # return response # # def get_success_url(self): # """Return to the department detail page after creation.""" # return reverse('core:department_detail', kwargs={'pk': self.object.pk}) # # def form_invalid(self, form): # """Handle form validation errors.""" # messages.error( # self.request, # _('There was an error creating the department. Please check the form.') # ) # return super().form_invalid(form) # # # class DepartmentUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """View for updating a department.""" # model = Department # form_class = DepartmentForm # template_name = 'core/department_form.html' # permission_required = 'core.change_department' # # def get_queryset(self): # """Filter by user's tenant if not superuser.""" # queryset = super().get_queryset() # user = self.request.user # # if not user.is_superuser and hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(tenant=user.tenant) # # return queryset # # def get_form_kwargs(self): # """Pass the current user to the form.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = _('Update Department') # return context # # def form_valid(self, form): # """Log the update action.""" # # Capture changes # if self.object: # changes = {} # for field in form.changed_data: # old_value = getattr(self.object, field) # new_value = form.cleaned_data[field] # changes[field] = { # 'old': str(old_value), # 'new': str(new_value) # } # # response = super().form_valid(form) # # # Log the action # AuditLogEntry.log_action( # user=self.request.user, # action_type='UPDATE', # content_object=self.object, # changes=changes, # request=self.request # ) # # messages.success( # self.request, # _('Department "{}" was updated successfully.').format(self.object.name) # ) # # return response # # def get_success_url(self): # """Return to the department detail page after update.""" # return reverse('core:department_detail', kwargs={'pk': self.object.pk}) # # def form_invalid(self, form): # """Handle form validation errors.""" # messages.error( # self.request, # _('There was an error updating the department. Please check the form.') # ) # return super().form_invalid(form) # # # class DepartmentDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """View for deleting a department.""" # model = Department # template_name = 'core/department_confirm_delete.html' # permission_required = 'core.delete_department' # # def get_queryset(self): # """Filter by user's tenant if not superuser.""" # queryset = super().get_queryset() # user = self.request.user # # if not user.is_superuser and hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(tenant=user.tenant) # # return queryset # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # # # Count related objects to warn user # department = self.get_object() # context['sub_departments_count'] = department.sub_departments.count() # # # Check for staff members if there's a relationship # if hasattr(department, 'staff'): # context['staff_count'] = department.staff.count() # # return context # # def delete(self, request, *args, **kwargs): # """Override delete to log the action.""" # self.object = self.get_object() # name = self.object.name # tenant = self.object.tenant # # # Log the action before deletion # AuditLogEntry.log_action( # user=request.user, # action_type='DELETE', # content_object=self.object, # request=request # ) # # # Proceed with deletion # self.object.delete() # # messages.success( # request, # _('Department "{}" was deleted successfully.').format(name) # ) # # # Return to department list # return HttpResponseRedirect(reverse('core:department_list')) # # # class AuditLogListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """View for listing audit logs.""" # model = AuditLogEntry # template_name = 'core/audit_log_list.html' # context_object_name = 'audit_logs' # paginate_by = 50 # permission_required = 'core.view_auditlogentry' # # def get_queryset(self): # queryset = super().get_queryset() # user = self.request.user # # # Filter by tenant for non-superusers # if not user.is_superuser and hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(tenant=user.tenant) # # # Apply filters # filters = {} # # action_type = self.request.GET.get('action_type') # if action_type: # filters['action_type'] = action_type # # user_id = self.request.GET.get('user') # if user_id: # filters['user_id'] = user_id # # content_type_id = self.request.GET.get('content_type') # if content_type_id: # filters['content_type_id'] = content_type_id # # date_from = self.request.GET.get('date_from') # if date_from: # filters['timestamp__date__gte'] = date_from # # date_to = self.request.GET.get('date_to') # if date_to: # filters['timestamp__date__lte'] = date_to # # # Apply search term # search_term = self.request.GET.get('search') # if search_term: # queryset = queryset.filter( # Q(username__icontains=search_term) | # Q(object_repr__icontains=search_term) | # Q(ip_address__icontains=search_term) # ) # # # Apply filters # if filters: # queryset = queryset.filter(**filters) # # return queryset.select_related('user', 'content_type').order_by('-timestamp') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # # # Add filter options # context['action_types'] = dict(AuditLogEntry.ACTION_TYPE_CHOICES) # # # Add content types for filtering # context['content_types'] = ContentType.objects.filter( # id__in=AuditLogEntry.objects.values_list('content_type_id', flat=True).distinct() # ) # # # Add users for filtering # user_ids = AuditLogEntry.objects.values_list('user_id', flat=True).distinct() # context['users'] = User.objects.filter(id__in=user_ids) # # # Current filters # context['current_filters'] = { # 'action_type': self.request.GET.get('action_type', ''), # 'user': self.request.GET.get('user', ''), # 'content_type': self.request.GET.get('content_type', ''), # 'date_from': self.request.GET.get('date_from', ''), # 'date_to': self.request.GET.get('date_to', ''), # 'search': self.request.GET.get('search', '') # } # # return context # # # class AuditLogDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """View for displaying audit log details.""" # model = AuditLogEntry # template_name = 'core/audit_log_detail.html' # context_object_name = 'audit_log' # permission_required = 'core.view_auditlogentry' # # def get_queryset(self): # """Filter by user's tenant if not superuser.""" # queryset = super().get_queryset() # user = self.request.user # # if not user.is_superuser and hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(tenant=user.tenant) # # return queryset.select_related('user', 'content_type') # # # class SystemConfigurationListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """View for listing system configurations.""" # model = SystemConfiguration # template_name = 'core/system_configuration_list.html' # context_object_name = 'configurations' # paginate_by = 50 # permission_required = 'core.view_systemconfiguration' # # def get_queryset(self): # queryset = super().get_queryset() # user = self.request.user # # # Filter by tenant for non-superusers # if not user.is_superuser: # if hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(Q(tenant=user.tenant) | Q(tenant__isnull=True)) # else: # queryset = queryset.filter(tenant__isnull=True) # # # Apply filters # category = self.request.GET.get('category') # if category: # queryset = queryset.filter(category=category) # # tenant_id = self.request.GET.get('tenant') # if tenant_id: # if tenant_id == 'system': # queryset = queryset.filter(tenant__isnull=True) # else: # queryset = queryset.filter(tenant_id=tenant_id) # # # Apply search # search_term = self.request.GET.get('search') # if search_term: # queryset = queryset.filter( # Q(key__icontains=search_term) | # Q(description__icontains=search_term) # ) # # return queryset.select_related('tenant').order_by('category', 'key') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # # # Add filter options # context['categories'] = dict(SystemConfiguration.CATEGORY_CHOICES) # # # Add tenants for filtering if superuser # if self.request.user.is_superuser: # context['tenants'] = Tenant.objects.filter(is_active=True) # # # Group configurations by category # configurations_by_category = {} # # for config in context['configurations']: # category = config.get_category_display() # if category not in configurations_by_category: # configurations_by_category[category] = [] # configurations_by_category[category].append(config) # # context['configurations_by_category'] = configurations_by_category # # # Current filters # context['current_filters'] = { # 'category': self.request.GET.get('category', ''), # 'tenant': self.request.GET.get('tenant', ''), # 'search': self.request.GET.get('search', '') # } # # return context # # # class SystemConfigurationDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """View for displaying system configuration details.""" # model = SystemConfiguration # template_name = 'core/system_configuration_detail.html' # context_object_name = 'configuration' # permission_required = 'core.view_systemconfiguration' # # def get_queryset(self): # """Filter by user's tenant if not superuser.""" # queryset = super().get_queryset() # user = self.request.user # # if not user.is_superuser: # if hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(Q(tenant=user.tenant) | Q(tenant__isnull=True)) # else: # queryset = queryset.filter(tenant__isnull=True) # # return queryset.select_related('tenant', 'created_by') # # # class SystemConfigurationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """View for creating a new system configuration.""" # model = SystemConfiguration # form_class = SystemConfigurationForm # template_name = 'core/system_configuration_form.html' # permission_required = 'core.add_systemconfiguration' # # def get_form_kwargs(self): # """Pass the current user to the form.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = _('Create New Configuration') # return context # # def form_valid(self, form): # """Set the created_by field to the current user.""" # form.instance.created_by = self.request.user # # # Set tenant automatically for non-superusers # if not self.request.user.is_superuser and hasattr(self.request.user, 'tenant'): # form.instance.tenant = self.request.user.tenant # # response = super().form_valid(form) # # # Log the action # AuditLogEntry.log_action( # user=self.request.user, # action_type='CREATE', # content_object=self.object, # request=self.request # ) # # messages.success( # self.request, # _('Configuration "{}" was created successfully.').format(self.object.key) # ) # # return response # # def get_success_url(self): # """Return to the configuration detail page after creation.""" # return reverse('core:system_configuration_detail', kwargs={'pk': self.object.pk}) # # def form_invalid(self, form): # """Handle form validation errors.""" # messages.error( # self.request, # _('There was an error creating the configuration. Please check the form.') # ) # return super().form_invalid(form) # # # class SystemConfigurationUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """View for updating a system configuration.""" # model = SystemConfiguration # form_class = SystemConfigurationForm # template_name = 'core/system_configuration_form.html' # permission_required = 'core.change_systemconfiguration' # # def get_queryset(self): # """Filter by user's tenant if not superuser.""" # queryset = super().get_queryset() # user = self.request.user # # if not user.is_superuser: # if hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter( # Q(tenant=user.tenant) | # Q(tenant__isnull=True, is_tenant_editable=True) # ) # else: # queryset = queryset.none() # # return queryset # # def get_form_kwargs(self): # """Pass the current user to the form.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = _('Update Configuration') # return context # # def form_valid(self, form): # """Log the update action.""" # # Capture changes # if self.object: # changes = {} # for field in form.changed_data: # old_value = getattr(self.object, field) # new_value = form.cleaned_data[field] # # # Handle encrypted values # if field == 'value' and self.object.is_encrypted: # old_value = '********' # new_value = '********' # # changes[field] = { # 'old': str(old_value), # 'new': str(new_value) # } # # response = super().form_valid(form) # # # Log the action # AuditLogEntry.log_action( # user=self.request.user, # action_type='UPDATE', # content_object=self.object, # changes=changes, # request=self.request # ) # # messages.success( # self.request, # _('Configuration "{}" was updated successfully.').format(self.object.key) # ) # # # Check if restart is required # if self.object.requires_restart: # messages.warning( # self.request, # _('This configuration change requires a system restart to take effect.') # ) # # return response # # def get_success_url(self): # """Return to the configuration detail page after update.""" # return reverse('core:system_configuration_detail', kwargs={'pk': self.object.pk}) # # def form_invalid(self, form): # """Handle form validation errors.""" # messages.error( # self.request, # _('There was an error updating the configuration. Please check the form.') # ) # return super().form_invalid(form) # # # class SystemConfigurationDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """View for deleting a system configuration.""" # model = SystemConfiguration # template_name = 'core/system_configuration_confirm_delete.html' # permission_required = 'core.delete_systemconfiguration' # # def get_queryset(self): # """Filter by user's tenant if not superuser.""" # queryset = super().get_queryset() # user = self.request.user # # if not user.is_superuser: # if hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(tenant=user.tenant) # else: # queryset = queryset.none() # # return queryset # # def delete(self, request, *args, **kwargs): # """Override delete to log the action.""" # self.object = self.get_object() # key = self.object.key # # # Log the action before deletion # AuditLogEntry.log_action( # user=request.user, # action_type='DELETE', # content_object=self.object, # request=request # ) # # # Proceed with deletion # self.object.delete() # # messages.success( # request, # _('Configuration "{}" was deleted successfully.').format(key) # ) # # # Return to configuration list # return HttpResponseRedirect(reverse('core:system_configuration_list')) # # # class SystemNotificationListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """View for listing system notifications.""" # model = SystemNotification # template_name = 'core/system_notification_list.html' # context_object_name = 'notifications' # paginate_by = 20 # permission_required = 'core.view_systemnotification' # # def get_queryset(self): # queryset = super().get_queryset() # user = self.request.user # # # Filter by tenant for non-superusers # if not user.is_superuser: # if hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(Q(tenant=user.tenant) | Q(tenant__isnull=True)) # else: # queryset = queryset.filter(tenant__isnull=True) # # # Apply filters # notification_type = self.request.GET.get('notification_type') # if notification_type: # queryset = queryset.filter(notification_type=notification_type) # # visibility = self.request.GET.get('visibility') # if visibility: # queryset = queryset.filter(visibility=visibility) # # is_active = self.request.GET.get('is_active') # if is_active is not None: # is_active_bool = is_active.lower() == 'true' # queryset = queryset.filter(is_active=is_active_bool) # # # Apply search # search_term = self.request.GET.get('search') # if search_term: # queryset = queryset.filter( # Q(title__icontains=search_term) | # Q(message__icontains=search_term) # ) # # return queryset.select_related('tenant', 'created_by').order_by('-start_date') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # # # Add filter options # context['notification_types'] = dict(SystemNotification.NOTIFICATION_TYPE_CHOICES) # context['visibility_options'] = dict(SystemNotification.VISIBILITY_CHOICES) # # # Current filters # context['current_filters'] = { # 'notification_type': self.request.GET.get('notification_type', ''), # 'visibility': self.request.GET.get('visibility', ''), # 'is_active': self.request.GET.get('is_active', ''), # 'search': self.request.GET.get('search', '') # } # # return context # # # class SystemNotificationDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """View for displaying system notification details.""" # model = SystemNotification # template_name = 'core/system_notification_detail.html' # context_object_name = 'notification' # permission_required = 'core.view_systemnotification' # # def get_queryset(self): # """Filter by user's tenant if not superuser.""" # queryset = super().get_queryset() # user = self.request.user # # if not user.is_superuser: # if hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(Q(tenant=user.tenant) | Q(tenant__isnull=True)) # else: # queryset = queryset.filter(tenant__isnull=True) # # return queryset.select_related('tenant', 'created_by') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # notification = self.get_object() # # # Check if user has dismissed this notification # if notification.is_dismissible: # context['is_dismissed'] = notification.dismissals.filter( # pk=self.request.user.pk # ).exists() # # # Get dismissal count # context['dismissal_count'] = notification.dismissals.count() # # # Get user groups that can see this notification # if notification.visibility == 'CUSTOM': # context['target_groups'] = notification.target_groups.all() # # return context # # # class SystemNotificationCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """View for creating a new system notification.""" # model = SystemNotification # form_class = SystemNotificationForm # template_name = 'core/system_notification_form.html' # permission_required = 'core.add_systemnotification' # # def get_form_kwargs(self): # """Pass the current user to the form.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = _('Create New Notification') # return context # # def form_valid(self, form): # """Set the created_by field to the current user.""" # form.instance.created_by = self.request.user # # # Set tenant automatically for non-superusers # if not self.request.user.is_superuser and hasattr(self.request.user, 'tenant'): # form.instance.tenant = self.request.user.tenant # # response = super().form_valid(form) # # # Log the action # AuditLogEntry.log_action( # user=self.request.user, # action_type='CREATE', # content_object=self.object, # request=self.request # ) # # messages.success( # self.request, # _('Notification "{}" was created successfully.').format(self.object.title) # ) # # return response # # def get_success_url(self): # """Return to the notification detail page after creation.""" # return reverse('core:system_notification_detail', kwargs={'pk': self.object.pk}) # # def form_invalid(self, form): # """Handle form validation errors.""" # messages.error( # self.request, # _('There was an error creating the notification. Please check the form.') # ) # return super().form_invalid(form) # # # class SystemNotificationUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): # """View for updating a system notification.""" # model = SystemNotification # form_class = SystemNotificationForm # template_name = 'core/system_notification_form.html' # permission_required = 'core.change_systemnotification' # # def get_queryset(self): # """Filter by user's tenant if not superuser.""" # queryset = super().get_queryset() # user = self.request.user # # if not user.is_superuser: # if hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(tenant=user.tenant) # else: # queryset = queryset.none() # # return queryset # # def get_form_kwargs(self): # """Pass the current user to the form.""" # kwargs = super().get_form_kwargs() # kwargs['user'] = self.request.user # return kwargs # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context['title'] = _('Update Notification') # return context # # def form_valid(self, form): # """Log the update action.""" # # Capture changes # if self.object: # changes = {} # for field in form.changed_data: # if field != 'target_groups': # M2M requires special handling # old_value = getattr(self.object, field) # new_value = form.cleaned_data[field] # changes[field] = { # 'old': str(old_value), # 'new': str(new_value) # } # # response = super().form_valid(form) # # # Log the action # AuditLogEntry.log_action( # user=self.request.user, # action_type='UPDATE', # content_object=self.object, # changes=changes, # request=self.request # ) # # messages.success( # self.request, # _('Notification "{}" was updated successfully.').format(self.object.title) # ) # # return response # # def get_success_url(self): # """Return to the notification detail page after update.""" # return reverse('core:system_notification_detail', kwargs={'pk': self.object.pk}) # # def form_invalid(self, form): # """Handle form validation errors.""" # messages.error( # self.request, # _('There was an error updating the notification. Please check the form.') # ) # return super().form_invalid(form) # # # class SystemNotificationDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """View for deleting a system notification.""" # model = SystemNotification # template_name = 'core/system_notification_confirm_delete.html' # permission_required = 'core.delete_systemnotification' # # def get_queryset(self): # """Filter by user's tenant if not superuser.""" # queryset = super().get_queryset() # user = self.request.user # # if not user.is_superuser: # if hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(tenant=user.tenant) # else: # queryset = queryset.none() # # return queryset # # def delete(self, request, *args, **kwargs): # """Override delete to log the action.""" # self.object = self.get_object() # title = self.object.title # # # Log the action before deletion # AuditLogEntry.log_action( # user=request.user, # action_type='DELETE', # content_object=self.object, # request=request # ) # # # Proceed with deletion # self.object.delete() # # messages.success( # request, # _('Notification "{}" was deleted successfully.').format(title) # ) # # # Return to notification list # return HttpResponseRedirect(reverse('core:system_notification_list')) # # # class IntegrationLogListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # """View for listing integration logs.""" # model = IntegrationLog # template_name = 'core/integration_log_list.html' # context_object_name = 'logs' # paginate_by = 50 # permission_required = 'core.view_integrationlog' # # def get_queryset(self): # queryset = super().get_queryset() # user = self.request.user # # # Filter by tenant for non-superusers # if not user.is_superuser: # if hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(tenant=user.tenant) # else: # queryset = queryset.filter(tenant__isnull=True) # # # Get form data # form = IntegrationLogFilterForm( # self.request.GET, # tenant=getattr(user, 'tenant', None) # ) # # if form.is_valid(): # # Apply filters # integration_name = form.cleaned_data.get('integration_name') # if integration_name: # queryset = queryset.filter(integration_name=integration_name) # # event_type = form.cleaned_data.get('event_type') # if event_type: # queryset = queryset.filter(event_type=event_type) # # status = form.cleaned_data.get('status') # if status: # queryset = queryset.filter(status=status) # # direction = form.cleaned_data.get('direction') # if direction: # queryset = queryset.filter(direction=direction) # # date_from = form.cleaned_data.get('date_from') # if date_from: # queryset = queryset.filter(timestamp__gte=date_from) # # date_to = form.cleaned_data.get('date_to') # if date_to: # queryset = queryset.filter(timestamp__lte=date_to) # # correlation_id = form.cleaned_data.get('correlation_id') # if correlation_id: # queryset = queryset.filter(correlation_id=correlation_id) # # return queryset.select_related('tenant', 'user').order_by('-timestamp') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # # # Add filter form # context['filter_form'] = IntegrationLogFilterForm( # self.request.GET, # tenant=getattr(self.request.user, 'tenant', None) # ) # # # Integration success rates # logs = context['logs'] # integrations = {} # # for log in logs: # name = log.integration_name # if name not in integrations: # integrations[name] = {'success': 0, 'total': 0} # # integrations[name]['total'] += 1 # if log.status == 'SUCCESS': # integrations[name]['success'] += 1 # # for name, data in integrations.items(): # if data['total'] > 0: # data['success_rate'] = (data['success'] / data['total']) * 100 # else: # data['success_rate'] = 0 # # context['integrations'] = integrations # # return context # # # class IntegrationLogDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): # """View for displaying integration log details.""" # model = IntegrationLog # template_name = 'core/integration_log_detail.html' # context_object_name = 'log' # permission_required = 'core.view_integrationlog' # # def get_queryset(self): # """Filter by user's tenant if not superuser.""" # queryset = super().get_queryset() # user = self.request.user # # if not user.is_superuser: # if hasattr(user, 'tenant') and user.tenant: # queryset = queryset.filter(tenant=user.tenant) # else: # queryset = queryset.filter(tenant__isnull=True) # # return queryset.select_related('tenant', 'user', 'related_object_type') # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # log = self.get_object() # # # Get related logs with same correlation ID # if log.correlation_id: # context['related_logs'] = IntegrationLog.objects.filter( # correlation_id=log.correlation_id # ).exclude(pk=log.pk).order_by('-timestamp') # # # Format request/response data for better display # if log.request_data: # try: # context['formatted_request'] = json.dumps(log.request_data, indent=2) # except: # context['formatted_request'] = str(log.request_data) # # if log.response_data: # try: # context['formatted_response'] = json.dumps(log.response_data, indent=2) # except: # context['formatted_response'] = str(log.response_data) # # # Get related object if available # if log.related_object_type and log.related_object_id: # try: # model_class = log.related_object_type.model_class() # context['related_object'] = model_class.objects.filter( # pk=log.related_object_id # ).first() # except: # context['related_object'] = None # # return context # # # @login_required # @permission_required('core.view_systemnotification') # def dashboard_stats(request): # """API endpoint for dashboard statistics.""" # user = request.user # tenant = getattr(user, 'tenant', None) # # # Default to last 30 days for stats # thirty_days_ago = timezone.now() - timezone.timedelta(days=30) # # # Basic system stats # stats = { # 'tenant_count': Tenant.objects.filter(is_active=True).count(), # 'department_count': Department.objects.count(), # 'active_notifications': SystemNotification.objects.filter( # is_active=True, # start_date__lte=timezone.now(), # ).count(), # } # # # Audit log stats # if user.is_superuser: # log_base_query = AuditLogEntry.objects # elif tenant: # log_base_query = AuditLogEntry.objects.filter(tenant=tenant) # else: # log_base_query = AuditLogEntry.objects.none() # # audit_logs = log_base_query.filter(timestamp__gte=thirty_days_ago) # # stats['audit_logs'] = { # 'total': audit_logs.count(), # 'by_action': dict(audit_logs.values('action_type').annotate( # count=Count('action_type') # ).values_list('action_type', 'count')) # } # # # Integration stats # if user.is_superuser or (user.is_staff and user.has_perm('core.view_integrationlog')): # if tenant: # integration_base_query = IntegrationLog.objects.filter(tenant=tenant) # else: # integration_base_query = IntegrationLog.objects # # integration_logs = integration_base_query.filter(timestamp__gte=thirty_days_ago) # # stats['integrations'] = { # 'total': integration_logs.count(), # 'success_count': integration_logs.filter(status='SUCCESS').count(), # 'error_count': integration_logs.filter(status='ERROR').count(), # 'by_status': dict(integration_logs.values('status').annotate( # count=Count('status') # ).values_list('status', 'count')) # } # # return JsonResponse({'status': 'success', 'data': stats}) # # # @login_required # @permission_required('core.view_auditlogentry') # def audit_log_search(request): # """API endpoint for searching audit logs.""" # user = request.user # tenant = getattr(user, 'tenant', None) # # # Get search parameters # search_term = request.GET.get('term', '') # action_type = request.GET.get('action_type', '') # date_from = request.GET.get('date_from', '') # date_to = request.GET.get('date_to', '') # # # Base query # if user.is_superuser: # query = AuditLogEntry.objects.all() # elif tenant: # query = AuditLogEntry.objects.filter(tenant=tenant) # else: # query = AuditLogEntry.objects.none() # # # Apply filters # if search_term: # query = query.filter( # Q(username__icontains=search_term) | # Q(object_repr__icontains=search_term) | # Q(ip_address__icontains=search_term) # ) # # if action_type: # query = query.filter(action_type=action_type) # # if date_from: # query = query.filter(timestamp__date__gte=date_from) # # if date_to: # query = query.filter(timestamp__date__lte=date_to) # # # Get results # results = query.order_by('-timestamp')[:100] # # # Format results # formatted_results = [{ # 'id': str(log.log_id), # 'user': log.username, # 'action': log.get_action_type_display(), # 'object': log.object_repr, # 'timestamp': log.timestamp.isoformat(), # 'url': reverse('core:audit_log_detail', args=[log.log_id]) # } for log in results] # # return JsonResponse({ # 'status': 'success', # 'results': formatted_results, # 'count': len(formatted_results) # }) # # # @login_required # def system_notifications(request): # """API endpoint for getting active system notifications for the current user.""" # user = request.user # # # Get active notifications # active_notifications = SystemNotification.objects.filter( # is_active=True, # start_date__lte=timezone.now(), # ) # # # Filter to show relevant notifications to this user # user_notifications = [] # for notification in active_notifications: # if notification.is_visible_to_user(user): # user_notifications.append({ # 'id': str(notification.notification_id), # 'title': notification.title, # 'message': notification.message, # 'type': notification.notification_type, # 'is_dismissible': notification.is_dismissible, # 'link_url': notification.link_url, # 'link_text': notification.link_text # }) # # return JsonResponse({ # 'status': 'success', # 'notifications': user_notifications # }) # # # @login_required # @require_POST # @csrf_protect # def dismiss_notification(request): # """API endpoint for dismissing a notification.""" # form = NotificationDismissForm(request.POST) # # if form.is_valid(): # notification_id = form.cleaned_data['notification_id'] # # try: # notification = SystemNotification.objects.get(notification_id=notification_id) # # # Check if notification is dismissible # if not notification.is_dismissible: # return JsonResponse({ # 'status': 'error', # 'message': _('This notification cannot be dismissed.') # }, status=400) # # # Check if already dismissed # if NotificationDismissal.objects.filter( # notification=notification, # user=request.user # ).exists(): # return JsonResponse({ # 'status': 'success', # 'message': _('Notification already dismissed.') # }) # # # Create dismissal record # dismissal = NotificationDismissal.objects.create( # notification=notification, # user=request.user # ) # # # Log the action # AuditLogEntry.log_action( # user=request.user, # action_type='OTHER', # content_object=dismissal, # action_details={'action': 'notification_dismissed'}, # request=request # ) # # return JsonResponse({ # 'status': 'success', # 'message': _('Notification dismissed successfully.') # }) # # except SystemNotification.DoesNotExist: # return JsonResponse({ # 'status': 'error', # 'message': _('Notification not found.') # }, status=404) # # return JsonResponse({ # 'status': 'error', # 'message': _('Invalid form data.'), # 'errors': form.errors # }, status=400) # # # @login_required # def tenant_info(request): # """API endpoint for getting information about the current user's tenant.""" # user = request.user # tenant = getattr(user, 'tenant', None) # # if not tenant: # return JsonResponse({ # 'status': 'error', # 'message': _('No tenant associated with this user.') # }, status=404) # # tenant_data = { # 'id': str(tenant.tenant_id), # 'name': tenant.name, # 'code': tenant.code, # 'organization_type': tenant.get_organization_type_display(), # 'is_active': tenant.is_active, # 'subscription_valid': tenant.is_subscription_valid(), # 'department_count': tenant.departments.count(), # 'active_users_count': tenant.get_active_users_count() # } # # return JsonResponse({ # 'status': 'success', # 'tenant': tenant_data # }) # # # @login_required # @permission_required('core.view_integrationlog') # def system_health(request): # """API endpoint for system health information.""" # user = request.user # tenant = getattr(user, 'tenant', None) # # # Default to last 7 days for stats # days = int(request.GET.get('days', 7)) # period_start = timezone.now() - timezone.timedelta(days=days) # # # Base health data # health_data = { # 'tenants': { # 'active': Tenant.objects.filter(is_active=True).count(), # 'inactive': Tenant.objects.filter(is_active=False).count(), # }, # 'departments': Department.objects.count(), # } # # # Integration health # if user.is_superuser: # integration_base_query = IntegrationLog.objects # elif tenant: # integration_base_query = IntegrationLog.objects.filter(tenant=tenant) # else: # integration_base_query = IntegrationLog.objects.none() # # integration_logs = integration_base_query.filter(timestamp__gte=period_start) # integration_stats = { # 'total': integration_logs.count(), # 'success': integration_logs.filter(status='SUCCESS').count(), # 'error': integration_logs.filter(status='ERROR').count(), # 'warning': integration_logs.filter(status='WARNING').count(), # 'by_integration': {} # } # # # Get stats by integration # integrations = integration_logs.values('integration_name').distinct() # for integration in integrations: # name = integration['integration_name'] # logs = integration_logs.filter(integration_name=name) # # total = logs.count() # success = logs.filter(status='SUCCESS').count() # # integration_stats['by_integration'][name] = { # 'total': total, # 'success': success, # 'error': logs.filter(status='ERROR').count(), # 'success_rate': (success / total * 100) if total > 0 else 0 # } # # health_data['integrations'] = integration_stats # # # Recent system errors # if user.is_superuser: # recent_errors = IntegrationLog.objects.filter( # status='ERROR', # timestamp__gte=period_start # ).order_by('-timestamp')[:10] # elif tenant: # recent_errors = IntegrationLog.objects.filter( # tenant=tenant, # status='ERROR', # timestamp__gte=period_start # ).order_by('-timestamp')[:10] # else: # recent_errors = IntegrationLog.objects.none() # # health_data['recent_errors'] = [{ # 'id': str(log.log_id), # 'integration': log.integration_name, # 'event_type': log.event_type, # 'timestamp': log.timestamp.isoformat(), # 'error_message': log.error_message, # 'url': reverse('core:integration_log_detail', args=[log.log_id]) # } for log in recent_errors] # # return JsonResponse({ # 'status': 'success', # 'data': health_data # }) # # # @login_required # @permission_required('core.change_systemnotification') # @require_POST # @csrf_protect # def activate_notification(request, pk): # """Activate a system notification.""" # try: # notification = SystemNotification.objects.get(pk=pk) # # # Check permissions for tenant-specific notifications # user = request.user # tenant = getattr(user, 'tenant', None) # # if not user.is_superuser and notification.tenant and notification.tenant != tenant: # return JsonResponse({ # 'status': 'error', # 'message': _('You do not have permission to modify this notification.') # }, status=403) # # # Activate the notification # notification.is_active = True # notification.save() # # # Log the action # AuditLogEntry.log_action( # user=request.user, # action_type='UPDATE', # content_object=notification, # changes={'is_active': {'old': 'False', 'new': 'True'}}, # request=request # ) # # return JsonResponse({ # 'status': 'success', # 'message': _('Notification activated successfully.') # }) # # except SystemNotification.DoesNotExist: # return JsonResponse({ # 'status': 'error', # 'message': _('Notification not found.') # }, status=404) # # # @login_required # @permission_required('core.change_systemnotification') # @require_POST # @csrf_protect # def deactivate_notification(request, pk): # """Deactivate a system notification.""" # try: # notification = SystemNotification.objects.get(pk=pk) # # # Check permissions for tenant-specific notifications # user = request.user # tenant = getattr(user, 'tenant', None) # # if not user.is_superuser and notification.tenant and notification.tenant != tenant: # return JsonResponse({ # 'status': 'error', # 'message': _('You do not have permission to modify this notification.') # }, status=403) # # # Deactivate the notification # notification.is_active = False # notification.save() # # # Log the action # AuditLogEntry.log_action( # user=request.user, # action_type='UPDATE', # content_object=notification, # changes={'is_active': {'old': 'True', 'new': 'False'}}, # request=request # ) # # return JsonResponse({ # 'status': 'success', # 'message': _('Notification deactivated successfully.') # }) # # except SystemNotification.DoesNotExist: # return JsonResponse({ # 'status': 'error', # 'message': _('Notification not found.') # }, status=404) # # # @login_required # @permission_required('core.change_systemconfiguration') # @require_POST # @csrf_protect # def reset_configuration(request, pk): # """Reset a system configuration to its default value.""" # try: # config = SystemConfiguration.objects.get(pk=pk) # # # Check permissions for tenant-specific configurations # user = request.user # tenant = getattr(user, 'tenant', None) # # if not user.is_superuser: # if config.tenant and config.tenant != tenant: # return JsonResponse({ # 'status': 'error', # 'message': _('You do not have permission to modify this configuration.') # }, status=403) # # if not config.is_tenant_editable: # return JsonResponse({ # 'status': 'error', # 'message': _('This configuration cannot be modified by tenant users.') # }, status=403) # # # Get the default value # old_value = config.value # # # For tenant-specific configurations, try to get the system-wide value # if config.tenant: # try: # system_config = SystemConfiguration.objects.get( # key=config.key, # tenant=None # ) # config.value = system_config.value # config.save() # # # Log the action # AuditLogEntry.log_action( # user=request.user, # action_type='UPDATE', # content_object=config, # changes={'value': {'old': old_value, 'new': config.value}}, # request=request # ) # # return JsonResponse({ # 'status': 'success', # 'message': _('Configuration reset to system default successfully.'), # 'new_value': config.value # }) # # except SystemConfiguration.DoesNotExist: # return JsonResponse({ # 'status': 'error', # 'message': _('No system-wide default value found for this configuration.') # }, status=404) # # # For system-wide configurations, this operation doesn't make sense # return JsonResponse({ # 'status': 'error', # 'message': _('System-wide configurations cannot be reset to defaults.') # }, status=400) # # except SystemConfiguration.DoesNotExist: # return JsonResponse({ # 'status': 'error', # 'message': _('Configuration not found.') # }, status=404) # # # @login_required # def notification_search(request): # """API endpoint for searching notifications.""" # user = request.user # tenant = getattr(user, 'tenant', None) # # # Get search parameters # search_term = request.GET.get('term', '') # notification_type = request.GET.get('notification_type', '') # is_active = request.GET.get('is_active', '') # # # Base query # if user.is_superuser: # query = SystemNotification.objects.all() # elif tenant: # query = SystemNotification.objects.filter( # Q(tenant=tenant) | Q(tenant__isnull=True) # ) # else: # query = SystemNotification.objects.filter(tenant__isnull=True) # # # Apply filters # if search_term: # query = query.filter( # Q(title__icontains=search_term) | # Q(message__icontains=search_term) # ) # # if notification_type: # query = query.filter(notification_type=notification_type) # # if is_active: # is_active_bool = is_active.lower() == 'true' # query = query.filter(is_active=is_active_bool) # # # Only include currently visible notifications # if request.GET.get('visible_only', '') == 'true': # now = timezone.now() # query = query.filter( # is_active=True, # start_date__lte=now, # ) # # # Get results # results = query.order_by('-start_date')[:20] # # # Format results # formatted_results = [] # for notification in results: # # Check if this notification should be visible to the user # if request.GET.get('check_visibility', '') == 'true': # if not notification.is_visible_to_user(user): # continue # # formatted_results.append({ # 'id': str(notification.notification_id), # 'title': notification.title, # 'message': notification.message[:100] + '...' if len(notification.message) > 100 else notification.message, # 'type': notification.notification_type, # 'type_display': notification.get_notification_type_display(), # 'is_active': notification.is_active, # 'is_dismissible': notification.is_dismissible, # 'start_date': notification.start_date.isoformat(), # 'end_date': notification.end_date.isoformat() if notification.end_date else None, # 'url': reverse('core:system_notification_detail', args=[notification.notification_id]) # }) # # return JsonResponse({ # 'status': 'success', # 'results': formatted_results, # 'count': len(formatted_results) # }) # # # @login_required # def department_search(request): # """API endpoint for searching departments.""" # user = request.user # tenant = getattr(user, 'tenant', None) # # # Get search parameters # search_term = request.GET.get('term', '') # department_type = request.GET.get('department_type', '') # is_active = request.GET.get('is_active', '') # # # Base query # if user.is_superuser: # query = Department.objects.all() # elif tenant: # query = Department.objects.filter(tenant=tenant) # else: # query = Department.objects.none() # # # Apply filters # if search_term: # query = query.filter( # Q(name__icontains=search_term) | # Q(code__icontains=search_term) | # Q(description__icontains=search_term) # ) # # if department_type: # query = query.filter(department_type=department_type) # # if is_active: # is_active_bool = is_active.lower() == 'true' # query = query.filter(is_active=is_active_bool) # # # Get results # results = query.order_by('name')[:20] # # # Format results # formatted_results = [{ # 'id': str(department.department_id), # 'name': department.name, # 'code': department.code, # 'type': department.get_department_type_display(), # 'is_active': department.is_active, # 'department_head': department.department_head.get_full_name() if department.department_head else None, # 'location': department.get_full_location(), # 'url': reverse('core:department_detail', args=[department.department_id]) # } for department in results] # # return JsonResponse({ # 'status': 'success', # 'results': formatted_results, # 'count': len(formatted_results) # }) # # # @login_required # @permission_required('core.change_tenant') # @require_POST # @csrf_protect # def activate_tenant(request, pk): # """Activate a tenant.""" # try: # tenant = Tenant.objects.get(pk=pk) # # # Only superusers can activate any tenant # if not request.user.is_superuser: # user_tenant = getattr(request.user, 'tenant', None) # if tenant != user_tenant: # return JsonResponse({ # 'status': 'error', # 'message': _('You do not have permission to modify this tenant.') # }, status=403) # # # Activate the tenant # if tenant.is_active: # return JsonResponse({ # 'status': 'info', # 'message': _('Tenant is already active.') # }) # # tenant.is_active = True # tenant.save() # # # Log the action # AuditLogEntry.log_action( # user=request.user, # action_type='UPDATE', # content_object=tenant, # changes={'is_active': {'old': 'False', 'new': 'True'}}, # request=request # ) # # return JsonResponse({ # 'status': 'success', # 'message': _('Tenant activated successfully.') # }) # # except Tenant.DoesNotExist: # return JsonResponse({ # 'status': 'error', # 'message': _('Tenant not found.') # }, status=404) # # # @login_required # @permission_required('core.change_tenant') # @require_POST # @csrf_protect # def deactivate_tenant(request, pk): # """Deactivate a tenant.""" # try: # tenant = Tenant.objects.get(pk=pk) # # # Only superusers can deactivate any tenant # if not request.user.is_superuser: # user_tenant = getattr(request.user, 'tenant', None) # if tenant != user_tenant: # return JsonResponse({ # 'status': 'error', # 'message': _('You do not have permission to modify this tenant.') # }, status=403) # # # Prevent deactivating your own tenant # if tenant == getattr(request.user, 'tenant', None): # return JsonResponse({ # 'status': 'error', # 'message': _('You cannot deactivate your own tenant.') # }, status=400) # # # Deactivate the tenant # if not tenant.is_active: # return JsonResponse({ # 'status': 'info', # 'message': _('Tenant is already inactive.') # }) # # tenant.is_active = False # tenant.save() # # # Log the action # AuditLogEntry.log_action( # user=request.user, # action_type='UPDATE', # content_object=tenant, # changes={'is_active': {'old': 'True', 'new': 'False'}}, # request=request # ) # # return JsonResponse({ # 'status': 'success', # 'message': _('Tenant deactivated successfully.') # }) # # except Tenant.DoesNotExist: # return JsonResponse({ # 'status': 'error', # 'message': _('Tenant not found.') # }, status=404) # # # @login_required # @permission_required('core.change_department') # @require_POST # @csrf_protect # def activate_department(request, pk): # """Activate a department.""" # try: # department = Department.objects.get(pk=pk) # # # Check permissions # user = request.user # tenant = getattr(user, 'tenant', None) # # if not user.is_superuser and department.tenant != tenant: # return JsonResponse({ # 'status': 'error', # 'message': _('You do not have permission to modify this department.') # }, status=403) # # # Activate the department # if department.is_active: # return JsonResponse({ # 'status': 'info', # 'message': _('Department is already active.') # }) # # department.is_active = True # department.save() # # # Log the action # AuditLogEntry.log_action( # user=request.user, # action_type='UPDATE', # content_object=department, # changes={'is_active': {'old': 'False', 'new': 'True'}}, # request=request # ) # # return JsonResponse({ # 'status': 'success', # 'message': _('Department activated successfully.') # }) # # except Department.DoesNotExist: # return JsonResponse({ # 'status': 'error', # 'message': _('Department not found.') # }, status=404) # # # @login_required # @permission_required('core.change_department') # @require_POST # @csrf_protect # def deactivate_department(request, pk): # """Deactivate a department.""" # try: # department = Department.objects.get(pk=pk) # # # Check permissions # user = request.user # tenant = getattr(user, 'tenant', None) # # if not user.is_superuser and department.tenant != tenant: # return JsonResponse({ # 'status': 'error', # 'message': _('You do not have permission to modify this department.') # }, status=403) # # # Check if this is the user's department # if hasattr(user, 'department') and user.department == department: # return JsonResponse({ # 'status': 'error', # 'message': _('You cannot deactivate your own department.') # }, status=400) # # # Deactivate the department # if not department.is_active: # return JsonResponse({ # 'status': 'info', # 'message': _('Department is already inactive.') # }) # # department.is_active = False # department.save() # # # Log the action # AuditLogEntry.log_action( # user=request.user, # action_type='UPDATE', # content_object=department, # changes={'is_active': {'old': 'True', 'new': 'False'}}, # request=request # ) # # return JsonResponse({ # 'status': 'success', # 'message': _('Department deactivated successfully.') # }) # # except Department.DoesNotExist: # return JsonResponse({ # 'status': 'error', # 'message': _('Department not found.') # }, status=404) #