2025-08-12 13:33:25 +03:00

3101 lines
109 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
})
#
# """
# 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
# })
#
#
#