437 lines
16 KiB
Python
437 lines
16 KiB
Python
"""
|
|
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)
|