3115 lines
110 KiB
Python
3115 lines
110 KiB
Python
"""
|
|
Patients app views for hospital management system with comprehensive CRUD operations.
|
|
"""
|
|
|
|
from django.shortcuts import render, get_object_or_404, redirect
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
|
from django.views.generic import (
|
|
TemplateView, ListView, DetailView, CreateView, UpdateView, DeleteView
|
|
)
|
|
from django.http import JsonResponse
|
|
from django.contrib import messages
|
|
from django.db.models import Q, Count
|
|
from django.utils import timezone
|
|
from django.urls import reverse_lazy, reverse
|
|
from django.core.paginator import Paginator
|
|
from datetime import timedelta, date
|
|
|
|
from appointments.models import AppointmentRequest
|
|
from .models import (
|
|
PatientProfile, EmergencyContact, InsuranceInfo,
|
|
ConsentTemplate, ConsentForm, PatientNote
|
|
)
|
|
from .forms import (
|
|
PatientProfileForm, EmergencyContactForm, InsuranceInfoForm,
|
|
ConsentTemplateForm, ConsentFormForm, PatientNoteForm, PatientsSearchForm
|
|
)
|
|
from core.utils import AuditLogger
|
|
|
|
|
|
# ============================================================================
|
|
# PATIENT PROFILE VIEWS (FULL CRUD - Master Data)
|
|
# ============================================================================
|
|
|
|
class PatientListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
Patient listing view.
|
|
"""
|
|
model = PatientProfile
|
|
template_name = 'patients/patient_list.html'
|
|
context_object_name = 'patients'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return PatientProfile.objects.none()
|
|
|
|
queryset = PatientProfile.objects.filter(tenant=tenant)
|
|
|
|
# Apply filters
|
|
gender = self.request.GET.get('gender')
|
|
if gender:
|
|
queryset = queryset.filter(gender=gender)
|
|
|
|
age_range = self.request.GET.get('age_range')
|
|
if age_range:
|
|
today = timezone.now().date()
|
|
if age_range == 'pediatric':
|
|
# Under 18
|
|
min_birth_date = today.replace(year=today.year - 18)
|
|
queryset = queryset.filter(date_of_birth__gt=min_birth_date)
|
|
elif age_range == 'adult':
|
|
# 18-64
|
|
min_birth_date = today.replace(year=today.year - 65)
|
|
max_birth_date = today.replace(year=today.year - 18)
|
|
queryset = queryset.filter(
|
|
date_of_birth__gt=min_birth_date,
|
|
date_of_birth__lte=max_birth_date
|
|
)
|
|
elif age_range == 'geriatric':
|
|
# 65+
|
|
max_birth_date = today.replace(year=today.year - 65)
|
|
queryset = queryset.filter(date_of_birth__lte=max_birth_date)
|
|
|
|
blood_type = self.request.GET.get('blood_type')
|
|
if blood_type:
|
|
queryset = queryset.filter(blood_type=blood_type)
|
|
|
|
status = self.request.GET.get('status')
|
|
if status == 'active':
|
|
queryset = queryset.filter(is_active=True)
|
|
elif status == 'inactive':
|
|
queryset = queryset.filter(is_active=False)
|
|
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(patient_id__icontains=search) |
|
|
Q(first_name__icontains=search) |
|
|
Q(last_name__icontains=search) |
|
|
Q(email__icontains=search) |
|
|
Q(phone_number__icontains=search) |
|
|
Q(mobile_number__icontains=search)
|
|
)
|
|
|
|
return queryset.order_by('last_name', 'first_name')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
|
|
if tenant:
|
|
context.update({
|
|
'search_form': PatientsSearchForm(self.request.GET),
|
|
'total_patients': PatientProfile.objects.filter(tenant=tenant).count(),
|
|
'active_patients': PatientProfile.objects.filter(tenant=tenant, is_active=True).count(),
|
|
})
|
|
|
|
return context
|
|
|
|
|
|
class PatientDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Patient detail view.
|
|
"""
|
|
model = PatientProfile
|
|
template_name = 'patients/patient_detail.html'
|
|
context_object_name = 'patient'
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return PatientProfile.objects.none()
|
|
return PatientProfile.objects.filter(tenant=tenant)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
patient = self.get_object()
|
|
|
|
# Get related data
|
|
context['emergency_contacts'] = EmergencyContact.objects.filter(
|
|
patient=patient
|
|
).order_by('-is_primary')
|
|
|
|
context['insurance_info'] = InsuranceInfo.objects.filter(
|
|
patient=patient
|
|
).order_by('-is_active', '-effective_date')
|
|
|
|
context['consent_forms'] = ConsentForm.objects.filter(
|
|
patient=patient
|
|
).order_by('-created_at')
|
|
|
|
context['patient_notes'] = PatientNote.objects.filter(
|
|
patient=patient
|
|
).order_by('-created_at')[:10]
|
|
|
|
# Calculate age
|
|
if patient.date_of_birth:
|
|
today = date.today()
|
|
age = today.year - patient.date_of_birth.year
|
|
if today.month < patient.date_of_birth.month or \
|
|
(today.month == patient.date_of_birth.month and today.day < patient.date_of_birth.day):
|
|
age -= 1
|
|
context['patient_age'] = age
|
|
|
|
return context
|
|
|
|
|
|
class PatientCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create new patient.
|
|
"""
|
|
model = PatientProfile
|
|
form_class = PatientProfileForm
|
|
template_name = 'patients/patient_registration.html'
|
|
permission_required = 'patients.add_patientprofile'
|
|
success_url = reverse_lazy('patients:patient_list')
|
|
|
|
def form_valid(self, form):
|
|
# Set tenant
|
|
form.instance.tenant = getattr(self.request, 'tenant', None)
|
|
response = super().form_valid(form)
|
|
|
|
# Log patient creation
|
|
AuditLogger.log_event(
|
|
tenant=form.instance.tenant,
|
|
event_type='CREATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Create Patient',
|
|
description=f'Created new patient: {self.object.first_name} {self.object.last_name}',
|
|
user=self.request.user,
|
|
content_object=self.object,
|
|
request=self.request
|
|
)
|
|
|
|
messages.success(self.request, f'Patient "{self.object.first_name} {self.object.last_name}" created successfully.')
|
|
return response
|
|
|
|
|
|
class PatientUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
"""
|
|
Update patient information.
|
|
"""
|
|
model = PatientProfile
|
|
form_class = PatientProfileForm
|
|
template_name = 'patients/profiles/patient_form.html'
|
|
permission_required = 'patients.change_patientprofile'
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return PatientProfile.objects.none()
|
|
return PatientProfile.objects.filter(tenant=tenant)
|
|
|
|
def get_success_url(self):
|
|
return reverse('patients:patient_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log patient update
|
|
AuditLogger.log_event(
|
|
tenant=self.object.tenant,
|
|
event_type='UPDATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Update Patient',
|
|
description=f'Updated patient: {self.object.first_name} {self.object.last_name}',
|
|
user=self.request.user,
|
|
content_object=self.object,
|
|
request=self.request
|
|
)
|
|
|
|
messages.success(self.request, f'Patient "{self.object.first_name} {self.object.last_name}" updated successfully.')
|
|
return response
|
|
|
|
|
|
class PatientDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
"""
|
|
Delete patient (soft delete to inactive).
|
|
"""
|
|
model = PatientProfile
|
|
template_name = 'patients/profiles/patient_confirm_delete.html'
|
|
permission_required = 'patients.delete_patientprofile'
|
|
success_url = reverse_lazy('patients:patient_list')
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return PatientProfile.objects.none()
|
|
return PatientProfile.objects.filter(tenant=tenant)
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
|
|
# Soft delete - set to inactive
|
|
self.object.is_active = False
|
|
self.object.save()
|
|
|
|
# Log patient deletion
|
|
AuditLogger.log_event(
|
|
tenant=self.object.tenant,
|
|
event_type='DELETE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Deactivate Patient',
|
|
description=f'Deactivated patient: {self.object.first_name} {self.object.last_name}',
|
|
user=request.user,
|
|
content_object=self.object,
|
|
request=request
|
|
)
|
|
|
|
messages.success(request, f'Patient "{self.object.first_name} {self.object.last_name}" deactivated successfully.')
|
|
return redirect(self.success_url)
|
|
|
|
|
|
# ============================================================================
|
|
# EMERGENCY CONTACT VIEWS (FULL CRUD - Operational Data)
|
|
# ============================================================================
|
|
|
|
class EmergencyContactListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List emergency contacts.
|
|
"""
|
|
model = EmergencyContact
|
|
template_name = 'patients/emergency_contacts/emergency_contact_list.html'
|
|
context_object_name = 'emergency_contacts'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return EmergencyContact.objects.none()
|
|
|
|
queryset = EmergencyContact.objects.filter(patient__tenant=tenant)
|
|
|
|
# Apply filters
|
|
patient_id = self.request.GET.get('patient_id')
|
|
if patient_id:
|
|
queryset = queryset.filter(patient_id=patient_id)
|
|
|
|
relationship = self.request.GET.get('relationship')
|
|
if relationship:
|
|
queryset = queryset.filter(relationship__icontains=relationship)
|
|
|
|
is_primary = self.request.GET.get('is_primary')
|
|
if is_primary == 'true':
|
|
queryset = queryset.filter(is_primary=True)
|
|
elif is_primary == 'false':
|
|
queryset = queryset.filter(is_primary=False)
|
|
|
|
return queryset.order_by('patient__last_name', '-is_primary', 'name')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
|
|
if tenant:
|
|
context.update({
|
|
'patients': PatientProfile.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'),
|
|
})
|
|
|
|
return context
|
|
|
|
|
|
class EmergencyContactDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Display emergency contact details.
|
|
"""
|
|
model = EmergencyContact
|
|
template_name = 'patients/emergency_contacts/emergency_contact_detail.html'
|
|
context_object_name = 'emergency_contact'
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return EmergencyContact.objects.none()
|
|
return EmergencyContact.objects.filter(patient__tenant=tenant)
|
|
|
|
|
|
class EmergencyContactCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create new emergency contact.
|
|
"""
|
|
model = EmergencyContact
|
|
form_class = EmergencyContactForm
|
|
template_name = 'patients/emergency_contacts/emergency_contact_form.html'
|
|
permission_required = 'patients.add_emergencycontact'
|
|
success_url = reverse_lazy('patients:emergency_contact_list')
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log emergency contact creation
|
|
AuditLogger.log_event(
|
|
tenant=getattr(self.request, 'tenant', None),
|
|
event_type='CREATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Create Emergency Contact',
|
|
description=f'Created emergency contact: {self.object.name} for {self.object.patient}',
|
|
user=self.request.user,
|
|
content_object=self.object,
|
|
request=self.request
|
|
)
|
|
|
|
messages.success(self.request, f'Emergency contact "{self.object.name}" created successfully.')
|
|
return response
|
|
|
|
|
|
class EmergencyContactUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
"""
|
|
Update emergency contact.
|
|
"""
|
|
model = EmergencyContact
|
|
form_class = EmergencyContactForm
|
|
template_name = 'patients/emergency_contacts/emergency_contact_form.html'
|
|
permission_required = 'patients.change_emergencycontact'
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return EmergencyContact.objects.none()
|
|
return EmergencyContact.objects.filter(patient__tenant=tenant)
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def get_success_url(self):
|
|
return reverse('patients:emergency_contact_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log emergency contact update
|
|
AuditLogger.log_event(
|
|
tenant=getattr(self.request, 'tenant', None),
|
|
event_type='UPDATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Update Emergency Contact',
|
|
description=f'Updated emergency contact: {self.object.name}',
|
|
user=self.request.user,
|
|
content_object=self.object,
|
|
request=self.request
|
|
)
|
|
|
|
messages.success(self.request, f'Emergency contact "{self.object.name}" updated successfully.')
|
|
return response
|
|
|
|
|
|
class EmergencyContactDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
"""
|
|
Delete emergency contact.
|
|
"""
|
|
model = EmergencyContact
|
|
template_name = 'patients/emergency_contacts/emergency_contact_confirm_delete.html'
|
|
permission_required = 'patients.delete_emergencycontact'
|
|
success_url = reverse_lazy('patients:emergency_contact_list')
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return EmergencyContact.objects.none()
|
|
return EmergencyContact.objects.filter(patient__tenant=tenant)
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
contact_name = self.object.name
|
|
|
|
# Log emergency contact deletion
|
|
AuditLogger.log_event(
|
|
tenant=getattr(request, 'tenant', None),
|
|
event_type='DELETE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Delete Emergency Contact',
|
|
description=f'Deleted emergency contact: {contact_name}',
|
|
user=request.user,
|
|
content_object=self.object,
|
|
request=request
|
|
)
|
|
|
|
messages.success(request, f'Emergency contact "{contact_name}" deleted successfully.')
|
|
return super().delete(request, *args, **kwargs)
|
|
|
|
|
|
# ============================================================================
|
|
# INSURANCE INFO VIEWS (FULL CRUD - Operational Data)
|
|
# ============================================================================
|
|
|
|
class InsuranceInfoListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List insurance information.
|
|
"""
|
|
model = InsuranceInfo
|
|
template_name = 'patients/insurance/insurance_list.html'
|
|
context_object_name = 'insurance_info'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return InsuranceInfo.objects.none()
|
|
|
|
queryset = InsuranceInfo.objects.filter(patient__tenant=tenant)
|
|
|
|
# Apply filters
|
|
patient_id = self.request.GET.get('patient_id')
|
|
if patient_id:
|
|
queryset = queryset.filter(patient_id=patient_id)
|
|
|
|
insurance_company = self.request.GET.get('insurance_company')
|
|
if insurance_company:
|
|
queryset = queryset.filter(insurance_company__icontains=insurance_company)
|
|
|
|
is_primary = self.request.GET.get('is_primary')
|
|
if is_primary == 'true':
|
|
queryset = queryset.filter(is_primary=True)
|
|
elif is_primary == 'false':
|
|
queryset = queryset.filter(is_primary=False)
|
|
|
|
# Filter by expiry status
|
|
expiry_status = self.request.GET.get('expiry_status')
|
|
today = timezone.now().date()
|
|
if expiry_status == 'expired':
|
|
queryset = queryset.filter(expiry_date__lt=today)
|
|
elif expiry_status == 'expiring_soon':
|
|
# Expiring within 30 days
|
|
expiry_threshold = today + timedelta(days=30)
|
|
queryset = queryset.filter(expiry_date__lte=expiry_threshold, expiry_date__gte=today)
|
|
|
|
return queryset.order_by('patient__last_name', '-is_primary', '-effective_date')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
|
|
if tenant:
|
|
context.update({
|
|
'patients': PatientProfile.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'),
|
|
})
|
|
|
|
return context
|
|
|
|
|
|
class InsuranceInfoDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Display insurance information details.
|
|
"""
|
|
model = InsuranceInfo
|
|
template_name = 'patients/insurance/insurance_detail.html'
|
|
context_object_name = 'insurance_info'
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return InsuranceInfo.objects.none()
|
|
return InsuranceInfo.objects.filter(patient__tenant=tenant)
|
|
|
|
|
|
class InsuranceInfoCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create new insurance information.
|
|
"""
|
|
model = InsuranceInfo
|
|
form_class = InsuranceInfoForm
|
|
template_name = 'patients/insurance/insurance_form.html'
|
|
permission_required = 'patients.add_insuranceinfo'
|
|
success_url = reverse_lazy('patients:insurance_info_list')
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log insurance info creation
|
|
AuditLogger.log_event(
|
|
tenant=getattr(self.request, 'tenant', None),
|
|
event_type='CREATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Create Insurance Info',
|
|
description=f'Created insurance info: {self.object.insurance_company} for {self.object.patient}',
|
|
user=self.request.user,
|
|
content_object=self.object,
|
|
request=self.request
|
|
)
|
|
|
|
messages.success(self.request, f'Insurance information for "{self.object.insurance_company}" created successfully.')
|
|
return response
|
|
|
|
|
|
class InsuranceInfoUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
"""
|
|
Update insurance information.
|
|
"""
|
|
model = InsuranceInfo
|
|
form_class = InsuranceInfoForm
|
|
template_name = 'patients/insurance/insurance_form.html'
|
|
permission_required = 'patients.change_insuranceinfo'
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return InsuranceInfo.objects.none()
|
|
return InsuranceInfo.objects.filter(patient__tenant=tenant)
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def get_success_url(self):
|
|
return reverse('patients:insurance_info_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log insurance info update
|
|
AuditLogger.log_event(
|
|
tenant=getattr(self.request, 'tenant', None),
|
|
event_type='UPDATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Update Insurance Info',
|
|
description=f'Updated insurance info: {self.object.insurance_company}',
|
|
user=self.request.user,
|
|
content_object=self.object,
|
|
request=self.request
|
|
)
|
|
|
|
messages.success(self.request, f'Insurance information for "{self.object.insurance_company}" updated successfully.')
|
|
return response
|
|
|
|
|
|
class InsuranceInfoDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
"""
|
|
Delete insurance information.
|
|
"""
|
|
model = InsuranceInfo
|
|
template_name = 'patients/insurance/insurance_confirm_delete.html'
|
|
permission_required = 'patients.delete_insuranceinfo'
|
|
success_url = reverse_lazy('patients:insurance_info_list')
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return InsuranceInfo.objects.none()
|
|
return InsuranceInfo.objects.filter(patient__tenant=tenant)
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
insurance_company = self.object.insurance_company
|
|
|
|
# Log insurance info deletion
|
|
AuditLogger.log_event(
|
|
tenant=getattr(request, 'tenant', None),
|
|
event_type='DELETE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Delete Insurance Info',
|
|
description=f'Deleted insurance info: {insurance_company}',
|
|
user=request.user,
|
|
content_object=self.object,
|
|
request=request
|
|
)
|
|
|
|
messages.success(request, f'Insurance information for "{insurance_company}" deleted successfully.')
|
|
return super().delete(request, *args, **kwargs)
|
|
|
|
|
|
# ============================================================================
|
|
# CONSENT TEMPLATE VIEWS (FULL CRUD - Master Data)
|
|
# ============================================================================
|
|
|
|
class ConsentTemplateListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List consent templates.
|
|
"""
|
|
model = ConsentTemplate
|
|
template_name = 'patients/consent_template_list.html'
|
|
context_object_name = 'consent_templates'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return ConsentTemplate.objects.none()
|
|
|
|
queryset = ConsentTemplate.objects.filter(tenant=tenant)
|
|
|
|
# Apply filters
|
|
template_type = self.request.GET.get('template_type')
|
|
if template_type:
|
|
queryset = queryset.filter(template_type=template_type)
|
|
|
|
status = self.request.GET.get('status')
|
|
if status == 'active':
|
|
queryset = queryset.filter(is_active=True)
|
|
elif status == 'inactive':
|
|
queryset = queryset.filter(is_active=False)
|
|
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(name__icontains=search) |
|
|
Q(description__icontains=search)
|
|
)
|
|
|
|
return queryset.order_by('template_type', 'name')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
|
|
if tenant:
|
|
context.update({
|
|
'template_types': ConsentTemplate.objects.filter(
|
|
tenant=tenant
|
|
).values_list('template_type', flat=True).distinct(),
|
|
})
|
|
|
|
return context
|
|
|
|
|
|
class ConsentTemplateDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Display consent template details.
|
|
"""
|
|
model = ConsentTemplate
|
|
template_name = 'patients/consent_template_detail.html'
|
|
context_object_name = 'consent_template'
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return ConsentTemplate.objects.none()
|
|
return ConsentTemplate.objects.filter(tenant=tenant)
|
|
|
|
|
|
class ConsentTemplateCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create new consent template.
|
|
"""
|
|
model = ConsentTemplate
|
|
form_class = ConsentTemplateForm
|
|
template_name = 'patients/consent_template_form.html'
|
|
permission_required = 'patients.add_consenttemplate'
|
|
success_url = reverse_lazy('patients:consent_template_list')
|
|
|
|
def form_valid(self, form):
|
|
# Set tenant
|
|
form.instance.tenant = getattr(self.request, 'tenant', None)
|
|
response = super().form_valid(form)
|
|
|
|
# Log consent template creation
|
|
AuditLogger.log_event(
|
|
tenant=form.instance.tenant,
|
|
event_type='CREATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Create Consent Template',
|
|
description=f'Created consent template: {self.object.name}',
|
|
user=self.request.user,
|
|
content_object=self.object,
|
|
request=self.request
|
|
)
|
|
|
|
messages.success(self.request, f'Consent template "{self.object.name}" created successfully.')
|
|
return response
|
|
|
|
|
|
class ConsentTemplateUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
"""
|
|
Update consent template.
|
|
"""
|
|
model = ConsentTemplate
|
|
form_class = ConsentTemplateForm
|
|
template_name = 'patients/consent_template_form.html'
|
|
permission_required = 'patients.change_consenttemplate'
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return ConsentTemplate.objects.none()
|
|
return ConsentTemplate.objects.filter(tenant=tenant)
|
|
|
|
def get_success_url(self):
|
|
return reverse('patients:consent_template_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log consent template update
|
|
AuditLogger.log_event(
|
|
tenant=self.object.tenant,
|
|
event_type='UPDATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Update Consent Template',
|
|
description=f'Updated consent template: {self.object.name}',
|
|
user=self.request.user,
|
|
content_object=self.object,
|
|
request=self.request
|
|
)
|
|
|
|
messages.success(self.request, f'Consent template "{self.object.name}" updated successfully.')
|
|
return response
|
|
|
|
|
|
class ConsentTemplateDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
"""
|
|
Delete consent template.
|
|
"""
|
|
model = ConsentTemplate
|
|
template_name = 'patients/consent_template_confirm_delete.html'
|
|
permission_required = 'patients.delete_consenttemplate'
|
|
success_url = reverse_lazy('patients:consent_template_list')
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return ConsentTemplate.objects.none()
|
|
return ConsentTemplate.objects.filter(tenant=tenant)
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
template_name = self.object.name
|
|
|
|
# Check if template is being used
|
|
if ConsentForm.objects.filter(template=self.object).exists():
|
|
messages.error(request, f'Cannot delete consent template "{template_name}" as it is being used by consent forms.')
|
|
return redirect('patients:consent_template_detail', pk=self.object.pk)
|
|
|
|
# Log consent template deletion
|
|
AuditLogger.log_event(
|
|
tenant=getattr(request, 'tenant', None),
|
|
event_type='DELETE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Delete Consent Template',
|
|
description=f'Deleted consent template: {template_name}',
|
|
user=request.user,
|
|
content_object=self.object,
|
|
request=request
|
|
)
|
|
|
|
messages.success(request, f'Consent template "{template_name}" deleted successfully.')
|
|
return super().delete(request, *args, **kwargs)
|
|
|
|
|
|
# ============================================================================
|
|
# CONSENT FORM VIEWS (APPEND-ONLY - Clinical Records)
|
|
# ============================================================================
|
|
|
|
class ConsentFormListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List consent forms.
|
|
"""
|
|
model = ConsentForm
|
|
template_name = 'patients/consents/consent_form_list.html'
|
|
context_object_name = 'consent_forms'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return ConsentForm.objects.none()
|
|
|
|
queryset = ConsentForm.objects.filter(patient__tenant=tenant)
|
|
|
|
# Apply filters
|
|
patient_id = self.request.GET.get('patient_id')
|
|
if patient_id:
|
|
queryset = queryset.filter(patient_id=patient_id)
|
|
|
|
template_id = self.request.GET.get('template_id')
|
|
if template_id:
|
|
queryset = queryset.filter(template_id=template_id)
|
|
|
|
status = self.request.GET.get('status')
|
|
if status:
|
|
queryset = queryset.filter(status=status)
|
|
|
|
return queryset.order_by('-created_at')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
|
|
if tenant:
|
|
context.update({
|
|
'patients': PatientProfile.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'),
|
|
'templates': ConsentTemplate.objects.filter(tenant=tenant, is_active=True).order_by('name'),
|
|
'status_choices': ConsentForm.STATUS_CHOICES,
|
|
})
|
|
|
|
return context
|
|
|
|
|
|
class ConsentFormDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Display consent form details.
|
|
"""
|
|
model = ConsentForm
|
|
template_name = 'patients/consents/consent_form_detail.html'
|
|
context_object_name = 'consent_form'
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return ConsentForm.objects.none()
|
|
return ConsentForm.objects.filter(patient__tenant=tenant)
|
|
|
|
|
|
class ConsentFormCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create new consent form.
|
|
"""
|
|
model = ConsentForm
|
|
form_class = ConsentFormForm
|
|
template_name = 'patients/consents/consent_form_form.html'
|
|
permission_required = 'patients.add_consentform'
|
|
success_url = reverse_lazy('patients:consent_form_list')
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log consent form creation
|
|
AuditLogger.log_event(
|
|
tenant=getattr(self.request, 'tenant', None),
|
|
event_type='CREATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Create Consent Form',
|
|
description=f'Created consent form: {self.object.template.name} for {self.object.patient}',
|
|
user=self.request.user,
|
|
content_object=self.object,
|
|
request=self.request
|
|
)
|
|
|
|
messages.success(self.request, f'Consent form "{self.object.template.name}" created successfully.')
|
|
return response
|
|
|
|
|
|
# ============================================================================
|
|
# PATIENT NOTE VIEWS (APPEND-ONLY - Clinical Records)
|
|
# ============================================================================
|
|
|
|
class PatientNoteListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List patient notes.
|
|
"""
|
|
model = PatientNote
|
|
template_name = 'patients/notes/patient_note_list.html'
|
|
context_object_name = 'patient_notes'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return PatientNote.objects.none()
|
|
|
|
queryset = PatientNote.objects.filter(patient__tenant=tenant)
|
|
|
|
# Apply filters
|
|
patient_id = self.request.GET.get('patient_id')
|
|
if patient_id:
|
|
queryset = queryset.filter(patient_id=patient_id)
|
|
|
|
note_type = self.request.GET.get('note_type')
|
|
if note_type:
|
|
queryset = queryset.filter(note_type=note_type)
|
|
|
|
is_confidential = self.request.GET.get('is_confidential')
|
|
if is_confidential == 'true':
|
|
queryset = queryset.filter(is_confidential=True)
|
|
elif is_confidential == 'false':
|
|
queryset = queryset.filter(is_confidential=False)
|
|
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(subject__icontains=search) |
|
|
Q(content__icontains=search)
|
|
)
|
|
|
|
return queryset.order_by('-created_at')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
|
|
if tenant:
|
|
context.update({
|
|
'patients': PatientProfile.objects.filter(tenant=tenant, is_active=True).order_by('last_name', 'first_name'),
|
|
'note_types': PatientNote.NOTE_TYPE_CHOICES,
|
|
})
|
|
|
|
return context
|
|
|
|
|
|
class PatientNoteDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Display patient note details.
|
|
"""
|
|
model = PatientNote
|
|
template_name = 'patients/notes/patient_note_detail.html'
|
|
context_object_name = 'patient_note'
|
|
|
|
def get_queryset(self):
|
|
tenant = getattr(self.request, 'tenant', None)
|
|
if not tenant:
|
|
return PatientNote.objects.none()
|
|
return PatientNote.objects.filter(patient__tenant=tenant)
|
|
|
|
|
|
class PatientNoteCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create new patient note.
|
|
"""
|
|
model = PatientNote
|
|
form_class = PatientNoteForm
|
|
template_name = 'patients/notes/patient_note_form.html'
|
|
permission_required = 'patients.add_patientnote'
|
|
success_url = reverse_lazy('patients:patient_note_list')
|
|
|
|
def get_form_kwargs(self):
|
|
kwargs = super().get_form_kwargs()
|
|
kwargs['user'] = self.request.user
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
# Set author
|
|
form.instance.author = self.request.user
|
|
response = super().form_valid(form)
|
|
|
|
# Log patient note creation
|
|
AuditLogger.log_event(
|
|
tenant=getattr(self.request, 'tenant', None),
|
|
event_type='CREATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Create Patient Note',
|
|
description=f'Created patient note: {self.object.subject} for {self.object.patient}',
|
|
user=self.request.user,
|
|
content_object=self.object,
|
|
request=self.request
|
|
)
|
|
|
|
messages.success(self.request, f'Patient note "{self.object.subject}" created successfully.')
|
|
return response
|
|
|
|
|
|
# ============================================================================
|
|
# HTMX VIEWS FOR REAL-TIME UPDATES
|
|
# ============================================================================
|
|
|
|
@login_required
|
|
def patient_search(request):
|
|
"""
|
|
HTMX view for patient search.
|
|
"""
|
|
tenant = getattr(request, 'tenant', None)
|
|
if not tenant:
|
|
return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
|
|
search_query = request.GET.get('search', '')
|
|
queryset = PatientProfile.objects.filter(tenant=tenant, is_active=True)
|
|
|
|
if search_query:
|
|
queryset = queryset.filter(
|
|
Q(patient_id__icontains=search_query) |
|
|
Q(first_name__icontains=search_query) |
|
|
Q(last_name__icontains=search_query) |
|
|
Q(email__icontains=search_query) |
|
|
Q(phone_number__icontains=search_query)
|
|
)
|
|
|
|
patients = queryset.order_by('last_name', 'first_name')[:20]
|
|
|
|
return render(request, 'patients/partials/patient_list.html', {
|
|
'patients': patients
|
|
})
|
|
|
|
|
|
@login_required
|
|
def patient_stats(request):
|
|
"""
|
|
HTMX view for patient statistics.
|
|
"""
|
|
tenant = getattr(request, 'tenant', None)
|
|
if not tenant:
|
|
return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
|
|
# Calculate patient statistics
|
|
total_patients = PatientProfile.objects.filter(tenant=tenant).count()
|
|
active_patients = PatientProfile.objects.filter(tenant=tenant, is_active=True).count()
|
|
|
|
# Age distribution
|
|
today = timezone.now().date()
|
|
pediatric_count = PatientProfile.objects.filter(
|
|
tenant=tenant,
|
|
is_active=True,
|
|
date_of_birth__gt=today.replace(year=today.year - 18)
|
|
).count()
|
|
|
|
adult_count = PatientProfile.objects.filter(
|
|
tenant=tenant,
|
|
is_active=True,
|
|
date_of_birth__lte=today.replace(year=today.year - 18),
|
|
date_of_birth__gt=today.replace(year=today.year - 65)
|
|
).count()
|
|
|
|
geriatric_count = PatientProfile.objects.filter(
|
|
tenant=tenant,
|
|
is_active=True,
|
|
date_of_birth__lte=today.replace(year=today.year - 65)
|
|
).count()
|
|
|
|
# Gender distribution
|
|
gender_stats = PatientProfile.objects.filter(tenant=tenant, is_active=True).values('gender').annotate(
|
|
count=Count('id')
|
|
).order_by('-count')
|
|
|
|
# Recent registrations (last 30 days)
|
|
thirty_days_ago = today - timedelta(days=30)
|
|
recent_registrations = PatientProfile.objects.filter(
|
|
tenant=tenant,
|
|
created_at__gte=thirty_days_ago
|
|
).count()
|
|
|
|
stats = {
|
|
'total_patients': total_patients,
|
|
'active_patients': active_patients,
|
|
'pediatric_count': pediatric_count,
|
|
'adult_count': adult_count,
|
|
'geriatric_count': geriatric_count,
|
|
'gender_stats': gender_stats,
|
|
'recent_registrations': recent_registrations,
|
|
}
|
|
|
|
return render(request, 'patients/partials/patient_stats.html', {'stats': stats})
|
|
|
|
|
|
@login_required
|
|
def emergency_contacts_list(request, patient_id):
|
|
"""
|
|
HTMX view for emergency contacts list.
|
|
"""
|
|
tenant = getattr(request, 'tenant', None)
|
|
if not tenant:
|
|
return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
|
|
patient = get_object_or_404(PatientProfile, id=patient_id, tenant=tenant)
|
|
emergency_contacts = EmergencyContact.objects.filter(
|
|
patient=patient
|
|
).order_by('-is_primary', 'name')
|
|
|
|
return render(request, 'patients/partials/emergency_contacts_list.html', {
|
|
'emergency_contacts': emergency_contacts,
|
|
'patient': patient
|
|
})
|
|
|
|
|
|
@login_required
|
|
def insurance_info_list(request, patient_id):
|
|
"""
|
|
HTMX view for insurance info list.
|
|
"""
|
|
tenant = getattr(request, 'tenant', None)
|
|
if not tenant:
|
|
return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
|
|
patient = get_object_or_404(PatientProfile, id=patient_id, tenant=tenant)
|
|
insurance_info = InsuranceInfo.objects.filter(
|
|
patient=patient
|
|
).order_by('-effective_date')
|
|
|
|
return render(request, 'patients/partials/insurance_info_list.html', {
|
|
'insurance_info': insurance_info,
|
|
'patient': patient
|
|
})
|
|
|
|
|
|
@login_required
|
|
def consent_forms_list(request, patient_id):
|
|
"""
|
|
HTMX view for consent forms list.
|
|
"""
|
|
tenant = getattr(request, 'tenant', None)
|
|
if not tenant:
|
|
return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
|
|
patient = get_object_or_404(PatientProfile, id=patient_id, tenant=tenant)
|
|
consent_forms = ConsentForm.objects.filter(
|
|
patient=patient
|
|
).order_by('-created_at')
|
|
|
|
return render(request, 'patients/partials/consent_forms_list.html', {
|
|
'consent_forms': consent_forms,
|
|
'patient': patient
|
|
})
|
|
|
|
|
|
@login_required
|
|
def patient_notes_list(request, patient_id):
|
|
"""
|
|
HTMX view for patient notes list.
|
|
"""
|
|
tenant = getattr(request, 'tenant', None)
|
|
if not tenant:
|
|
return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
|
|
patient = get_object_or_404(PatientProfile, id=patient_id, tenant=tenant)
|
|
patient_notes = PatientNote.objects.filter(
|
|
patient=patient
|
|
).order_by('-created_at')[:10]
|
|
|
|
return render(request, 'patients/partials/patient_notes_list.html', {
|
|
'patient_notes': patient_notes,
|
|
'patient': patient
|
|
})
|
|
|
|
|
|
def patient_appointment_list(request, pk):
|
|
tenant = getattr(request, 'tenant', None)
|
|
if not tenant:
|
|
return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
|
|
patient = get_object_or_404(PatientProfile, id=pk, tenant=tenant)
|
|
patient_appointments = AppointmentRequest.objects.filter(
|
|
patient=patient
|
|
).order_by('-created_at')
|
|
|
|
return render(request, 'patients/partials/patient_appointment_list.html', {
|
|
'patient_appointments': patient_appointments,
|
|
'patient': patient
|
|
})
|
|
|
|
# ============================================================================
|
|
# ACTION VIEWS FOR WORKFLOW OPERATIONS
|
|
# ============================================================================
|
|
|
|
@login_required
|
|
def activate_patient(request, pk):
|
|
"""
|
|
Activate a patient account.
|
|
"""
|
|
tenant = getattr(request, 'tenant', None)
|
|
if not tenant:
|
|
messages.error(request, 'No tenant found.')
|
|
return redirect('patients:patient_list')
|
|
|
|
patient = get_object_or_404(PatientProfile, pk=pk, tenant=tenant)
|
|
|
|
patient.is_active = True
|
|
patient.save()
|
|
|
|
# Log activation
|
|
AuditLogger.log_event(
|
|
tenant=tenant,
|
|
event_type='UPDATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Activate Patient',
|
|
description=f'Activated patient: {patient.first_name} {patient.last_name}',
|
|
user=request.user,
|
|
content_object=patient,
|
|
request=request
|
|
)
|
|
|
|
messages.success(request, f'Patient "{patient.first_name} {patient.last_name}" activated successfully.')
|
|
return redirect('patients:patient_detail', pk=pk)
|
|
|
|
|
|
@login_required
|
|
def sign_consent_form(request, pk):
|
|
"""
|
|
Sign a consent form.
|
|
"""
|
|
tenant = getattr(request, 'tenant', None)
|
|
if not tenant:
|
|
return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
|
|
consent_form = get_object_or_404(
|
|
ConsentForm,
|
|
pk=pk,
|
|
patient__tenant=tenant
|
|
)
|
|
|
|
if request.method == 'POST':
|
|
# Update consent form status
|
|
consent_form.status = 'SIGNED'
|
|
consent_form.signed_date = timezone.now()
|
|
consent_form.signed_by = request.user
|
|
|
|
# Add witness information if required
|
|
if consent_form.template.requires_witness:
|
|
consent_form.witness_name = request.POST.get('witness_name')
|
|
consent_form.witness_signature = request.POST.get('witness_signature')
|
|
|
|
consent_form.save()
|
|
|
|
# Log consent form signing
|
|
AuditLogger.log_event(
|
|
tenant=tenant,
|
|
event_type='UPDATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Sign Consent Form',
|
|
description=f'Signed consent form: {consent_form.template.name} for {consent_form.patient}',
|
|
user=request.user,
|
|
content_object=consent_form,
|
|
request=request
|
|
)
|
|
|
|
messages.success(request, f'Consent form "{consent_form.template.name}" signed successfully.')
|
|
return JsonResponse({'status': 'signed'})
|
|
|
|
return render(request, 'patients/partials/sign_consent_form.html', {
|
|
'consent_form': consent_form
|
|
})
|
|
|
|
|
|
@login_required
|
|
def add_patient_note(request, patient_id):
|
|
"""
|
|
HTMX view to add a patient note.
|
|
"""
|
|
tenant = getattr(request, 'tenant', None)
|
|
if not tenant:
|
|
return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
|
|
patient = get_object_or_404(PatientProfile, id=patient_id, tenant=tenant)
|
|
|
|
if request.method == 'POST':
|
|
form = PatientNoteForm(request.POST, user=request.user)
|
|
if form.is_valid():
|
|
note = form.save(commit=False)
|
|
note.patient = patient
|
|
note.author = request.user
|
|
note.save()
|
|
|
|
# Log patient note creation
|
|
AuditLogger.log_event(
|
|
tenant=tenant,
|
|
event_type='CREATE',
|
|
event_category='PATIENT_MANAGEMENT',
|
|
action='Add Patient Note',
|
|
description=f'Added patient note: {note.subject} for {patient}',
|
|
user=request.user,
|
|
content_object=note,
|
|
request=request
|
|
)
|
|
|
|
return JsonResponse({'status': 'created', 'note_id': note.id})
|
|
else:
|
|
form = PatientNoteForm(user=request.user, initial={'patient': patient})
|
|
|
|
return render(request, 'patients/partials/add_patient_note.html', {
|
|
'form': form,
|
|
'patient': patient
|
|
})
|
|
|
|
|
|
def get_patient_info(request, pk):
|
|
patient = get_object_or_404(PatientProfile, pk=pk)
|
|
patient_info = {
|
|
'id': patient.id,
|
|
'first_name': patient.first_name,
|
|
'last_name': patient.last_name,
|
|
'gender': patient.get_gender_display(),
|
|
'date_of_birth': patient.date_of_birth,
|
|
'phone_number': patient.phone_number,
|
|
'email': patient.email,
|
|
}
|
|
return JsonResponse(patient_info)
|
|
|
|
#
|
|
# """
|
|
# Patients app views for hospital management system with comprehensive CRUD operations.
|
|
# """
|
|
#
|
|
# from django.shortcuts import render, get_object_or_404, redirect
|
|
# from django.contrib.auth.decorators import login_required
|
|
# from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
|
# from django.views.generic import (
|
|
# TemplateView, ListView, DetailView, CreateView, UpdateView, DeleteView
|
|
# )
|
|
# from django.http import JsonResponse
|
|
# from django.contrib import messages
|
|
# from django.db.models import Q, Count
|
|
# from django.utils import timezone
|
|
# from django.urls import reverse_lazy, reverse
|
|
# from django.core.paginator import Paginator
|
|
# from datetime import timedelta, date
|
|
#
|
|
# from core.models import AuditLogEntry
|
|
# from .models import *
|
|
# from .forms import *
|
|
# from core.utils import AuditLogger
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # PATIENT PROFILE VIEWS (FULL CRUD - Master Data)
|
|
# # ============================================================================
|
|
#
|
|
# class PatientListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# Patient listing view.
|
|
# """
|
|
# model = PatientProfile
|
|
# template_name = 'patients/patient_list.html'
|
|
# context_object_name = 'patients'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return PatientProfile.objects.none()
|
|
#
|
|
# queryset = PatientProfile.objects.filter(tenant=tenant)
|
|
#
|
|
# # Apply filters
|
|
# gender = self.request.GET.get('gender')
|
|
# if gender:
|
|
# queryset = queryset.filter(gender=gender)
|
|
#
|
|
# age_range = self.request.GET.get('age_range')
|
|
# if age_range:
|
|
# today = timezone.now().date()
|
|
# if age_range == 'pediatric':
|
|
# # Under 18
|
|
# min_birth_date = today.replace(year=today.year - 18)
|
|
# queryset = queryset.filter(date_of_birth__gt=min_birth_date)
|
|
# elif age_range == 'adult':
|
|
# # 18-64
|
|
# min_birth_date = today.replace(year=today.year - 65)
|
|
# max_birth_date = today.replace(year=today.year - 18)
|
|
# queryset = queryset.filter(
|
|
# date_of_birth__gt=min_birth_date,
|
|
# date_of_birth__lte=max_birth_date
|
|
# )
|
|
# elif age_range == 'geriatric':
|
|
# # 65+
|
|
# max_birth_date = today.replace(year=today.year - 65)
|
|
# queryset = queryset.filter(date_of_birth__lte=max_birth_date)
|
|
#
|
|
# blood_type = self.request.GET.get('blood_type')
|
|
# if blood_type:
|
|
# queryset = queryset.filter(blood_type=blood_type)
|
|
#
|
|
# status = self.request.GET.get('status')
|
|
# if status == 'active':
|
|
# queryset = queryset.filter(is_active=True)
|
|
# elif status == 'inactive':
|
|
# queryset = queryset.filter(is_active=False)
|
|
#
|
|
# search = self.request.GET.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(patient_id__icontains=search) |
|
|
# Q(first_name__icontains=search) |
|
|
# Q(last_name__icontains=search) |
|
|
# Q(email__icontains=search) |
|
|
# Q(phone_number__icontains=search) |
|
|
# Q(mobile_number__icontains=search)
|
|
# )
|
|
#
|
|
# return queryset.order_by('last_name', 'first_name')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
#
|
|
# if tenant:
|
|
# context.update({
|
|
# 'search_form': PatientsSearchForm(self.request.GET),
|
|
# 'total_patients': PatientProfile.objects.filter(tenant=tenant).count(),
|
|
# 'active_patients': PatientProfile.objects.filter(tenant=tenant, is_active=True).count(),
|
|
# })
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class PatientDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Patient detail view.
|
|
# """
|
|
# model = PatientProfile
|
|
# template_name = 'patients/patient_detail.html'
|
|
# context_object_name = 'patient'
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return PatientProfile.objects.none()
|
|
# return PatientProfile.objects.filter(tenant=tenant)
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# patient = self.get_object()
|
|
#
|
|
# # Get related data
|
|
# context['emergency_contacts'] = EmergencyContact.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-is_primary', 'name')
|
|
#
|
|
# context['insurance_info'] = InsuranceInfo.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-is_primary', '-effective_date')
|
|
#
|
|
# context['consent_forms'] = ConsentForm.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-created_at')
|
|
#
|
|
# context['patient_notes'] = PatientNote.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-created_at')[:10]
|
|
#
|
|
# # Calculate age
|
|
# if patient.date_of_birth:
|
|
# today = date.today()
|
|
# age = today.year - patient.date_of_birth.year
|
|
# if today.month < patient.date_of_birth.month or \
|
|
# (today.month == patient.date_of_birth.month and today.day < patient.date_of_birth.day):
|
|
# age -= 1
|
|
# context['patient_age'] = age
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class PatientRegistrationView(LoginRequiredMixin, CreateView):
|
|
# """
|
|
# View for registering new patients with comprehensive information.
|
|
# """
|
|
# model = PatientProfile
|
|
# form_class = PatientRegistrationForm
|
|
# template_name = 'patients/patient_registration.html'
|
|
# success_url = reverse_lazy('patients:patient_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'] = getattr(self.request, 'tenant', None)
|
|
# return kwargs
|
|
#
|
|
# def form_valid(self, form):
|
|
# """Process valid form and handle the related objects"""
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Access the related objects created during form save
|
|
# if hasattr(form, 'emergency_contacts'):
|
|
# messages.success(self.request, 'Emergency contact information saved.')
|
|
#
|
|
# if hasattr(form, 'insurance_info'):
|
|
# messages.success(self.request, 'Insurance information saved.')
|
|
#
|
|
# messages.success(self.request, f'Patient {self.object.get_full_name()} registered successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class PatientCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create new patient.
|
|
# """
|
|
# model = PatientProfile
|
|
# form_class = PatientProfileForm
|
|
# template_name = 'patients/patient_create.html'
|
|
# permission_required = 'patients.add_patientprofile'
|
|
# success_url = reverse_lazy('patients:patient_list')
|
|
#
|
|
# def form_valid(self, form):
|
|
# # Set tenant
|
|
# form.instance.tenant = getattr(self.request, 'tenant', None)
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log patient creation
|
|
# AuditLogger.log_event(
|
|
# tenant=form.instance.tenant,
|
|
# event_type='CREATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Create Patient',
|
|
# description=f'Created new patient: {self.object.first_name} {self.object.last_name}',
|
|
# user=self.request.user,
|
|
# content_object=self.object,
|
|
# request=self.request
|
|
# )
|
|
#
|
|
# messages.success(self.request,
|
|
# f'Patient "{self.object.first_name} {self.object.last_name}" created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class PatientUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
# """
|
|
# Update patient information.
|
|
# """
|
|
# model = PatientProfile
|
|
# form_class = PatientProfileForm
|
|
# template_name = 'patients/patient_form.html'
|
|
# permission_required = 'patients.change_patientprofile'
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return PatientProfile.objects.none()
|
|
# return PatientProfile.objects.filter(tenant=tenant)
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('patients:patient_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log patient update
|
|
# AuditLogger.log_event(
|
|
# tenant=self.object.tenant,
|
|
# event_type='UPDATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Update Patient',
|
|
# description=f'Updated patient: {self.object.first_name} {self.object.last_name}',
|
|
# user=self.request.user,
|
|
# content_object=self.object,
|
|
# request=self.request
|
|
# )
|
|
#
|
|
# messages.success(self.request,
|
|
# f'Patient "{self.object.first_name} {self.object.last_name}" updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class PatientDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
# """
|
|
# Delete patient (soft delete to inactive).
|
|
# """
|
|
# model = PatientProfile
|
|
# template_name = 'patients/patient_confirm_delete.html'
|
|
# permission_required = 'patients.delete_patientprofile'
|
|
# success_url = reverse_lazy('patients:patient_list')
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return PatientProfile.objects.none()
|
|
# return PatientProfile.objects.filter(tenant=tenant)
|
|
#
|
|
# def delete(self, request, *args, **kwargs):
|
|
# self.object = self.get_object()
|
|
#
|
|
# # Soft delete - set to inactive
|
|
# self.object.is_active = False
|
|
# self.object.save()
|
|
#
|
|
# # Log patient deletion
|
|
# AuditLogger.log_event(
|
|
# tenant=self.object.tenant,
|
|
# event_type='DELETE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Deactivate Patient',
|
|
# description=f'Deactivated patient: {self.object.first_name} {self.object.last_name}',
|
|
# user=request.user,
|
|
# content_object=self.object,
|
|
# request=request
|
|
# )
|
|
#
|
|
# messages.success(request,
|
|
# f'Patient "{self.object.first_name} {self.object.last_name}" deactivated successfully.')
|
|
# return redirect(self.success_url)
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # EMERGENCY CONTACT VIEWS (FULL CRUD - Operational Data)
|
|
# # ============================================================================
|
|
#
|
|
# class EmergencyContactListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List emergency contacts.
|
|
# """
|
|
# model = EmergencyContact
|
|
# template_name = 'patients/emergency_contact_list.html'
|
|
# context_object_name = 'emergency_contacts'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return EmergencyContact.objects.none()
|
|
#
|
|
# queryset = EmergencyContact.objects.filter(patient__tenant=tenant)
|
|
#
|
|
# # Apply filters
|
|
# patient_id = self.request.GET.get('patient_id')
|
|
# if patient_id:
|
|
# queryset = queryset.filter(patient_id=patient_id)
|
|
#
|
|
# relationship = self.request.GET.get('relationship')
|
|
# if relationship:
|
|
# queryset = queryset.filter(relationship__icontains=relationship)
|
|
#
|
|
# is_primary = self.request.GET.get('is_primary')
|
|
# if is_primary == 'true':
|
|
# queryset = queryset.filter(is_primary=True)
|
|
# elif is_primary == 'false':
|
|
# queryset = queryset.filter(is_primary=False)
|
|
#
|
|
# return queryset.order_by('patient__last_name', '-is_primary', 'name')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
#
|
|
# if tenant:
|
|
# context.update({
|
|
# 'patients': PatientProfile.objects.filter(tenant=tenant, is_active=True).order_by('last_name',
|
|
# 'first_name'),
|
|
# })
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class EmergencyContactDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Display emergency contact details.
|
|
# """
|
|
# model = EmergencyContact
|
|
# template_name = 'patients/emergency_contact_detail.html'
|
|
# context_object_name = 'emergency_contact'
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return EmergencyContact.objects.none()
|
|
# return EmergencyContact.objects.filter(patient__tenant=tenant)
|
|
#
|
|
#
|
|
# class EmergencyContactCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create new emergency contact.
|
|
# """
|
|
# model = EmergencyContact
|
|
# form_class = EmergencyContactForm
|
|
# template_name = 'patients/emergency_contact_form.html'
|
|
# permission_required = 'patients.add_emergencycontact'
|
|
# success_url = reverse_lazy('patients:emergency_contact_list')
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log emergency contact creation
|
|
# AuditLogger.log_event(
|
|
# tenant=getattr(self.request, 'tenant', None),
|
|
# event_type='CREATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Create Emergency Contact',
|
|
# description=f'Created emergency contact: {self.object.name} for {self.object.patient}',
|
|
# user=self.request.user,
|
|
# content_object=self.object,
|
|
# request=self.request
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Emergency contact "{self.object.name}" created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class EmergencyContactUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
# """
|
|
# Update emergency contact.
|
|
# """
|
|
# model = EmergencyContact
|
|
# form_class = EmergencyContactForm
|
|
# template_name = 'patients/emergency_contact_form.html'
|
|
# permission_required = 'patients.change_emergencycontact'
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return EmergencyContact.objects.none()
|
|
# return EmergencyContact.objects.filter(patient__tenant=tenant)
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('patients:emergency_contact_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log emergency contact update
|
|
# AuditLogger.log_event(
|
|
# tenant=getattr(self.request, 'tenant', None),
|
|
# event_type='UPDATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Update Emergency Contact',
|
|
# description=f'Updated emergency contact: {self.object.name}',
|
|
# user=self.request.user,
|
|
# content_object=self.object,
|
|
# request=self.request
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Emergency contact "{self.object.name}" updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class EmergencyContactDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
# """
|
|
# Delete emergency contact.
|
|
# """
|
|
# model = EmergencyContact
|
|
# template_name = 'patients/emergency_contact_confirm_delete.html'
|
|
# permission_required = 'patients.delete_emergencycontact'
|
|
# success_url = reverse_lazy('patients:emergency_contact_list')
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return EmergencyContact.objects.none()
|
|
# return EmergencyContact.objects.filter(patient__tenant=tenant)
|
|
#
|
|
# def delete(self, request, *args, **kwargs):
|
|
# self.object = self.get_object()
|
|
# contact_name = self.object.name
|
|
#
|
|
# # Log emergency contact deletion
|
|
# AuditLogger.log_event(
|
|
# tenant=getattr(request, 'tenant', None),
|
|
# event_type='DELETE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Delete Emergency Contact',
|
|
# description=f'Deleted emergency contact: {contact_name}',
|
|
# user=request.user,
|
|
# content_object=self.object,
|
|
# request=request
|
|
# )
|
|
#
|
|
# messages.success(request, f'Emergency contact "{contact_name}" deleted successfully.')
|
|
# return super().delete(request, *args, **kwargs)
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # INSURANCE INFO VIEWS (FULL CRUD - Operational Data)
|
|
# # ============================================================================
|
|
#
|
|
# class InsuranceInfoListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List insurance information.
|
|
# """
|
|
# model = InsuranceInfo
|
|
# template_name = 'patients/insurance_info_list.html'
|
|
# context_object_name = 'insurance_info'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return InsuranceInfo.objects.none()
|
|
#
|
|
# queryset = InsuranceInfo.objects.filter(patient__tenant=tenant)
|
|
#
|
|
# # Apply filters
|
|
# patient_id = self.request.GET.get('patient_id')
|
|
# if patient_id:
|
|
# queryset = queryset.filter(patient_id=patient_id)
|
|
#
|
|
# insurance_company = self.request.GET.get('insurance_company')
|
|
# if insurance_company:
|
|
# queryset = queryset.filter(insurance_company__icontains=insurance_company)
|
|
#
|
|
# is_primary = self.request.GET.get('is_primary')
|
|
# if is_primary == 'true':
|
|
# queryset = queryset.filter(is_primary=True)
|
|
# elif is_primary == 'false':
|
|
# queryset = queryset.filter(is_primary=False)
|
|
#
|
|
# # Filter by expiry status
|
|
# expiry_status = self.request.GET.get('expiry_status')
|
|
# today = timezone.now().date()
|
|
# if expiry_status == 'expired':
|
|
# queryset = queryset.filter(expiry_date__lt=today)
|
|
# elif expiry_status == 'expiring_soon':
|
|
# # Expiring within 30 days
|
|
# expiry_threshold = today + timedelta(days=30)
|
|
# queryset = queryset.filter(expiry_date__lte=expiry_threshold, expiry_date__gte=today)
|
|
#
|
|
# return queryset.order_by('patient__last_name', '-is_primary', '-effective_date')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
#
|
|
# if tenant:
|
|
# context.update({
|
|
# 'patients': PatientProfile.objects.filter(tenant=tenant, is_active=True).order_by('last_name',
|
|
# 'first_name'),
|
|
# })
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class InsuranceInfoDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Display insurance information details.
|
|
# """
|
|
# model = InsuranceInfo
|
|
# template_name = 'patients/insurance_info_detail.html'
|
|
# context_object_name = 'insurance_info'
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return InsuranceInfo.objects.none()
|
|
# return InsuranceInfo.objects.filter(patient__tenant=tenant)
|
|
#
|
|
#
|
|
# class InsuranceInfoCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create new insurance information.
|
|
# """
|
|
# model = InsuranceInfo
|
|
# form_class = InsuranceInfoForm
|
|
# template_name = 'patients/insurance_info_form.html'
|
|
# permission_required = 'patients.add_insuranceinfo'
|
|
# success_url = reverse_lazy('patients:insurance_info_list')
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log insurance info creation
|
|
# AuditLogger.log_event(
|
|
# tenant=getattr(self.request, 'tenant', None),
|
|
# event_type='CREATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Create Insurance Info',
|
|
# description=f'Created insurance info: {self.object.insurance_company} for {self.object.patient}',
|
|
# user=self.request.user,
|
|
# content_object=self.object,
|
|
# request=self.request
|
|
# )
|
|
#
|
|
# messages.success(self.request,
|
|
# f'Insurance information for "{self.object.insurance_company}" created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class InsuranceInfoUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
# """
|
|
# Update insurance information.
|
|
# """
|
|
# model = InsuranceInfo
|
|
# form_class = InsuranceInfoForm
|
|
# template_name = 'patients/insurance_info_form.html'
|
|
# permission_required = 'patients.change_insuranceinfo'
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return InsuranceInfo.objects.none()
|
|
# return InsuranceInfo.objects.filter(patient__tenant=tenant)
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('patients:insurance_info_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log insurance info update
|
|
# AuditLogger.log_event(
|
|
# tenant=getattr(self.request, 'tenant', None),
|
|
# event_type='UPDATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Update Insurance Info',
|
|
# description=f'Updated insurance info: {self.object.insurance_company}',
|
|
# user=self.request.user,
|
|
# content_object=self.object,
|
|
# request=self.request
|
|
# )
|
|
#
|
|
# messages.success(self.request,
|
|
# f'Insurance information for "{self.object.insurance_company}" updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class InsuranceInfoDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
# """
|
|
# Delete insurance information.
|
|
# """
|
|
# model = InsuranceInfo
|
|
# template_name = 'patients/insurance_info_confirm_delete.html'
|
|
# permission_required = 'patients.delete_insuranceinfo'
|
|
# success_url = reverse_lazy('patients:insurance_info_list')
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return InsuranceInfo.objects.none()
|
|
# return InsuranceInfo.objects.filter(patient__tenant=tenant)
|
|
#
|
|
# def delete(self, request, *args, **kwargs):
|
|
# self.object = self.get_object()
|
|
# insurance_company = self.object.insurance_company
|
|
#
|
|
# # Log insurance info deletion
|
|
# AuditLogger.log_event(
|
|
# tenant=getattr(request, 'tenant', None),
|
|
# event_type='DELETE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Delete Insurance Info',
|
|
# description=f'Deleted insurance info: {insurance_company}',
|
|
# user=request.user,
|
|
# content_object=self.object,
|
|
# request=request
|
|
# )
|
|
#
|
|
# messages.success(request, f'Insurance information for "{insurance_company}" deleted successfully.')
|
|
# return super().delete(request, *args, **kwargs)
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # CONSENT TEMPLATE VIEWS (FULL CRUD - Master Data)
|
|
# # ============================================================================
|
|
#
|
|
# class ConsentTemplateListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List consent templates.
|
|
# """
|
|
# model = ConsentTemplate
|
|
# template_name = 'patients/consent_template_list.html'
|
|
# context_object_name = 'consent_templates'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return ConsentTemplate.objects.none()
|
|
#
|
|
# queryset = ConsentTemplate.objects.filter(tenant=tenant)
|
|
#
|
|
# # Apply filters
|
|
# template_type = self.request.GET.get('template_type')
|
|
# if template_type:
|
|
# queryset = queryset.filter(template_type=template_type)
|
|
#
|
|
# status = self.request.GET.get('status')
|
|
# if status == 'active':
|
|
# queryset = queryset.filter(is_active=True)
|
|
# elif status == 'inactive':
|
|
# queryset = queryset.filter(is_active=False)
|
|
#
|
|
# search = self.request.GET.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(name__icontains=search) |
|
|
# Q(description__icontains=search)
|
|
# )
|
|
#
|
|
# return queryset.order_by('template_type', 'name')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
#
|
|
# if tenant:
|
|
# context.update({
|
|
# 'template_types': ConsentTemplate.objects.filter(
|
|
# tenant=tenant
|
|
# ).values_list('template_type', flat=True).distinct(),
|
|
# })
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class ConsentTemplateDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Display consent template details.
|
|
# """
|
|
# model = ConsentTemplate
|
|
# template_name = 'patients/consent_template_detail.html'
|
|
# context_object_name = 'consent_template'
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return ConsentTemplate.objects.none()
|
|
# return ConsentTemplate.objects.filter(tenant=tenant)
|
|
#
|
|
#
|
|
# class ConsentTemplateCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create new consent template.
|
|
# """
|
|
# model = ConsentTemplate
|
|
# form_class = ConsentTemplateForm
|
|
# template_name = 'patients/consent_template_form.html'
|
|
# permission_required = 'patients.add_consenttemplate'
|
|
# success_url = reverse_lazy('patients:consent_template_list')
|
|
#
|
|
# def form_valid(self, form):
|
|
# # Set tenant
|
|
# form.instance.tenant = getattr(self.request, 'tenant', None)
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log consent template creation
|
|
# AuditLogger.log_event(
|
|
# tenant=form.instance.tenant,
|
|
# event_type='CREATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Create Consent Template',
|
|
# description=f'Created consent template: {self.object.name}',
|
|
# user=self.request.user,
|
|
# content_object=self.object,
|
|
# request=self.request
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Consent template "{self.object.name}" created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class ConsentTemplateUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
# """
|
|
# Update consent template.
|
|
# """
|
|
# model = ConsentTemplate
|
|
# form_class = ConsentTemplateForm
|
|
# template_name = 'patients/consent_template_form.html'
|
|
# permission_required = 'patients.change_consenttemplate'
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return ConsentTemplate.objects.none()
|
|
# return ConsentTemplate.objects.filter(tenant=tenant)
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('patients:consent_template_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log consent template update
|
|
# AuditLogger.log_event(
|
|
# tenant=self.object.tenant,
|
|
# event_type='UPDATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Update Consent Template',
|
|
# description=f'Updated consent template: {self.object.name}',
|
|
# user=self.request.user,
|
|
# content_object=self.object,
|
|
# request=self.request
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Consent template "{self.object.name}" updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class ConsentTemplateDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
# """
|
|
# Delete consent template.
|
|
# """
|
|
# model = ConsentTemplate
|
|
# template_name = 'patients/consent_template_confirm_delete.html'
|
|
# permission_required = 'patients.delete_consenttemplate'
|
|
# success_url = reverse_lazy('patients:consent_template_list')
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return ConsentTemplate.objects.none()
|
|
# return ConsentTemplate.objects.filter(tenant=tenant)
|
|
#
|
|
# def delete(self, request, *args, **kwargs):
|
|
# self.object = self.get_object()
|
|
# template_name = self.object.name
|
|
#
|
|
# # Check if template is being used
|
|
# if ConsentForm.objects.filter(template=self.object).exists():
|
|
# messages.error(request,
|
|
# f'Cannot delete consent template "{template_name}" as it is being used by consent forms.')
|
|
# return redirect('patients:consent_template_detail', pk=self.object.pk)
|
|
#
|
|
# # Log consent template deletion
|
|
# AuditLogger.log_event(
|
|
# tenant=getattr(request, 'tenant', None),
|
|
# event_type='DELETE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Delete Consent Template',
|
|
# description=f'Deleted consent template: {template_name}',
|
|
# user=request.user,
|
|
# content_object=self.object,
|
|
# request=request
|
|
# )
|
|
#
|
|
# messages.success(request, f'Consent template "{template_name}" deleted successfully.')
|
|
# return super().delete(request, *args, **kwargs)
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # CONSENT FORM VIEWS (APPEND-ONLY - Clinical Records)
|
|
# # ============================================================================
|
|
#
|
|
# class ConsentFormListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List consent forms.
|
|
# """
|
|
# model = ConsentForm
|
|
# template_name = 'patients/consent_form_list.html'
|
|
# context_object_name = 'consent_forms'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return ConsentForm.objects.none()
|
|
#
|
|
# queryset = ConsentForm.objects.filter(patient__tenant=tenant)
|
|
#
|
|
# # Apply filters
|
|
# patient_id = self.request.GET.get('patient_id')
|
|
# if patient_id:
|
|
# queryset = queryset.filter(patient_id=patient_id)
|
|
#
|
|
# template_id = self.request.GET.get('template_id')
|
|
# if template_id:
|
|
# queryset = queryset.filter(template_id=template_id)
|
|
#
|
|
# status = self.request.GET.get('status')
|
|
# if status:
|
|
# queryset = queryset.filter(status=status)
|
|
#
|
|
# return queryset.order_by('-created_at')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
#
|
|
# if tenant:
|
|
# context.update({
|
|
# 'patients': PatientProfile.objects.filter(tenant=tenant, is_active=True).order_by('last_name',
|
|
# 'first_name'),
|
|
# 'templates': ConsentTemplate.objects.filter(tenant=tenant, is_active=True).order_by('name'),
|
|
# 'status_choices': ConsentForm.STATUS_CHOICES,
|
|
# })
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class ConsentManagementView(LoginRequiredMixin, PermissionRequiredMixin, TemplateView):
|
|
# """
|
|
# View for managing consent templates and patient consent forms.
|
|
#
|
|
# This view provides functionality to:
|
|
# 1. Create and manage consent templates
|
|
# 2. View consent status across patients
|
|
# 3. Generate consent forms from templates
|
|
# 4. Track consent expirations
|
|
# """
|
|
# template_name = 'patients/consent_management.html'
|
|
# permission_required = 'patients.view_consenttemplate'
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# """Prepare comprehensive context for consent management"""
|
|
# context = super().get_context_data(**kwargs)
|
|
#
|
|
# # Get active consent templates
|
|
# templates = ConsentTemplate.objects.filter(
|
|
# tenant=self.request.user.tenant,
|
|
# is_active=True
|
|
# ).order_by('category', 'name')
|
|
#
|
|
# # Count consents by category
|
|
# category_counts = ConsentTemplate.objects.filter(
|
|
# tenant=self.request.user.tenant
|
|
# ).values('category').annotate(count=Count('id'))
|
|
#
|
|
# # Get recent consent forms
|
|
# recent_consents = ConsentForm.objects.filter(
|
|
# tenant=self.request.user.tenant
|
|
# ).select_related('patient', 'template').order_by('-created_at')[:10]
|
|
#
|
|
# # Get expired/expiring consents
|
|
# today = timezone.now().date()
|
|
# expiry_threshold = today + timezone.timedelta(days=30)
|
|
#
|
|
# expiring_consents = ConsentForm.objects.filter(
|
|
# tenant=self.request.user.tenant,
|
|
# status='SIGNED',
|
|
# expiry_date__lte=expiry_threshold,
|
|
# expiry_date__gt=today
|
|
# ).select_related('patient', 'template').order_by('expiry_date')
|
|
#
|
|
# expired_consents = ConsentForm.objects.filter(
|
|
# tenant=self.request.user.tenant,
|
|
# status='SIGNED',
|
|
# expiry_date__lt=today
|
|
# ).select_related('patient', 'template').order_by('-expiry_date')[:20]
|
|
#
|
|
# # Get unsigned consents
|
|
# unsigned_consents = ConsentForm.objects.filter(
|
|
# tenant=self.request.user.tenant,
|
|
# status='PENDING'
|
|
# ).select_related('patient', 'template').order_by('-created_at')[:20]
|
|
#
|
|
# # Calculate consent statistics
|
|
# total_consents = ConsentForm.objects.filter(tenant=self.request.user.tenant).count()
|
|
# signed_count = ConsentForm.objects.filter(
|
|
# tenant=self.request.user.tenant,
|
|
# status='SIGNED'
|
|
# ).count()
|
|
#
|
|
# expired_count = ConsentForm.objects.filter(
|
|
# tenant=self.request.user.tenant,
|
|
# status='SIGNED',
|
|
# expiry_date__lt=today
|
|
# ).count()
|
|
#
|
|
# pending_count = ConsentForm.objects.filter(
|
|
# tenant=self.request.user.tenant,
|
|
# status='PENDING'
|
|
# ).count()
|
|
#
|
|
# # Check if we have a template to create
|
|
# template_form = None
|
|
# if self.request.user.has_perm('patients.add_consenttemplate'):
|
|
# template_form = ConsentTemplateForm(
|
|
# user=self.request.user,
|
|
# tenant=self.request.user.tenant
|
|
# )
|
|
#
|
|
# # Prepare context
|
|
# context.update({
|
|
# 'title': 'Consent Management',
|
|
# 'templates': templates,
|
|
# 'category_counts': category_counts,
|
|
# 'recent_consents': recent_consents,
|
|
# 'expiring_consents': expiring_consents,
|
|
# 'expired_consents': expired_consents,
|
|
# 'unsigned_consents': unsigned_consents,
|
|
# 'total_consents': total_consents,
|
|
# 'signed_count': signed_count,
|
|
# 'expired_count': expired_count,
|
|
# 'pending_count': pending_count,
|
|
# 'template_form': template_form,
|
|
# 'consent_categories': ConsentTemplate._meta.get_field('category').choices,
|
|
# })
|
|
#
|
|
# return context
|
|
#
|
|
# def post(self, request, *args, **kwargs):
|
|
# """Handle form submissions for template creation"""
|
|
# # Check permission for template creation
|
|
# if not request.user.has_perm('patients.add_consenttemplate'):
|
|
# messages.error(request, "You don't have permission to create consent templates.")
|
|
# return redirect('patients:consent_management')
|
|
#
|
|
# # Process template form
|
|
# template_form = ConsentTemplateForm(
|
|
# request.POST,
|
|
# user=request.user,
|
|
# tenant=request.user.tenant
|
|
# )
|
|
#
|
|
# if template_form.is_valid():
|
|
# template = template_form.save()
|
|
# messages.success(
|
|
# request,
|
|
# f"Consent template '{template.name}' created successfully."
|
|
# )
|
|
# return redirect('patients:consent_management')
|
|
# else:
|
|
# # Re-render page with form errors
|
|
# context = self.get_context_data()
|
|
# context['template_form'] = template_form
|
|
# return self.render_to_response(context)
|
|
#
|
|
# def get_template(self, template_id):
|
|
# """Get a consent template by ID"""
|
|
# return get_object_or_404(
|
|
# ConsentTemplate,
|
|
# pk=template_id,
|
|
# tenant=self.request.user.tenant
|
|
# )
|
|
#
|
|
# def generate_consent_form(self, patient_id, template_id):
|
|
# """Generate a consent form for a patient from a template"""
|
|
# # Validate permissions
|
|
# if not self.request.user.has_perm('patients.add_consentform'):
|
|
# messages.error(self.request, "You don't have permission to generate consent forms.")
|
|
# return redirect('patients:consent_management')
|
|
#
|
|
# # Get the patient and template
|
|
# patient = get_object_or_404(
|
|
# PatientProfile,
|
|
# pk=patient_id,
|
|
# tenant=self.request.user.tenant
|
|
# )
|
|
#
|
|
# template = self.get_template(template_id)
|
|
#
|
|
# # Check if a consent form already exists
|
|
# existing_form = ConsentForm.objects.filter(
|
|
# patient=patient,
|
|
# template=template,
|
|
# status='PENDING'
|
|
# ).first()
|
|
#
|
|
# if existing_form:
|
|
# messages.info(
|
|
# self.request,
|
|
# f"A pending consent form already exists for {patient.get_full_name()}."
|
|
# )
|
|
# return redirect('patients:consent_form_detail', pk=existing_form.pk)
|
|
#
|
|
# # Create the consent form
|
|
# consent_form = ConsentForm.objects.create(
|
|
# consent_id=uuid.uuid4(),
|
|
# patient=patient,
|
|
# template=template,
|
|
# tenant=self.request.user.tenant,
|
|
# status='PENDING',
|
|
# created_by=self.request.user,
|
|
# effective_date=timezone.now().date(),
|
|
# expiry_date=template.expiry_date
|
|
# )
|
|
#
|
|
# # Log the creation
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=self.request.user.tenant,
|
|
# user=self.request.user,
|
|
# action='CREATE',
|
|
# entity_type='ConsentForm',
|
|
# entity_id=str(consent_form.consent_id),
|
|
# details={
|
|
# 'patient': patient.get_full_name(),
|
|
# 'template': template.name,
|
|
# 'status': 'PENDING'
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(
|
|
# self.request,
|
|
# f"Consent form generated for {patient.get_full_name()}"
|
|
# )
|
|
#
|
|
# return redirect('patients:consent_form_detail', pk=consent_form.pk)
|
|
#
|
|
#
|
|
# class ConsentFormDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Display consent form details.
|
|
# """
|
|
# model = ConsentForm
|
|
# template_name = 'patients/consent_form_detail.html'
|
|
# context_object_name = 'consent_form'
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return ConsentForm.objects.none()
|
|
# return ConsentForm.objects.filter(patient__tenant=tenant)
|
|
#
|
|
#
|
|
# class ConsentFormCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create new consent form.
|
|
# """
|
|
# model = ConsentForm
|
|
# form_class = ConsentFormForm
|
|
# template_name = 'patients/consent_form_form.html'
|
|
# permission_required = 'patients.add_consentform'
|
|
# success_url = reverse_lazy('patients:consent_form_list')
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log consent form creation
|
|
# AuditLogger.log_event(
|
|
# tenant=getattr(self.request, 'tenant', None),
|
|
# event_type='CREATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Create Consent Form',
|
|
# description=f'Created consent form: {self.object.template.name} for {self.object.patient}',
|
|
# user=self.request.user,
|
|
# content_object=self.object,
|
|
# request=self.request
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Consent form "{self.object.template.name}" created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # PATIENT NOTE VIEWS (APPEND-ONLY - Clinical Records)
|
|
# # ============================================================================
|
|
#
|
|
# class PatientNoteListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List patient notes.
|
|
# """
|
|
# model = PatientNote
|
|
# template_name = 'patients/patient_note_list.html'
|
|
# context_object_name = 'patient_notes'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return PatientNote.objects.none()
|
|
#
|
|
# queryset = PatientNote.objects.filter(patient__tenant=tenant)
|
|
#
|
|
# # Apply filters
|
|
# patient_id = self.request.GET.get('patient_id')
|
|
# if patient_id:
|
|
# queryset = queryset.filter(patient_id=patient_id)
|
|
#
|
|
# note_type = self.request.GET.get('note_type')
|
|
# if note_type:
|
|
# queryset = queryset.filter(note_type=note_type)
|
|
#
|
|
# is_confidential = self.request.GET.get('is_confidential')
|
|
# if is_confidential == 'true':
|
|
# queryset = queryset.filter(is_confidential=True)
|
|
# elif is_confidential == 'false':
|
|
# queryset = queryset.filter(is_confidential=False)
|
|
#
|
|
# search = self.request.GET.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(subject__icontains=search) |
|
|
# Q(content__icontains=search)
|
|
# )
|
|
#
|
|
# return queryset.order_by('-created_at')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
#
|
|
# if tenant:
|
|
# context.update({
|
|
# 'patients': PatientProfile.objects.filter(tenant=tenant, is_active=True).order_by('last_name',
|
|
# 'first_name'),
|
|
# 'note_types': PatientNote.NOTE_TYPE_CHOICES,
|
|
# })
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class PatientNoteDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Display patient note details.
|
|
# """
|
|
# model = PatientNote
|
|
# template_name = 'patients/patient_note_detail.html'
|
|
# context_object_name = 'patient_note'
|
|
#
|
|
# def get_queryset(self):
|
|
# tenant = getattr(self.request, 'tenant', None)
|
|
# if not tenant:
|
|
# return PatientNote.objects.none()
|
|
# return PatientNote.objects.filter(patient__tenant=tenant)
|
|
#
|
|
#
|
|
# class PatientNoteCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create new patient note.
|
|
# """
|
|
# model = PatientNote
|
|
# form_class = PatientNoteForm
|
|
# template_name = 'patients/patient_note_form.html'
|
|
# permission_required = 'patients.add_patientnote'
|
|
# success_url = reverse_lazy('patients:patient_note_list')
|
|
#
|
|
# def get_form_kwargs(self):
|
|
# kwargs = super().get_form_kwargs()
|
|
# kwargs['user'] = self.request.user
|
|
# return kwargs
|
|
#
|
|
# def form_valid(self, form):
|
|
# # Set author
|
|
# form.instance.author = self.request.user
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log patient note creation
|
|
# AuditLogger.log_event(
|
|
# tenant=getattr(self.request, 'tenant', None),
|
|
# event_type='CREATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Create Patient Note',
|
|
# description=f'Created patient note: {self.object.subject} for {self.object.patient}',
|
|
# user=self.request.user,
|
|
# content_object=self.object,
|
|
# request=self.request
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Patient note "{self.object.subject}" created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # HTMX VIEWS FOR REAL-TIME UPDATES
|
|
# # ============================================================================
|
|
#
|
|
# @login_required
|
|
# def patient_search(request):
|
|
# """
|
|
# HTMX view for patient search.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# if not tenant:
|
|
# return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
#
|
|
# search_query = request.GET.get('search', '')
|
|
# queryset = PatientProfile.objects.filter(tenant=tenant, is_active=True)
|
|
#
|
|
# if search_query:
|
|
# queryset = queryset.filter(
|
|
# Q(patient_id__icontains=search_query) |
|
|
# Q(first_name__icontains=search_query) |
|
|
# Q(last_name__icontains=search_query) |
|
|
# Q(email__icontains=search_query) |
|
|
# Q(phone_number__icontains=search_query)
|
|
# )
|
|
#
|
|
# patients = queryset.order_by('last_name', 'first_name')[:20]
|
|
#
|
|
# return render(request, 'patients/partials/patient_list.html', {
|
|
# 'patients': patients
|
|
# })
|
|
#
|
|
#
|
|
# @login_required
|
|
# def patient_stats(request):
|
|
# """
|
|
# HTMX view for patient statistics.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# if not tenant:
|
|
# return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
#
|
|
# # Calculate patient statistics
|
|
# total_patients = PatientProfile.objects.filter(tenant=tenant).count()
|
|
# active_patients = PatientProfile.objects.filter(tenant=tenant, is_active=True).count()
|
|
#
|
|
# # Age distribution
|
|
# today = timezone.now().date()
|
|
# pediatric_count = PatientProfile.objects.filter(
|
|
# tenant=tenant,
|
|
# is_active=True,
|
|
# date_of_birth__gt=today.replace(year=today.year - 18)
|
|
# ).count()
|
|
#
|
|
# adult_count = PatientProfile.objects.filter(
|
|
# tenant=tenant,
|
|
# is_active=True,
|
|
# date_of_birth__lte=today.replace(year=today.year - 18),
|
|
# date_of_birth__gt=today.replace(year=today.year - 65)
|
|
# ).count()
|
|
#
|
|
# geriatric_count = PatientProfile.objects.filter(
|
|
# tenant=tenant,
|
|
# is_active=True,
|
|
# date_of_birth__lte=today.replace(year=today.year - 65)
|
|
# ).count()
|
|
#
|
|
# # Gender distribution
|
|
# gender_stats = PatientProfile.objects.filter(tenant=tenant, is_active=True).values('gender').annotate(
|
|
# count=Count('id')
|
|
# ).order_by('-count')
|
|
#
|
|
# # Recent registrations (last 30 days)
|
|
# thirty_days_ago = today - timedelta(days=30)
|
|
# recent_registrations = PatientProfile.objects.filter(
|
|
# tenant=tenant,
|
|
# created_at__gte=thirty_days_ago
|
|
# ).count()
|
|
#
|
|
# stats = {
|
|
# 'total_patients': total_patients,
|
|
# 'active_patients': active_patients,
|
|
# 'pediatric_count': pediatric_count,
|
|
# 'adult_count': adult_count,
|
|
# 'geriatric_count': geriatric_count,
|
|
# 'gender_stats': gender_stats,
|
|
# 'recent_registrations': recent_registrations,
|
|
# }
|
|
#
|
|
# return render(request, 'patients/partials/patient_stats.html', {'stats': stats})
|
|
#
|
|
#
|
|
# @login_required
|
|
# def emergency_contacts_list(request, patient_id):
|
|
# """
|
|
# HTMX view for emergency contacts list.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# if not tenant:
|
|
# return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
#
|
|
# patient = get_object_or_404(PatientProfile, id=patient_id, tenant=tenant)
|
|
# emergency_contacts = EmergencyContact.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-is_primary', 'name')
|
|
#
|
|
# return render(request, 'patients/partials/emergency_contacts_list.html', {
|
|
# 'emergency_contacts': emergency_contacts,
|
|
# 'patient': patient
|
|
# })
|
|
#
|
|
#
|
|
# @login_required
|
|
# def insurance_info_list(request, patient_id):
|
|
# """
|
|
# HTMX view for insurance info list.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# if not tenant:
|
|
# return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
#
|
|
# patient = get_object_or_404(PatientProfile, id=patient_id, tenant=tenant)
|
|
# insurance_info = InsuranceInfo.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-is_primary', '-effective_date')
|
|
#
|
|
# return render(request, 'patients/partials/insurance_info_list.html', {
|
|
# 'insurance_info': insurance_info,
|
|
# 'patient': patient
|
|
# })
|
|
#
|
|
#
|
|
# @login_required
|
|
# def consent_forms_list(request, patient_id):
|
|
# """
|
|
# HTMX view for consent forms list.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# if not tenant:
|
|
# return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
#
|
|
# patient = get_object_or_404(PatientProfile, id=patient_id, tenant=tenant)
|
|
# consent_forms = ConsentForm.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-created_at')
|
|
#
|
|
# return render(request, 'patients/partials/consent_forms_list.html', {
|
|
# 'consent_forms': consent_forms,
|
|
# 'patient': patient
|
|
# })
|
|
#
|
|
#
|
|
# @login_required
|
|
# def patient_notes_list(request, patient_id):
|
|
# """
|
|
# HTMX view for patient notes list.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# if not tenant:
|
|
# return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
#
|
|
# patient = get_object_or_404(PatientProfile, id=patient_id, tenant=tenant)
|
|
# patient_notes = PatientNote.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-created_at')[:10]
|
|
#
|
|
# return render(request, 'patients/partials/patient_notes_list.html', {
|
|
# 'patient_notes': patient_notes,
|
|
# 'patient': patient
|
|
# })
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # ACTION VIEWS FOR WORKFLOW OPERATIONS
|
|
# # ============================================================================
|
|
#
|
|
# @login_required
|
|
# def activate_patient(request, pk):
|
|
# """
|
|
# Activate a patient account.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# if not tenant:
|
|
# messages.error(request, 'No tenant found.')
|
|
# return redirect('patients:patient_list')
|
|
#
|
|
# patient = get_object_or_404(PatientProfile, pk=pk, tenant=tenant)
|
|
#
|
|
# patient.is_active = True
|
|
# patient.save()
|
|
#
|
|
# # Log activation
|
|
# AuditLogger.log_event(
|
|
# tenant=tenant,
|
|
# event_type='UPDATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Activate Patient',
|
|
# description=f'Activated patient: {patient.first_name} {patient.last_name}',
|
|
# user=request.user,
|
|
# content_object=patient,
|
|
# request=request
|
|
# )
|
|
#
|
|
# messages.success(request, f'Patient "{patient.first_name} {patient.last_name}" activated successfully.')
|
|
# return redirect('patients:patient_detail', pk=pk)
|
|
#
|
|
#
|
|
# @login_required
|
|
# def sign_consent_form(request, pk):
|
|
# """
|
|
# Sign a consent form.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# if not tenant:
|
|
# return JsonResponse({'error': 'No tenant found'}, status=400)
|
|
#
|
|
# consent_form = get_object_or_404(
|
|
# ConsentForm,
|
|
# pk=pk,
|
|
# patient__tenant=tenant
|
|
# )
|
|
#
|
|
# if request.method == 'POST':
|
|
# # Update consent form status
|
|
# consent_form.status = 'SIGNED'
|
|
# consent_form.signed_date = timezone.now()
|
|
# consent_form.signed_by = request.user
|
|
#
|
|
# # Add witness information if required
|
|
# if consent_form.template.requires_witness:
|
|
# consent_form.witness_name = request.POST.get('witness_name')
|
|
# consent_form.witness_signature = request.POST.get('witness_signature')
|
|
#
|
|
# consent_form.save()
|
|
#
|
|
# # Log consent form signing
|
|
# AuditLogger.log_event(
|
|
# tenant=tenant,
|
|
# event_type='UPDATE',
|
|
# event_category='PATIENT_MANAGEMENT',
|
|
# action='Sign Consent Form',
|
|
# description=f'Signed consent form: {consent_form.template.name} for {consent_form.patient}',
|
|
# user=request.user,
|
|
# content_object=consent_form,
|
|
# request=request
|
|
# )
|
|
#
|
|
# messages.success(request, f'Consent form "{consent_form.template.name}" signed successfully.')
|
|
# return JsonResponse({'status': 'signed'})
|
|
#
|
|
# return render(request, 'patients/partials/sign_consent_form.html', {
|
|
# 'consent_form': consent_form
|
|
# })
|
|
#
|
|
#
|
|
# @login_required
|
|
# def add_patient_note(request, patient_id):
|
|
# """
|
|
# HTMX view for adding a patient note.
|
|
# """
|
|
# patient = get_object_or_404(
|
|
# PatientProfile,
|
|
# pk=patient_id,
|
|
# tenant=getattr(request, 'tenant', None)
|
|
# )
|
|
#
|
|
# if request.method == 'POST':
|
|
# form = PatientNoteForm(request.POST, user=request.user)
|
|
# form.user = request.user # Set user for form.save() method
|
|
#
|
|
# if form.is_valid():
|
|
# note = form.save(commit=False)
|
|
# note.patient = patient
|
|
# note.created_by = request.user
|
|
# note.save()
|
|
#
|
|
# # Fetch the latest notes to refresh the list
|
|
# notes = PatientNote.objects.filter(
|
|
# patient=patient,
|
|
# is_active=True
|
|
# ).order_by('-created_at')
|
|
#
|
|
# return render(request, 'patients/partials/patient_notes_list.html', {
|
|
# 'notes': notes,
|
|
# 'patient': patient
|
|
# })
|
|
# else:
|
|
# # Return form with errors
|
|
# return render(request, 'patients/partials/add_patient_note_form.html', {
|
|
# 'form': form,
|
|
# 'patient': patient
|
|
# })
|
|
# else:
|
|
# # Initialize the form with patient pre-selected
|
|
# initial_data = {'patient': patient.pk}
|
|
# form = PatientNoteForm(initial=initial_data, user=request.user)
|
|
#
|
|
# return render(request, 'patients/partials/add_patient_note_form.html', {
|
|
# 'form': form,
|
|
# 'patient': patient
|
|
# })
|
|
#
|
|
#
|
|
# @login_required
|
|
# def patient_demographics_update(request, patient_id):
|
|
# """
|
|
# HTMX view for updating patient demographics.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# patient = get_object_or_404(PatientProfile, pk=patient_id, tenant=tenant)
|
|
#
|
|
# if request.method == 'POST':
|
|
# form = PatientProfileForm(request.POST, instance=patient)
|
|
# if form.is_valid():
|
|
# form.save()
|
|
#
|
|
# # Log the update
|
|
# from core.models import AuditLogEntry
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=tenant,
|
|
# user=request.user,
|
|
# action='UPDATE',
|
|
# entity_type='PatientProfile',
|
|
# entity_id=str(patient.patient_id),
|
|
# details={
|
|
# 'name': patient.get_full_name(),
|
|
# 'mrn': patient.mrn,
|
|
# 'fields_updated': list(form.changed_data)
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(request, 'Patient demographics updated successfully.')
|
|
# return JsonResponse({'status': 'success'})
|
|
# else:
|
|
# return JsonResponse({'status': 'error', 'errors': form.errors})
|
|
# else:
|
|
# form = PatientProfileForm(instance=patient)
|
|
#
|
|
# return render(request, 'patients/partials/demographics_form.html', {
|
|
# 'form': form,
|
|
# 'patient': patient
|
|
# })
|
|
#
|
|
#
|
|
# @login_required
|
|
# def verify_insurance(request, insurance_id):
|
|
# """
|
|
# HTMX view for verifying insurance information.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# insurance = get_object_or_404(InsuranceInfo, pk=insurance_id, patient__tenant=tenant)
|
|
#
|
|
# if request.method == 'POST':
|
|
# insurance.is_verified = True
|
|
# insurance.verification_date = timezone.now()
|
|
# insurance.verified_by = request.user
|
|
# insurance.save()
|
|
#
|
|
# # Log the verification
|
|
# from core.models import AuditLogEntry
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=tenant,
|
|
# user=request.user,
|
|
# action='UPDATE',
|
|
# entity_type='InsuranceInfo',
|
|
# entity_id=str(insurance.id),
|
|
# details={
|
|
# 'patient': insurance.patient.get_full_name(),
|
|
# 'mrn': insurance.patient.mrn,
|
|
# 'insurance': insurance.insurance_company,
|
|
# 'policy': insurance.policy_number,
|
|
# 'verification_date': str(insurance.verification_date)
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(request, 'Insurance information verified successfully.')
|
|
#
|
|
# # Fetch all insurance records for this patient to refresh the list
|
|
# insurance_records = InsuranceInfo.objects.filter(
|
|
# patient=insurance.patient
|
|
# ).order_by('-is_active', '-effective_date')
|
|
#
|
|
# return render(request, 'patients/partials/insurance_info_list.html', {
|
|
# 'insurance_records': insurance_records,
|
|
# 'patient': insurance.patient
|
|
# })
|
|
#
|
|
# return JsonResponse({'status': 'error', 'message': 'Invalid request method'})
|
|
#
|
|
#
|
|
# @login_required
|
|
# def emergency_contacts_list(request, patient_id):
|
|
# """
|
|
# HTMX view for listing emergency contacts.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# patient = get_object_or_404(PatientProfile, pk=patient_id, tenant=tenant)
|
|
#
|
|
# contacts = EmergencyContact.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-is_active', 'priority')
|
|
#
|
|
# # Handle form submission for adding a new contact
|
|
# if request.method == 'POST':
|
|
# form = EmergencyContactForm(request.POST, user=request.user)
|
|
# if form.is_valid():
|
|
# contact = form.save(commit=False)
|
|
# contact.patient = patient
|
|
# contact.save()
|
|
#
|
|
# # Log the addition
|
|
# from core.models import AuditLogEntry
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=tenant,
|
|
# user=request.user,
|
|
# action='CREATE',
|
|
# entity_type='EmergencyContact',
|
|
# entity_id=str(contact.id),
|
|
# details={
|
|
# 'patient': patient.get_full_name(),
|
|
# 'mrn': patient.mrn,
|
|
# 'contact': contact.get_full_name(),
|
|
# 'relationship': contact.relationship
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(request, 'Emergency contact added successfully.')
|
|
#
|
|
# # Refresh contacts list
|
|
# contacts = EmergencyContact.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-is_active', 'priority')
|
|
#
|
|
# return render(request, 'patients/partials/emergency_contacts_list.html', {
|
|
# 'contacts': contacts,
|
|
# 'patient': patient
|
|
# })
|
|
# else:
|
|
# # Return form with errors
|
|
# return render(request, 'patients/partials/emergency_contact_form.html', {
|
|
# 'form': form,
|
|
# 'patient': patient
|
|
# })
|
|
# else:
|
|
# # Initial form for adding new contact
|
|
# initial_data = {'patient': patient.pk}
|
|
# form = EmergencyContactForm(initial=initial_data, user=request.user)
|
|
#
|
|
# return render(request, 'patients/partials/emergency_contacts_list.html', {
|
|
# 'contacts': contacts,
|
|
# 'patient': patient,
|
|
# 'form': form
|
|
# })
|
|
#
|
|
#
|
|
# @login_required
|
|
# def insurance_info_list(request, patient_id):
|
|
# """
|
|
# HTMX view for listing insurance information.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# patient = get_object_or_404(PatientProfile, pk=patient_id, tenant=tenant)
|
|
#
|
|
# insurance_records = InsuranceInfo.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-is_active', '-effective_date')
|
|
#
|
|
# # Handle form submission for adding new insurance
|
|
# if request.method == 'POST':
|
|
# form = InsuranceInfoForm(request.POST, user=request.user)
|
|
# if form.is_valid():
|
|
# insurance = form.save(commit=False)
|
|
# insurance.patient = patient
|
|
# insurance.save()
|
|
#
|
|
# # Log the addition
|
|
# from core.models import AuditLogEntry
|
|
# AuditLogEntry.objects.create(
|
|
# tenant=tenant,
|
|
# user=request.user,
|
|
# action='CREATE',
|
|
# entity_type='InsuranceInfo',
|
|
# entity_id=str(insurance.id),
|
|
# details={
|
|
# 'patient': patient.get_full_name(),
|
|
# 'mrn': patient.mrn,
|
|
# 'insurance': insurance.insurance_company,
|
|
# 'policy': insurance.policy_number
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(request, 'Insurance information added successfully.')
|
|
#
|
|
# # Refresh insurance list
|
|
# insurance_records = InsuranceInfo.objects.filter(
|
|
# patient=patient
|
|
# ).order_by('-is_active', '-effective_date')
|
|
#
|
|
# return render(request, 'patients/partials/insurance_info_list.html', {
|
|
# 'insurance_records': insurance_records,
|
|
# 'patient': patient
|
|
# })
|
|
# else:
|
|
# # Return form with errors
|
|
# return render(request, 'patients/partials/insurance_info_form.html', {
|
|
# 'form': form,
|
|
# 'patient': patient
|
|
# })
|
|
# else:
|
|
# # Initial form for adding new insurance
|
|
# initial_data = {'patient': patient.pk}
|
|
# form = InsuranceInfoForm(initial=initial_data, user=request.user)
|
|
#
|
|
# return render(request, 'patients/partials/insurance_info_list.html', {
|
|
# 'insurance_records': insurance_records,
|
|
# 'patient': patient,
|
|
# 'form': form
|
|
# })
|
|
#
|
|
#
|
|
# @login_required
|
|
# def patient_notes_list(request, patient_id):
|
|
# """
|
|
# HTMX view for listing patient notes.
|
|
# """
|
|
# tenant = getattr(request, 'tenant', None)
|
|
# patient = get_object_or_404(PatientProfile, pk=patient_id, tenant=tenant)
|
|
#
|
|
# notes = PatientNote.objects.filter(
|
|
# patient=patient,
|
|
# is_active=True
|
|
# ).order_by('-created_at')
|
|
#
|
|
# return render(request, 'patients/partials/patient_notes_list.html', {
|
|
# 'notes': notes,
|
|
# 'patient': patient
|
|
# })
|
|
#
|
|
#
|
|
#
|