325 lines
10 KiB
Python
325 lines
10 KiB
Python
from django.shortcuts import render, redirect, get_object_or_404
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib import messages
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.core.paginator import Paginator
|
|
from django.db.models import Q
|
|
from django.utils import timezone
|
|
from .models import DocumentTemplate, ClinicalNote, NoteAddendum, NoteAuditLog
|
|
from .forms import DocumentTemplateForm, ClinicalNoteForm, NoteAddendumForm, NoteSearchForm
|
|
|
|
|
|
# Template Views
|
|
@login_required
|
|
def template_list(request):
|
|
"""List all document templates."""
|
|
templates = DocumentTemplate.objects.all()
|
|
|
|
# Filter by category if provided
|
|
category = request.GET.get('category')
|
|
if category:
|
|
templates = templates.filter(category=category)
|
|
|
|
# Filter by active status
|
|
is_active = request.GET.get('is_active')
|
|
if is_active:
|
|
templates = templates.filter(is_active=is_active == 'true')
|
|
|
|
paginator = Paginator(templates, 20)
|
|
page_number = request.GET.get('page')
|
|
templates = paginator.get_page(page_number)
|
|
|
|
context = {
|
|
'templates': templates,
|
|
'categories': DocumentTemplate.CATEGORY_CHOICES,
|
|
}
|
|
return render(request, 'documents/template_list.html', context)
|
|
|
|
|
|
@login_required
|
|
def template_detail(request, pk):
|
|
"""View template details."""
|
|
template = get_object_or_404(DocumentTemplate, pk=pk)
|
|
context = {
|
|
'template': template,
|
|
}
|
|
return render(request, 'documents/template_detail.html', context)
|
|
|
|
|
|
@login_required
|
|
def template_create(request):
|
|
"""Create a new document template."""
|
|
if request.method == 'POST':
|
|
form = DocumentTemplateForm(request.POST)
|
|
if form.is_valid():
|
|
template = form.save(commit=False)
|
|
template.created_by = request.user
|
|
template.save()
|
|
messages.success(request, _('Template created successfully.'))
|
|
return redirect('documents:template-detail', pk=template.pk)
|
|
else:
|
|
form = DocumentTemplateForm()
|
|
|
|
context = {
|
|
'form': form,
|
|
'title': _('Create Template'),
|
|
}
|
|
return render(request, 'documents/template_form.html', context)
|
|
|
|
|
|
@login_required
|
|
def template_update(request, pk):
|
|
"""Update an existing document template."""
|
|
template = get_object_or_404(DocumentTemplate, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
form = DocumentTemplateForm(request.POST, instance=template)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, _('Template updated successfully.'))
|
|
return redirect('documents:template-detail', pk=template.pk)
|
|
else:
|
|
form = DocumentTemplateForm(instance=template)
|
|
|
|
context = {
|
|
'form': form,
|
|
'template': template,
|
|
'title': _('Update Template'),
|
|
}
|
|
return render(request, 'documents/template_form.html', context)
|
|
|
|
|
|
@login_required
|
|
def template_delete(request, pk):
|
|
"""Delete a document template."""
|
|
template = get_object_or_404(DocumentTemplate, pk=pk)
|
|
|
|
if request.method == 'POST':
|
|
template.delete()
|
|
messages.success(request, _('Template deleted successfully.'))
|
|
return redirect('documents:template-list')
|
|
|
|
context = {
|
|
'template': template,
|
|
}
|
|
return render(request, 'documents/template_confirm_delete.html', context)
|
|
|
|
|
|
# Clinical Note Views
|
|
@login_required
|
|
def note_list(request):
|
|
"""List all clinical notes with search and filtering."""
|
|
notes = ClinicalNote.objects.select_related('patient', 'author', 'template')
|
|
|
|
# Apply search form filters
|
|
form = NoteSearchForm(request.GET)
|
|
if form.is_valid():
|
|
if form.cleaned_data.get('patient'):
|
|
patient_query = form.cleaned_data['patient']
|
|
notes = notes.filter(
|
|
Q(patient__first_name__icontains=patient_query) |
|
|
Q(patient__last_name__icontains=patient_query) |
|
|
Q(patient__patient_id__icontains=patient_query)
|
|
)
|
|
|
|
if form.cleaned_data.get('status'):
|
|
notes = notes.filter(status=form.cleaned_data['status'])
|
|
|
|
if form.cleaned_data.get('date_from'):
|
|
notes = notes.filter(created_at__gte=form.cleaned_data['date_from'])
|
|
|
|
if form.cleaned_data.get('date_to'):
|
|
notes = notes.filter(created_at__lte=form.cleaned_data['date_to'])
|
|
|
|
if form.cleaned_data.get('author'):
|
|
author_query = form.cleaned_data['author']
|
|
notes = notes.filter(
|
|
Q(author__first_name__icontains=author_query) |
|
|
Q(author__last_name__icontains=author_query)
|
|
)
|
|
|
|
paginator = Paginator(notes, 20)
|
|
page_number = request.GET.get('page')
|
|
notes = paginator.get_page(page_number)
|
|
|
|
context = {
|
|
'notes': notes,
|
|
'search_form': form,
|
|
}
|
|
return render(request, 'documents/note_list.html', context)
|
|
|
|
|
|
@login_required
|
|
def note_detail(request, pk):
|
|
"""View clinical note details."""
|
|
note = get_object_or_404(
|
|
ClinicalNote.objects.select_related('patient', 'author', 'template', 'finalized_by'),
|
|
pk=pk
|
|
)
|
|
|
|
# Log the view action
|
|
NoteAuditLog.objects.create(
|
|
note=note,
|
|
action='viewed',
|
|
user=request.user,
|
|
ip_address=request.META.get('REMOTE_ADDR')
|
|
)
|
|
|
|
addendums = note.addendums.select_related('author').all()
|
|
|
|
context = {
|
|
'note': note,
|
|
'addendums': addendums,
|
|
}
|
|
return render(request, 'documents/note_detail.html', context)
|
|
|
|
|
|
@login_required
|
|
def note_create(request):
|
|
"""Create a new clinical note."""
|
|
if request.method == 'POST':
|
|
form = ClinicalNoteForm(request.POST, user=request.user)
|
|
if form.is_valid():
|
|
note = form.save()
|
|
|
|
# Log the creation
|
|
NoteAuditLog.objects.create(
|
|
note=note,
|
|
action='created',
|
|
user=request.user,
|
|
ip_address=request.META.get('REMOTE_ADDR')
|
|
)
|
|
|
|
messages.success(request, _('Clinical note created successfully.'))
|
|
return redirect('documents:note-detail', pk=note.pk)
|
|
else:
|
|
form = ClinicalNoteForm(user=request.user)
|
|
|
|
context = {
|
|
'form': form,
|
|
'title': _('Create Clinical Note'),
|
|
}
|
|
return render(request, 'documents/note_form.html', context)
|
|
|
|
|
|
@login_required
|
|
def note_update(request, pk):
|
|
"""Update an existing clinical note."""
|
|
note = get_object_or_404(ClinicalNote, pk=pk)
|
|
|
|
# Only allow editing of draft notes
|
|
if note.status != 'draft':
|
|
messages.error(request, _('Only draft notes can be edited. Use addendum for finalized notes.'))
|
|
return redirect('documents:note-detail', pk=note.pk)
|
|
|
|
if request.method == 'POST':
|
|
form = ClinicalNoteForm(request.POST, instance=note, user=request.user)
|
|
if form.is_valid():
|
|
old_content = note.content
|
|
note = form.save()
|
|
|
|
# Log the update
|
|
NoteAuditLog.objects.create(
|
|
note=note,
|
|
action='updated',
|
|
user=request.user,
|
|
ip_address=request.META.get('REMOTE_ADDR'),
|
|
changes={'old_content': old_content, 'new_content': note.content}
|
|
)
|
|
|
|
messages.success(request, _('Clinical note updated successfully.'))
|
|
return redirect('documents:note-detail', pk=note.pk)
|
|
else:
|
|
form = ClinicalNoteForm(instance=note, user=request.user)
|
|
|
|
context = {
|
|
'form': form,
|
|
'note': note,
|
|
'title': _('Update Clinical Note'),
|
|
}
|
|
return render(request, 'documents/note_form.html', context)
|
|
|
|
|
|
@login_required
|
|
def note_finalize(request, pk):
|
|
"""Finalize a clinical note."""
|
|
note = get_object_or_404(ClinicalNote, pk=pk)
|
|
|
|
if note.status != 'draft':
|
|
messages.error(request, _('Only draft notes can be finalized.'))
|
|
return redirect('documents:note-detail', pk=note.pk)
|
|
|
|
if request.method == 'POST':
|
|
note.status = 'final'
|
|
note.finalized_at = timezone.now()
|
|
note.finalized_by = request.user
|
|
note.save()
|
|
|
|
# Log the finalization
|
|
NoteAuditLog.objects.create(
|
|
note=note,
|
|
action='finalized',
|
|
user=request.user,
|
|
ip_address=request.META.get('REMOTE_ADDR')
|
|
)
|
|
|
|
messages.success(request, _('Clinical note finalized successfully.'))
|
|
return redirect('documents:note-detail', pk=note.pk)
|
|
|
|
context = {
|
|
'note': note,
|
|
}
|
|
return render(request, 'documents/note_confirm_finalize.html', context)
|
|
|
|
|
|
@login_required
|
|
def note_addendum(request, pk):
|
|
"""Add an addendum to a finalized clinical note."""
|
|
note = get_object_or_404(ClinicalNote, pk=pk)
|
|
|
|
if note.status != 'final':
|
|
messages.error(request, _('Addendums can only be added to finalized notes.'))
|
|
return redirect('documents:note-detail', pk=note.pk)
|
|
|
|
if request.method == 'POST':
|
|
form = NoteAddendumForm(request.POST, user=request.user, note=note)
|
|
if form.is_valid():
|
|
addendum = form.save()
|
|
|
|
# Update note status to amended
|
|
note.status = 'amended'
|
|
note.save()
|
|
|
|
# Log the addendum
|
|
NoteAuditLog.objects.create(
|
|
note=note,
|
|
action='amended',
|
|
user=request.user,
|
|
ip_address=request.META.get('REMOTE_ADDR'),
|
|
changes={'addendum_id': addendum.id, 'reason': addendum.reason}
|
|
)
|
|
|
|
messages.success(request, _('Addendum added successfully.'))
|
|
return redirect('documents:note-detail', pk=note.pk)
|
|
else:
|
|
form = NoteAddendumForm(user=request.user, note=note)
|
|
|
|
context = {
|
|
'form': form,
|
|
'note': note,
|
|
}
|
|
return render(request, 'documents/note_addendum.html', context)
|
|
|
|
|
|
@login_required
|
|
def note_audit(request, pk):
|
|
"""View audit trail for a clinical note."""
|
|
note = get_object_or_404(ClinicalNote, pk=pk)
|
|
audit_logs = note.audit_logs.select_related('user').all()
|
|
|
|
context = {
|
|
'note': note,
|
|
'audit_logs': audit_logs,
|
|
}
|
|
return render(request, 'documents/note_audit.html', context)
|