agdar/mdt/views.py
Marwan Alwali 38d22db6f4 update
2025-12-11 10:43:43 +03:00

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)