from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.db.models import Count, Q from django.utils import timezone from django.views.generic import TemplateView, ListView, DetailView from django.shortcuts import get_object_or_404 from apps.batches.models import ProcessingBatch from apps.qc.models import QCResult from apps.shipments.models import Shipment from apps.capa.models import CAPARecord from apps.documents.models import Document from apps.esg.models import ESGFieldReport from apps.notifications.models import Notification class OpsRequiredMixin(UserPassesTestMixin): def test_func(self): user = self.request.user return user.is_authenticated and (user.role in ('ops', 'admin') or user.is_superuser) class DashboardView(OpsRequiredMixin, TemplateView): template_name = 'ops/dashboard.html' def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['total_batches'] = ProcessingBatch.objects.count() ctx['batches_by_status'] = { s: ProcessingBatch.objects.filter(status=s).count() for s, _ in ProcessingBatch.Status.choices } ctx['recent_qc'] = QCResult.objects.select_related('batch').order_by('-tested_at')[:10] ctx['qc_summary'] = QCResult.objects.aggregate( passed=Count('id', filter=Q(overall_pass=True)), failed=Count('id', filter=Q(overall_pass=False)), ) ctx['active_shipments'] = Shipment.objects.exclude(status='delivered').count() ctx['open_capas'] = CAPARecord.objects.filter(status__in=('open', 'in_progress')).count() ctx['recent_notifications'] = Notification.objects.filter( user=self.request.user, is_read=False )[:5] ctx['recent_batches'] = ProcessingBatch.objects.order_by('-created_at')[:5] return ctx class BatchListView(OpsRequiredMixin, ListView): model = ProcessingBatch template_name = 'ops/batch_list.html' context_object_name = 'batches' paginate_by = 25 def get_queryset(self): qs = ProcessingBatch.objects.all() status = self.request.GET.get('status') grade = self.request.GET.get('grade') search = self.request.GET.get('q') if status: qs = qs.filter(status=status) if grade: qs = qs.filter(grade=grade) if search: qs = qs.filter(batch_reference__icontains=search) return qs def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['status_choices'] = ProcessingBatch.Status.choices ctx['grade_choices'] = ProcessingBatch.Grade.choices ctx['current_status'] = self.request.GET.get('status', '') ctx['current_grade'] = self.request.GET.get('grade', '') ctx['current_q'] = self.request.GET.get('q', '') return ctx class BatchDetailView(OpsRequiredMixin, DetailView): model = ProcessingBatch template_name = 'ops/batch_detail.html' context_object_name = 'batch' def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) batch = self.object ctx['qc_results'] = QCResult.objects.filter(batch=batch) ctx['shipments'] = Shipment.objects.filter(batch=batch) ctx['capa_records'] = CAPARecord.objects.filter(batch=batch) ctx['documents'] = Document.objects.filter(batch=batch) ctx['esg_reports'] = ESGFieldReport.objects.filter(batch=batch) return ctx class QCListView(OpsRequiredMixin, ListView): model = QCResult template_name = 'ops/qc_list.html' context_object_name = 'results' paginate_by = 25 def get_queryset(self): qs = QCResult.objects.select_related('batch').all() passed = self.request.GET.get('passed') if passed == '1': qs = qs.filter(overall_pass=True) elif passed == '0': qs = qs.filter(overall_pass=False) return qs def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['current_passed'] = self.request.GET.get('passed', '') return ctx class QCDetailView(OpsRequiredMixin, DetailView): model = QCResult template_name = 'ops/qc_detail.html' context_object_name = 'result' class ShipmentListView(OpsRequiredMixin, ListView): model = Shipment template_name = 'ops/shipment_list.html' context_object_name = 'shipments' paginate_by = 25 def get_queryset(self): qs = Shipment.objects.select_related('batch').all() status = self.request.GET.get('status') if status: qs = qs.filter(status=status) return qs def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['status_choices'] = Shipment.Status.choices ctx['current_status'] = self.request.GET.get('status', '') return ctx class ShipmentDetailView(OpsRequiredMixin, DetailView): model = Shipment template_name = 'ops/shipment_detail.html' context_object_name = 'shipment' class CAPAListView(OpsRequiredMixin, ListView): model = CAPARecord template_name = 'ops/capa_list.html' context_object_name = 'records' paginate_by = 25 def get_queryset(self): qs = CAPARecord.objects.select_related('batch').all() status = self.request.GET.get('status') if status: qs = qs.filter(status=status) return qs def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['status_choices'] = CAPARecord.Status.choices ctx['current_status'] = self.request.GET.get('status', '') return ctx class CAPADetailView(OpsRequiredMixin, DetailView): model = CAPARecord template_name = 'ops/capa_detail.html' context_object_name = 'record' class DocumentListView(OpsRequiredMixin, ListView): model = Document template_name = 'ops/document_list.html' context_object_name = 'documents' paginate_by = 25 def get_queryset(self): qs = Document.objects.select_related('batch', 'uploaded_by').all() doc_type = self.request.GET.get('doc_type') if doc_type: qs = qs.filter(doc_type=doc_type) return qs def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) ctx['doc_type_choices'] = Document.DocType.choices ctx['current_doc_type'] = self.request.GET.get('doc_type', '') return ctx class ESGListView(OpsRequiredMixin, ListView): model = ESGFieldReport template_name = 'ops/esg_list.html' context_object_name = 'reports' paginate_by = 25 def get_queryset(self): return ESGFieldReport.objects.select_related('batch').all()