""" Views for MDT (Multi-Disciplinary Team) app. """ from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin from django.db.models import Q, Count from django.shortcuts import get_object_or_404, redirect from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from django.views.generic import ListView, DetailView, CreateView, UpdateView from core.mixins import TenantFilterMixin from .models import MDTNote, MDTContribution, MDTApproval, MDTAttachment, MDTMention from .forms import MDTNoteForm, MDTContributionForm, MDTApprovalForm, MDTAttachmentForm, MDTSearchForm from notifications.models import Notification class MDTNoteListView(LoginRequiredMixin, TenantFilterMixin, ListView): """List view for MDT notes.""" model = MDTNote template_name = 'mdt/mdt_note_list.html' context_object_name = 'mdt_notes' paginate_by = 20 def get_queryset(self): """Filter MDT notes by tenant and user access.""" queryset = super().get_queryset() # Filter by search form form = MDTSearchForm(self.request.GET, tenant=self.request.user.tenant) if form.is_valid(): if form.cleaned_data.get('patient'): search = form.cleaned_data['patient'] queryset = queryset.filter( Q(patient__mrn__icontains=search) | Q(patient__first_name_en__icontains=search) | Q(patient__last_name_en__icontains=search) ) if form.cleaned_data.get('status'): queryset = queryset.filter(status=form.cleaned_data['status']) if form.cleaned_data.get('contributor'): queryset = queryset.filter(contributors=form.cleaned_data['contributor']) if form.cleaned_data.get('date_from'): queryset = queryset.filter(created_at__gte=form.cleaned_data['date_from']) if form.cleaned_data.get('date_to'): queryset = queryset.filter(created_at__lte=form.cleaned_data['date_to']) return queryset.select_related('patient', 'initiated_by').prefetch_related('contributions', 'approvals') def get_context_data(self, **kwargs): """Add search form to context.""" context = super().get_context_data(**kwargs) context['search_form'] = MDTSearchForm(self.request.GET, tenant=self.request.user.tenant) # Add statistics context['total_notes'] = self.get_queryset().count() context['draft_notes'] = self.get_queryset().filter(status=MDTNote.Status.DRAFT).count() context['pending_notes'] = self.get_queryset().filter(status=MDTNote.Status.PENDING_APPROVAL).count() context['finalized_notes'] = self.get_queryset().filter(status=MDTNote.Status.FINALIZED).count() return context class MDTNoteDetailView(LoginRequiredMixin, TenantFilterMixin, DetailView): """Detail view for MDT notes.""" model = MDTNote template_name = 'mdt/mdt_note_detail.html' context_object_name = 'mdt_note' def get_context_data(self, **kwargs): """Add contributions, approvals, and forms to context.""" context = super().get_context_data(**kwargs) # Get contributions ordered by creation context['contributions'] = self.object.contributions.select_related( 'contributor', 'clinic' ).prefetch_related('mentioned_users').order_by('created_at') # Get approvals context['approvals'] = self.object.approvals.select_related( 'approver', 'clinic' ).order_by('-approved_at') # Get attachments context['attachments'] = self.object.attachments.select_related('uploaded_by').order_by('-created_at') # Add forms if note is editable if self.object.is_editable: context['contribution_form'] = MDTContributionForm( user=self.request.user, tenant=self.request.user.tenant, mdt_note=self.object ) context['attachment_form'] = MDTAttachmentForm(user=self.request.user) # Add approval form if user is senior and hasn't approved yet if self.request.user.role in ['DOCTOR', 'ADMIN']: # Senior roles existing_approval = self.object.approvals.filter(approver=self.request.user).first() if not existing_approval: context['approval_form'] = MDTApprovalForm( user=self.request.user, tenant=self.request.user.tenant ) else: context['user_approval'] = existing_approval # Check if user can finalize context['can_finalize'] = self.object.can_finalize and self.object.status != MDTNote.Status.FINALIZED return context class MDTNoteCreateView(LoginRequiredMixin, TenantFilterMixin, CreateView): """Create view for MDT notes.""" model = MDTNote form_class = MDTNoteForm template_name = 'mdt/mdt_note_form.html' success_url = reverse_lazy('mdt:mdt_note_list') def get_form_kwargs(self): """Add user and tenant to form kwargs.""" kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user kwargs['tenant'] = self.request.user.tenant return kwargs def form_valid(self, form): """Set tenant and initiated_by before saving.""" form.instance.tenant = self.request.user.tenant form.instance.initiated_by = self.request.user response = super().form_valid(form) messages.success( self.request, _('MDT note created successfully. You can now add contributions.') ) return redirect('mdt:mdt_note_detail', pk=self.object.pk) class MDTNoteUpdateView(LoginRequiredMixin, TenantFilterMixin, UpdateView): """Update view for MDT notes.""" model = MDTNote form_class = MDTNoteForm template_name = 'mdt/mdt_note_form.html' def get_form_kwargs(self): """Add user and tenant to form kwargs.""" kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user kwargs['tenant'] = self.request.user.tenant return kwargs def form_valid(self, form): """Save and redirect to detail view.""" response = super().form_valid(form) messages.success( self.request, _('MDT note updated successfully.') ) return redirect('mdt:mdt_note_detail', pk=self.object.pk) def get_queryset(self): """Only allow editing if not finalized.""" queryset = super().get_queryset() return queryset.exclude(status=MDTNote.Status.FINALIZED) class MDTContributionCreateView(LoginRequiredMixin, CreateView): """Create view for MDT contributions.""" model = MDTContribution form_class = MDTContributionForm template_name = 'mdt/mdt_contribution_form.html' def dispatch(self, request, *args, **kwargs): """Get MDT note and check permissions.""" self.mdt_note = get_object_or_404(MDTNote, pk=kwargs['mdt_note_pk']) # Check if note is editable if not self.mdt_note.is_editable: messages.error(request, _('This MDT note is finalized and cannot be edited.')) return redirect('mdt:mdt_note_detail', pk=self.mdt_note.pk) return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self): """Add user, tenant, and mdt_note to form kwargs.""" kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user kwargs['tenant'] = self.request.user.tenant kwargs['mdt_note'] = self.mdt_note return kwargs def get_success_url(self): """Return URL to redirect to after successful form submission.""" return reverse_lazy('mdt:mdt_note_detail', kwargs={'pk': self.mdt_note.pk}) def form_valid(self, form): """Set contributor and mdt_note before saving.""" form.instance.mdt_note = self.mdt_note form.instance.contributor = self.request.user # Save the form self.object = form.save() # Handle mentions mention_users = form.cleaned_data.get('mention_users', []) if mention_users: for user in mention_users: # Create mention record MDTMention.objects.create( contribution=self.object, mentioned_user=user ) # Create notification Notification.objects.create( user=user, title=_("You were mentioned in an MDT note"), message=_( f"{self.request.user.get_full_name()} mentioned you in MDT note: " f"{self.mdt_note.title} for patient {self.mdt_note.patient}" ), notification_type=Notification.NotificationType.INFO, related_object_type='mdt_note', related_object_id=self.mdt_note.id, action_url=f'/mdt/notes/{self.mdt_note.pk}/' ) messages.success( self.request, _('Your contribution has been added successfully.') ) return redirect('mdt:mdt_note_detail', pk=self.mdt_note.pk) def get_context_data(self, **kwargs): """Add MDT note to context.""" context = super().get_context_data(**kwargs) context['mdt_note'] = self.mdt_note return context class MDTApprovalCreateView(LoginRequiredMixin, CreateView): """Create view for MDT approvals.""" model = MDTApproval form_class = MDTApprovalForm template_name = 'mdt/mdt_approval_form.html' def dispatch(self, request, *args, **kwargs): """Get MDT note and check permissions.""" self.mdt_note = get_object_or_404(MDTNote, pk=kwargs['mdt_note_pk']) # Check if user is senior if request.user.role not in ['DOCTOR', 'ADMIN']: messages.error(request, _('Only senior therapists can approve MDT notes.')) return redirect('mdt:mdt_note_detail', pk=self.mdt_note.pk) # Check if user already approved if self.mdt_note.approvals.filter(approver=request.user).exists(): messages.warning(request, _('You have already approved this MDT note.')) return redirect('mdt:mdt_note_detail', pk=self.mdt_note.pk) return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self): """Add user and tenant to form kwargs.""" kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user kwargs['tenant'] = self.request.user.tenant return kwargs def form_valid(self, form): """Set approver, mdt_note, and approve before saving.""" form.instance.mdt_note = self.mdt_note form.instance.approver = self.request.user form.instance.approved = True response = super().form_valid(form) # Call approve method to handle logic self.object.approve(form.cleaned_data.get('comments', '')) # Notify initiator Notification.objects.create( user=self.mdt_note.initiated_by, title=_("MDT Note Approved"), message=_( f"{self.request.user.get_full_name()} approved your MDT note: {self.mdt_note.title}" ), notification_type=Notification.NotificationType.SUCCESS, related_object_type='mdt_note', related_object_id=self.mdt_note.id, action_url=f'/mdt/notes/{self.mdt_note.pk}/' ) messages.success( self.request, _('MDT note approved successfully.') ) # Check if can be finalized if self.mdt_note.can_finalize: messages.info( self.request, _('This MDT note now has sufficient approvals and can be finalized.') ) return redirect('mdt:mdt_note_detail', pk=self.mdt_note.pk) def get_context_data(self, **kwargs): """Add MDT note to context.""" context = super().get_context_data(**kwargs) context['mdt_note'] = self.mdt_note return context class MDTAttachmentCreateView(LoginRequiredMixin, CreateView): """Create view for MDT attachments.""" model = MDTAttachment form_class = MDTAttachmentForm template_name = 'mdt/mdt_attachment_form.html' def dispatch(self, request, *args, **kwargs): """Get MDT note.""" self.mdt_note = get_object_or_404(MDTNote, pk=kwargs['mdt_note_pk']) return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self): """Add user to form kwargs.""" kwargs = super().get_form_kwargs() kwargs['user'] = self.request.user return kwargs def form_valid(self, form): """Set mdt_note and uploaded_by before saving.""" form.instance.mdt_note = self.mdt_note form.instance.uploaded_by = self.request.user response = super().form_valid(form) messages.success( self.request, _('Attachment uploaded successfully.') ) return redirect('mdt:mdt_note_detail', pk=self.mdt_note.pk) def get_context_data(self, **kwargs): """Add MDT note to context.""" context = super().get_context_data(**kwargs) context['mdt_note'] = self.mdt_note return context def finalize_mdt_note(request, pk): """View to finalize an MDT note.""" mdt_note = get_object_or_404(MDTNote, pk=pk, tenant=request.user.tenant) # Check permissions if request.user.role not in ['DOCTOR', 'ADMIN']: messages.error(request, _('Only senior therapists can finalize MDT notes.')) return redirect('mdt:mdt_note_detail', pk=pk) # Check if can be finalized if not mdt_note.can_finalize: messages.error( request, _('This MDT note cannot be finalized yet. It requires at least 2 approvals from different departments.') ) return redirect('mdt:mdt_note_detail', pk=pk) # Finalize try: mdt_note.finalize() # Notify all contributors for contribution in mdt_note.contributions.all(): Notification.objects.create( user=contribution.contributor, title=_("MDT Note Finalized"), message=_( f"MDT note '{mdt_note.title}' for patient {mdt_note.patient} has been finalized." ), notification_type=Notification.NotificationType.SUCCESS, related_object_type='mdt_note', related_object_id=mdt_note.id, action_url=f'/mdt/notes/{mdt_note.pk}/' ) messages.success(request, _('MDT note finalized successfully.')) except ValueError as e: messages.error(request, str(e)) return redirect('mdt:mdt_note_detail', pk=pk) def my_mdt_notes(request): """View for user's MDT notes (contributed or initiated).""" from django.shortcuts import render # Get notes where user is contributor or initiator my_notes = MDTNote.objects.filter( Q(contributors=request.user) | Q(initiated_by=request.user), tenant=request.user.tenant ).distinct().select_related('patient', 'initiated_by').prefetch_related('contributions', 'approvals') # Get pending mentions pending_mentions = MDTMention.objects.filter( mentioned_user=request.user, viewed_at__isnull=True ).select_related('contribution__mdt_note').order_by('-created_at') context = { 'my_notes': my_notes, 'pending_mentions': pending_mentions, 'draft_count': my_notes.filter(status=MDTNote.Status.DRAFT).count(), 'pending_count': my_notes.filter(status=MDTNote.Status.PENDING_APPROVAL).count(), 'finalized_count': my_notes.filter(status=MDTNote.Status.FINALIZED).count(), } return render(request, 'mdt/my_mdt_notes.html', context)