Marwan Alwali be70e47e22 update
2025-08-30 09:45:26 +03:00

1613 lines
57 KiB
Python

"""
Views for EMR app.
"""
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
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.http import JsonResponse, HttpResponse
from django.db.models import Q, Count, Avg, F, Case, When, IntegerField, Max
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from django.contrib import messages
from django.core.paginator import Paginator
from django.template.loader import render_to_string
from datetime import datetime, timedelta, date
import json
from django.contrib.messages.views import SuccessMessageMixin
from core.models import AuditLogEntry
from core.utils import AuditLogger
from patients.models import PatientProfile
from .models import *
from .forms import *
from core.mixins import TenantMixin, FormKwargsMixin
class EMRDashboardView(LoginRequiredMixin, ListView):
"""
Main dashboard for EMR management.
"""
template_name = 'emr/dashboard.html'
context_object_name = 'encounters'
def get_queryset(self):
"""Get recent encounters for current tenant."""
return Encounter.objects.filter(
tenant=self.request.user.tenant
).select_related('patient', 'provider').order_by('-start_datetime')[:10]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
tenant = self.request.user.tenant
today = timezone.now().date()
# Dashboard statistics
context.update({
'total_encounters': Encounter.objects.filter(tenant=tenant).count(),
'active_encounters': Encounter.objects.filter(
tenant=tenant,
status__in=['ARRIVED', 'TRIAGED', 'IN_PROGRESS', 'ON_HOLD']
).count(),
'todays_encounters': Encounter.objects.filter(
tenant=tenant,
start_datetime__date=today
).count(),
'pending_documentation': Encounter.objects.filter(
tenant=tenant,
documentation_complete=False,
status='FINISHED'
).count(),
'unsigned_notes': ClinicalNote.objects.filter(
patient__tenant=tenant,
status='COMPLETED',
electronically_signed=False
).count(),
'active_problems': ProblemList.objects.filter(
tenant=tenant,
status='ACTIVE'
).count(),
'active_care_plans': CarePlan.objects.filter(
tenant=tenant,
status='ACTIVE'
).count(),
'critical_vitals': VitalSigns.objects.filter(
patient__tenant=tenant,
measured_datetime__date=today
).exclude(critical_values=[]).count(),
})
return context
class EncounterListView(LoginRequiredMixin, ListView):
"""
List view for encounters.
"""
model = Encounter
template_name = 'emr/encounter_list.html'
context_object_name = 'encounters'
paginate_by = 25
def get_queryset(self):
queryset = Encounter.objects.filter(tenant=self.request.user.tenant)
# Search functionality
search = self.request.GET.get('search')
if search:
queryset = queryset.filter(
Q(patient__first_name__icontains=search) |
Q(patient__last_name__icontains=search) |
Q(patient__mrn__icontains=search) |
Q(provider__first_name__icontains=search) |
Q(provider__last_name__icontains=search) |
Q(chief_complaint__icontains=search)
)
# Filter by encounter type
encounter_type = self.request.GET.get('encounter_type')
if encounter_type:
queryset = queryset.filter(encounter_type=encounter_type)
# Filter by status
status = self.request.GET.get('status')
if status:
queryset = queryset.filter(status=status)
# Filter by provider
provider_id = self.request.GET.get('provider')
if provider_id:
queryset = queryset.filter(provider_id=provider_id)
# Filter by date range
date_from = self.request.GET.get('date_from')
date_to = self.request.GET.get('date_to')
if date_from:
queryset = queryset.filter(start_datetime__date__gte=date_from)
if date_to:
queryset = queryset.filter(start_datetime__date__lte=date_to)
return queryset.select_related('patient', 'provider').order_by('-start_datetime')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'encounter_types': Encounter._meta.get_field('encounter_type').choices,
'encounter_statuses': Encounter._meta.get_field('status').choices,
})
return context
class EncounterCreateView(LoginRequiredMixin, CreateView):
model = Encounter
form_class = EncounterForm
template_name = 'emr/encounter_create.html'
success_message = _('Encounter for %(patient)s created successfully.')
def form_valid(self, form):
form.instance.tenant = self.request.user.tenant
response = super().form_valid(form)
AuditLogEntry.objects.create(
tenant=self.request.user.tenant,
user=self.request.user,
action='CREATE',
model_name='Encounter',
object_id=str(self.object.pk),
changes={'status': 'Encounter created'}
)
return response
def get_success_url(self):
return reverse_lazy('emr:encounter_detail', kwargs={'pk': self.object.pk})
class EncounterDetailView(LoginRequiredMixin, DetailView):
"""
Detail view for encounter.
"""
model = Encounter
template_name = 'emr/encounter_detail.html'
context_object_name = 'encounter'
def get_queryset(self):
return Encounter.objects.filter(tenant=self.request.user.tenant)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
encounter = self.object
# Get related data
context.update({
'vital_signs': encounter.vital_signs.all().order_by('-measured_datetime'),
'clinical_notes': encounter.clinical_notes.all().order_by('-note_datetime'),
'problems': encounter.problems_identified.all().order_by('-created_at'),
})
return context
class VitalSignsListView(LoginRequiredMixin, ListView):
"""
List view for vital signs.
"""
model = VitalSigns
template_name = 'emr/vital_signs_list.html'
context_object_name = 'vital_signs'
paginate_by = 25
def get_queryset(self):
queryset = VitalSigns.objects.filter(patient__tenant=self.request.user.tenant)
# Filter by patient
patient_id = self.request.GET.get('patient')
if patient_id:
queryset = queryset.filter(patient_id=patient_id)
# Filter by encounter
encounter_id = self.request.GET.get('encounter')
if encounter_id:
queryset = queryset.filter(encounter_id=encounter_id)
# Filter by date range
date_from = self.request.GET.get('date_from')
date_to = self.request.GET.get('date_to')
if date_from:
queryset = queryset.filter(measured_datetime__date__gte=date_from)
if date_to:
queryset = queryset.filter(measured_datetime__date__lte=date_to)
# Search functionality
search = self.request.GET.get('search')
if search:
queryset = queryset.filter(
Q(patient__first_name__icontains=search) |
Q(patient__last_name__icontains=search) |
Q(patient__mrn__icontains=search)
)
return queryset.select_related('patient', 'encounter', 'measured_by').order_by('-measured_datetime')
class ProblemListView(LoginRequiredMixin, ListView):
"""
List view for problem list.
"""
model = ProblemList
template_name = 'emr/problem_list.html'
context_object_name = 'problems'
paginate_by = 25
def get_queryset(self):
queryset = ProblemList.objects.filter(tenant=self.request.user.tenant)
# Filter by patient
patient_id = self.request.GET.get('patient')
if patient_id:
queryset = queryset.filter(patient_id=patient_id)
# Filter by status
status = self.request.GET.get('status')
if status:
queryset = queryset.filter(status=status)
# Filter by problem type
problem_type = self.request.GET.get('problem_type')
if problem_type:
queryset = queryset.filter(problem_type=problem_type)
# Filter by priority
priority = self.request.GET.get('priority')
if priority:
queryset = queryset.filter(priority=priority)
# Search functionality
search = self.request.GET.get('search')
if search:
queryset = queryset.filter(
Q(patient__first_name__icontains=search) |
Q(patient__last_name__icontains=search) |
Q(patient__mrn__icontains=search) |
Q(problem_name__icontains=search) |
Q(problem_code__icontains=search)
)
return queryset.select_related(
'patient', 'diagnosing_provider', 'managing_provider'
).order_by('-created_at')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'problem_types': ProblemList._meta.get_field('problem_type').choices,
'problem_statuses': ProblemList._meta.get_field('status').choices,
'priorities': ProblemList._meta.get_field('priority').choices,
})
return context
class CarePlanListView(LoginRequiredMixin, ListView):
"""
List view for care plans.
"""
model = CarePlan
template_name = 'emr/care_plan_list.html'
context_object_name = 'care_plans'
paginate_by = 25
def get_queryset(self):
queryset = CarePlan.objects.filter(tenant=self.request.user.tenant)
# Filter by patient
patient_id = self.request.GET.get('patient')
if patient_id:
queryset = queryset.filter(patient_id=patient_id)
# Filter by status
status = self.request.GET.get('status')
if status:
queryset = queryset.filter(status=status)
# Filter by plan type
plan_type = self.request.GET.get('plan_type')
if plan_type:
queryset = queryset.filter(plan_type=plan_type)
# Filter by provider
provider_id = self.request.GET.get('provider')
if provider_id:
queryset = queryset.filter(primary_provider_id=provider_id)
# Search functionality
search = self.request.GET.get('search')
if search:
queryset = queryset.filter(
Q(patient__first_name__icontains=search) |
Q(patient__last_name__icontains=search) |
Q(patient__mrn__icontains=search) |
Q(title__icontains=search) |
Q(description__icontains=search)
)
return queryset.select_related('patient', 'primary_provider').order_by('-created_at')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'plan_types': CarePlan._meta.get_field('plan_type').choices,
'plan_statuses': CarePlan._meta.get_field('status').choices,
})
return context
class ClinicalNoteListView(LoginRequiredMixin, ListView):
"""
List view for clinical notes.
"""
model = ClinicalNote
template_name = 'emr/clinical_note_list.html'
context_object_name = 'notes'
paginate_by = 25
def get_queryset(self):
queryset = ClinicalNote.objects.filter(patient__tenant=self.request.user.tenant)
# Filter by patient
patient_id = self.request.GET.get('patient')
if patient_id:
queryset = queryset.filter(patient_id=patient_id)
# Filter by note type
note_type = self.request.GET.get('note_type')
if note_type:
queryset = queryset.filter(note_type=note_type)
# Filter by status
status = self.request.GET.get('status')
if status:
queryset = queryset.filter(status=status)
# Filter by author
author_id = self.request.GET.get('author')
if author_id:
queryset = queryset.filter(author_id=author_id)
# Search functionality
search = self.request.GET.get('search')
if search:
queryset = queryset.filter(
Q(patient__first_name__icontains=search) |
Q(patient__last_name__icontains=search) |
Q(patient__mrn__icontains=search) |
Q(title__icontains=search) |
Q(content__icontains=search)
)
return queryset.select_related('patient', 'encounter', 'author').order_by('-note_datetime')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'note_types': ClinicalNote._meta.get_field('note_type').choices,
'note_statuses': ClinicalNote._meta.get_field('status').choices,
})
return context
class ClinicalNoteDetailView(LoginRequiredMixin, TenantMixin, DetailView):
model = ClinicalNote
template_name = 'emr/clinical_note_detail.html'
context_object_name = 'note'
class ClinicalNoteCreateView(
LoginRequiredMixin, FormKwargsMixin,
SuccessMessageMixin, CreateView
):
model = ClinicalNote
form_class = ClinicalNoteForm
template_name = 'emr/clinical_note_form.html'
success_message = _('Clinical note created successfully.')
def form_valid(self, form):
form.instance.author = self.request.user
response = super().form_valid(form)
AuditLogEntry.objects.create(
tenant=self.request.user.tenant,
user=self.request.user,
action='CREATE',
model_name='ClinicalNote',
object_id=str(self.object.pk),
changes={'status': 'Clinical note created'}
)
return response
def get_success_url(self):
return reverse_lazy('emr:clinical_note_detail', kwargs={'pk': self.object.pk})
class ClinicalNoteUpdateView(
LoginRequiredMixin, FormKwargsMixin,
SuccessMessageMixin, TenantMixin, UpdateView
):
model = ClinicalNote
form_class = ClinicalNoteForm
template_name = 'emr/clinical_note_form.html'
success_message = _('Clinical note updated successfully.')
def form_valid(self, form):
response = super().form_valid(form)
AuditLogEntry.objects.create(
tenant=self.request.user.tenant,
user=self.request.user,
action='UPDATE',
model_name='ClinicalNote',
object_id=str(self.object.pk),
changes={'status': 'Clinical note updated'}
)
return response
def get_success_url(self):
return reverse_lazy('emr:clinical_note_detail', kwargs={'pk': self.object.pk})
class ClinicalNoteDeleteView(
LoginRequiredMixin, TenantMixin,
SuccessMessageMixin, DeleteView
):
model = ClinicalNote
template_name = 'emr/clinical_notes/clinical_note_confirm_delete.html'
success_url = reverse_lazy('emr:clinical_note_list')
success_message = _('Clinical note deleted successfully.')
def delete(self, request, *args, **kwargs):
cn = self.get_object()
AuditLogEntry.objects.create(
tenant=request.user.tenant,
user=request.user,
action='DELETE',
model_name='ClinicalNote',
object_id=str(cn.pk),
changes={'status': 'Clinical note deleted'}
)
messages.success(request, self.success_message)
return super().delete(request, *args, **kwargs)
# HTMX Views for real-time updates
@login_required
def emr_stats(request):
"""
HTMX endpoint for EMR statistics.
"""
tenant = request.user.tenant
today = timezone.now().date()
stats = {
'active_encounters': Encounter.objects.filter(
tenant=tenant,
status__in=['ARRIVED', 'TRIAGED', 'IN_PROGRESS', 'ON_HOLD']
).count(),
'pending_documentation': Encounter.objects.filter(
tenant=tenant,
documentation_complete=False,
status='FINISHED'
).count(),
'unsigned_notes': ClinicalNote.objects.filter(
patient__tenant=tenant,
status='COMPLETED',
electronically_signed=False
).count(),
'critical_vitals': VitalSigns.objects.filter(
patient__tenant=tenant,
measured_datetime__date=today
).exclude(critical_values=[]).count(),
}
return render(request, 'emr/partials/emr_stats.html', {'stats': stats})
@login_required
def encounter_search(request):
"""
HTMX endpoint for encounter search.
"""
search = request.GET.get('search', '')
status = request.GET.get('status', '')
encounter_type = request.GET.get('encounter_type', '')
queryset = Encounter.objects.filter(tenant=request.user.tenant)
if search:
queryset = queryset.filter(
Q(patient__first_name__icontains=search) |
Q(patient__last_name__icontains=search) |
Q(patient__mrn__icontains=search) |
Q(chief_complaint__icontains=search)
)
if status:
queryset = queryset.filter(status=status)
if encounter_type:
queryset = queryset.filter(encounter_type=encounter_type)
encounters = queryset.select_related('patient', 'provider').order_by('-start_datetime')[:20]
return render(request, 'emr/partials/encounter_list.html', {'encounters': encounters})
@login_required
def vital_signs_chart(request, patient_id):
"""
HTMX endpoint for vital signs chart.
"""
patient = get_object_or_404(
PatientProfile,
id=patient_id,
tenant=request.user.tenant
)
# Get recent vital signs
vital_signs = VitalSigns.objects.filter(
patient=patient
).order_by('-measured_datetime')[:10]
return render(request, 'emr/partials/vital_signs_chart.html', {
'patient': patient,
'vital_signs': vital_signs
})
@login_required
def problem_list_patient(request, patient_id):
"""
HTMX endpoint for patient problem list.
"""
patient = get_object_or_404(
PatientProfile,
id=patient_id,
tenant=request.user.tenant
)
problems = ProblemList.objects.filter(
patient=patient
).select_related('diagnosing_provider').order_by('-created_at')
return render(request, 'emr/partials/problem_list.html', {
'patient': patient,
'problems': problems
})
@login_required
def add_vital_signs(request, encounter_id):
"""
HTMX endpoint for adding vital signs.
"""
if request.method == 'POST':
encounter = get_object_or_404(
Encounter,
id=encounter_id,
tenant=request.user.tenant
)
# Extract vital signs data
vital_signs_data = {
'encounter': encounter,
'patient': encounter.patient,
'measured_by': request.user,
'measured_datetime': timezone.now(),
}
# Add measurements if provided
if request.POST.get('temperature'):
vital_signs_data['temperature'] = float(request.POST.get('temperature'))
vital_signs_data['temperature_method'] = request.POST.get('temperature_method', 'ORAL')
if request.POST.get('systolic_bp') and request.POST.get('diastolic_bp'):
vital_signs_data['systolic_bp'] = int(request.POST.get('systolic_bp'))
vital_signs_data['diastolic_bp'] = int(request.POST.get('diastolic_bp'))
vital_signs_data['bp_position'] = request.POST.get('bp_position', 'SITTING')
if request.POST.get('heart_rate'):
vital_signs_data['heart_rate'] = int(request.POST.get('heart_rate'))
vital_signs_data['heart_rhythm'] = request.POST.get('heart_rhythm', 'REGULAR')
if request.POST.get('respiratory_rate'):
vital_signs_data['respiratory_rate'] = int(request.POST.get('respiratory_rate'))
if request.POST.get('oxygen_saturation'):
vital_signs_data['oxygen_saturation'] = int(request.POST.get('oxygen_saturation'))
vital_signs_data['oxygen_delivery'] = request.POST.get('oxygen_delivery', 'ROOM_AIR')
if request.POST.get('pain_scale'):
vital_signs_data['pain_scale'] = int(request.POST.get('pain_scale'))
vital_signs_data['pain_location'] = request.POST.get('pain_location', '')
if request.POST.get('weight'):
vital_signs_data['weight'] = float(request.POST.get('weight'))
if request.POST.get('height'):
vital_signs_data['height'] = float(request.POST.get('height'))
# Create vital signs record
vital_signs = VitalSigns.objects.create(**vital_signs_data)
# Log the action
AuditLogger.log_event(
user=request.user,
action='VITAL_SIGNS_RECORDED',
model='VitalSigns',
object_id=vital_signs.id,
details=f"Vital signs recorded for {encounter.patient.get_full_name()}"
)
messages.success(request, 'Vital signs recorded successfully')
return JsonResponse({'success': True, 'vital_signs_id': vital_signs.id})
return JsonResponse({'error': 'Invalid request'}, status=400)
@login_required
def add_problem(request, patient_id):
"""
HTMX endpoint for adding a problem.
"""
if request.method == 'POST':
patient = get_object_or_404(
PatientProfile,
id=patient_id,
tenant=request.user.tenant
)
problem_data = {
'tenant': request.user.tenant,
'patient': patient,
'problem_name': request.POST.get('problem_name'),
'problem_type': request.POST.get('problem_type', 'DIAGNOSIS'),
'severity': request.POST.get('severity'),
'priority': request.POST.get('priority', 'MEDIUM'),
'diagnosing_provider': request.user,
'clinical_notes': request.POST.get('clinical_notes', ''),
}
# Add optional fields
if request.POST.get('problem_code'):
problem_data['problem_code'] = request.POST.get('problem_code')
problem_data['coding_system'] = request.POST.get('coding_system', 'ICD10')
if request.POST.get('onset_date'):
problem_data['onset_date'] = request.POST.get('onset_date')
# Create problem
problem = ProblemList.objects.create(**problem_data)
# Log the action
AuditLogger.log_event(
user=request.user,
action='PROBLEM_ADDED',
model='ProblemList',
object_id=problem.id,
details=f"Problem added for {patient.get_full_name()}: {problem.problem_name}"
)
messages.success(request, f'Problem "{problem.problem_name}" added successfully')
return JsonResponse({'success': True, 'problem_id': problem.id})
return JsonResponse({'error': 'Invalid request'}, status=400)
@login_required
def update_encounter_status(request, encounter_id):
"""
HTMX endpoint for updating encounter status.
"""
if request.method == 'POST':
encounter = get_object_or_404(
Encounter,
id=encounter_id,
tenant=request.user.tenant
)
new_status = request.POST.get('status')
if new_status in dict(Encounter._meta.get_field('status').choices):
old_status = encounter.status
encounter.status = new_status
# Handle status-specific logic
if new_status == 'FINISHED':
encounter.end_datetime = timezone.now()
elif new_status == 'IN_PROGRESS' and not encounter.end_datetime:
encounter.end_datetime = None
encounter.save()
# Log the action
AuditLogger.log_event(
user=request.user,
action='ENCOUNTER_STATUS_UPDATED',
model='Encounter',
object_id=encounter.id,
details=f"Encounter status changed from {old_status} to {new_status}"
)
return JsonResponse({
'success': True,
'new_status': encounter.get_status_display(),
'status_class': get_status_class(new_status)
})
return JsonResponse({'error': 'Invalid request'}, status=400)
@login_required
def sign_note(request, note_id):
"""
HTMX endpoint for signing a clinical note.
"""
if request.method == 'POST':
note = get_object_or_404(
ClinicalNote,
id=note_id,
patient__tenant=request.user.tenant,
author=request.user,
status='COMPLETED'
)
note.status = 'SIGNED'
note.electronically_signed = True
note.signed_datetime = timezone.now()
note.signature_method = 'ELECTRONIC'
note.save()
# Log the action
AuditLogger.log_event(
user=request.user,
action='NOTE_SIGNED',
model='ClinicalNote',
object_id=note.id,
details=f"Clinical note signed: {note.title}"
)
messages.success(request, f'Note "{note.title}" signed successfully')
return JsonResponse({'success': True})
return JsonResponse({'error': 'Invalid request'}, status=400)
def get_status_class(status):
"""
Get CSS class for status display.
"""
status_classes = {
'PLANNED': 'secondary',
'ARRIVED': 'info',
'TRIAGED': 'warning',
'IN_PROGRESS': 'primary',
'ON_HOLD': 'warning',
'FINISHED': 'success',
'CANCELLED': 'danger',
'ENTERED_IN_ERROR': 'danger',
'UNKNOWN': 'secondary',
}
return status_classes.get(status, 'secondary')
#
# # encounters/views.py
#
# from django.shortcuts import get_object_or_404, redirect, render
# from django.urls import reverse_lazy
# from django.contrib.auth.decorators import login_required
# from django.contrib.auth.mixins import LoginRequiredMixin
# from django.contrib import messages
# from django.views.generic import (
# TemplateView, ListView, DetailView, CreateView, UpdateView, DeleteView
# )
# from django.views.generic.edit import FormMixin
# from django.contrib.messages.views import SuccessMessageMixin
# from django.db.models import Q, Avg
# from django.utils import timezone
# from django.http import JsonResponse
#
# from .models import *
# from .forms import *
# from core.models import AuditLogEntry
# from patients.models import PatientProfile
# from django.utils.translation import gettext_lazy as _
#
#
# # Mixins -------------------------------------------------------------------------
#
# class TenantMixin:
# def get_queryset(self):
# qs = super().get_queryset()
# tenant = getattr(self.request.user, 'tenant', None)
# if tenant and not self.request.user.is_superuser:
# # Models with patient FK:
# if hasattr(qs.model, 'patient'):
# return qs.filter(patient__tenant=tenant)
# # NoteTemplate uses tenant directly:
# return qs.filter(tenant=tenant)
# return qs
#
# def get_object(self, queryset=None):
# qs = queryset or self.get_queryset()
# return super().get_object(qs)
#
#
# class FormKwargsMixin:
# def get_form_kwargs(self):
# kw = super().get_form_kwargs()
# kw['user'] = self.request.user
# kw['tenant'] = getattr(self.request.user, 'tenant', None)
# return kw
#
#
# # Dashboard ----------------------------------------------------------------------
#
# class DashboardView(LoginRequiredMixin, TemplateView):
# template_name = 'emr/dashboard.html'
#
# def get_context_data(self, **kwargs):
# ctx = super().get_context_data(**kwargs)
# tenant = getattr(self.request.user, 'tenant', None)
# today = timezone.now().date()
# week_ago = today - timezone.timedelta(days=7)
#
# enc = Encounter.objects.filter(patient__tenant=tenant)
# vs = VitalSigns.objects.filter(encounter__patient__tenant=tenant)
# pb = ProblemList.objects.filter(patient__tenant=tenant)
# cp = CarePlan.objects.filter(patient__tenant=tenant)
# cn = ClinicalNote.objects.filter(encounter__patient__tenant=tenant)
#
# ctx.update({
# 'total_encounters': enc.count(),
# 'encounters_today': enc.filter(scheduled_datetime__date=today).count(),
# 'encounters_this_week': enc.filter(scheduled_datetime__date__gte=week_ago).count(),
# 'active_encounters': enc.filter(status='IN_PROGRESS').count(),
#
# 'total_vital_signs': vs.count(),
# 'vital_signs_today': vs.filter(recorded_at__date=today).count(),
# 'avg_temp_week': vs.filter(recorded_at__date__gte=week_ago).aggregate(Avg('temperature'))['temperature__avg'],
# 'avg_hr_week': vs.filter(recorded_at__date__gte=week_ago).aggregate(Avg('heart_rate'))['heart_rate__avg'],
#
# 'total_problems': pb.count(),
# 'active_problems': pb.filter(status='ACTIVE').count(),
# 'resolved_problems': pb.filter(status='RESOLVED').count(),
#
# 'total_care_plans': cp.count(),
# 'active_care_plans': cp.filter(status='ACTIVE').count(),
# 'completed_care_plans': cp.filter(status='COMPLETED').count(),
#
# 'total_notes': cn.count(),
# 'notes_today': cn.filter(created_at__date=today).count(),
#
# 'recent_encounters': enc.select_related('patient','provider').order_by('-scheduled_datetime')[:5],
# 'recent_vitals': vs.select_related('encounter','recorded_by').order_by('-recorded_at')[:5],
# 'recent_notes': cn.select_related('encounter','author').order_by('-created_at')[:5],
# })
# return ctx
#
#
# # Encounter ----------------------------------------------------------------------
#
# class EncounterListView(LoginRequiredMixin, TenantMixin, FormMixin, ListView):
# model = Encounter
# template_name = 'emr/encounter_list.html'
# context_object_name = 'encounters'
# paginate_by = 20
# form_class = EMRSearchForm
#
# def get_queryset(self):
# qs = super().get_queryset().select_related('patient','provider').order_by('-scheduled_datetime')
# if self.request.GET:
# form = self.get_form()
# if form.is_valid():
# cd = form.cleaned_data
# if cd.get('search'):
# qs = qs.filter(
# Q(patient__first_name__icontains=cd['search']) |
# Q(patient__last_name__icontains=cd['search']) |
# Q(chief_complaint__icontains=cd['search'])
# )
# for fld in ('patient','provider','encounter_type','status'):
# if cd.get(fld):
# qs = qs.filter(**{fld: cd[fld]})
# if cd.get('date_from'):
# qs = qs.filter(scheduled_datetime__date__gte=cd['date_from'])
# if cd.get('date_to'):
# qs = qs.filter(scheduled_datetime__date__lte=cd['date_to'])
# return qs
#
# def get_context_data(self, **kwargs):
# ctx = super().get_context_data(**kwargs)
# ctx['search_form'] = self.get_form()
# ctx['total_count'] = self.get_queryset().count()
# return ctx
#
#
# class EncounterDetailView(LoginRequiredMixin, TenantMixin, DetailView):
# model = Encounter
# template_name = 'emr/encounter_detail.html'
# context_object_name = 'encounter'
#
# def get_context_data(self, **kwargs):
# ctx = super().get_context_data(**kwargs)
# enc = self.object
# ctx.update({
# 'vital_signs': VitalSigns.objects.filter(encounter=enc).order_by('-recorded_at'),
# 'clinical_notes': ClinicalNote.objects.filter(encounter=enc).order_by('-created_at'),
# 'problems': ProblemList.objects.filter(patient=enc.patient, status='ACTIVE'),
# 'care_plans': CarePlan.objects.filter(patient=enc.patient, status='ACTIVE'),
# })
# return ctx
#
#
# class EncounterCreateView(
# LoginRequiredMixin, FormKwargsMixin,
# SuccessMessageMixin, CreateView
# ):
# model = Encounter
# form_class = EncounterForm
# template_name = 'emr/encounter_form.html'
# success_message = _('Encounter for %(patient)s created successfully.')
#
# def form_valid(self, form):
# form.instance.tenant = self.request.user.tenant
# response = super().form_valid(form)
# AuditLogEntry.objects.create(
# tenant=self.request.user.tenant,
# user=self.request.user,
# action='CREATE',
# model_name='Encounter',
# object_id=str(self.object.pk),
# changes={'status': 'Encounter created'}
# )
# return response
#
# def get_success_url(self):
# return reverse_lazy('emr:encounter_detail', kwargs={'pk': self.object.pk})
#
#
# class EncounterUpdateView(
# LoginRequiredMixin, FormKwargsMixin,
# SuccessMessageMixin, TenantMixin, UpdateView
# ):
# model = Encounter
# form_class = EncounterForm
# template_name = 'emr/encounter_form.html'
# success_message = _('Encounter for %(patient)s updated successfully.')
#
# def dispatch(self, request, *args, **kwargs):
# enc = self.get_object()
# if enc.status == 'COMPLETED' and not request.user.is_superuser:
# messages.error(request, _('Cannot modify a completed encounter.'))
# return redirect('emr:encounter_detail', pk=enc.pk)
# return super().dispatch(request, *args, **kwargs)
#
# def form_valid(self, form):
# response = super().form_valid(form)
# AuditLogEntry.objects.create(
# tenant=self.request.user.tenant,
# user=self.request.user,
# action='UPDATE',
# model_name='Encounter',
# object_id=str(self.object.pk),
# changes={'status': 'Encounter updated'}
# )
# return response
#
# def get_success_url(self):
# return reverse_lazy('emr:encounter_detail', kwargs={'pk': self.object.pk})
#
#
# class EncounterDeleteView(
# LoginRequiredMixin, TenantMixin,
# SuccessMessageMixin, DeleteView
# ):
# model = Encounter
# template_name = 'emr/encounter_confirm_delete.html'
# success_url = reverse_lazy('emr:encounter_list')
# success_message = _('Encounter deleted successfully.')
#
# def delete(self, request, *args, **kwargs):
# enc = self.get_object()
# AuditLogEntry.objects.create(
# tenant=request.user.tenant,
# user=request.user,
# action='DELETE',
# model_name='Encounter',
# object_id=str(enc.pk),
# changes={'status': 'Encounter deleted'}
# )
# messages.success(request, self.success_message)
# return super().delete(request, *args, **kwargs)
#
#
# # VitalSigns ----------------------------------------------------------------------
#
# class VitalSignsListView(LoginRequiredMixin, TenantMixin, ListView):
# model = VitalSigns
# template_name = 'emr/vital_signs_list.html'
# context_object_name = 'vital_signs'
# paginate_by = 20
#
# def get_queryset(self):
# qs = super().get_queryset().select_related('encounter','recorded_by').order_by('-recorded_at')
# # (Search/filter logic would use a VitalSignsSearchForm — omitted for brevity)
# return qs
#
#
# class VitalSignsDetailView(LoginRequiredMixin, TenantMixin, DetailView):
# model = VitalSigns
# template_name = 'emr/vital_signs_detail.html'
# context_object_name = 'vital_signs'
#
#
# class VitalSignsCreateView(
# LoginRequiredMixin, FormKwargsMixin,
# SuccessMessageMixin, CreateView
# ):
# model = VitalSigns
# form_class = VitalSignsForm
# template_name = 'emr/vital_signs_form.html'
# success_message = _('Vital signs recorded successfully.')
#
# def form_valid(self, form):
# form.instance.recorded_by = self.request.user
# response = super().form_valid(form)
# AuditLogEntry.objects.create(
# tenant=self.request.user.tenant,
# user=self.request.user,
# action='CREATE',
# model_name='VitalSigns',
# object_id=str(self.object.pk),
# changes={'status': 'Vital signs recorded'}
# )
# return response
#
# def get_success_url(self):
# return reverse_lazy('emr:vital_signs_detail', kwargs={'pk': self.object.pk})
#
#
# # ProblemList ---------------------------------------------------------------------
#
# class ProblemListListView(LoginRequiredMixin, TenantMixin, ListView):
# model = ProblemList
# template_name = 'emr/problem_list.html'
# context_object_name = 'problems'
# paginate_by = 20
#
# def get_queryset(self):
# return super().get_queryset().select_related('patient','identified_by').order_by('-created_at')
#
#
# class ProblemListDetailView(LoginRequiredMixin, TenantMixin, DetailView):
# model = ProblemList
# template_name = 'emr/problem_detail.html'
# context_object_name = 'problem'
#
#
# class ProblemListCreateView(
# LoginRequiredMixin, FormKwargsMixin,
# SuccessMessageMixin, CreateView
# ):
# model = ProblemList
# form_class = ProblemListForm
# template_name = 'emr/problem_form.html'
# success_message = _('Problem added successfully.')
#
# def form_valid(self, form):
# form.instance.identified_by = self.request.user
# response = super().form_valid(form)
# AuditLogEntry.objects.create(
# tenant=self.request.user.tenant,
# user=self.request.user,
# action='CREATE',
# model_name='ProblemList',
# object_id=str(self.object.pk),
# changes={'status': 'Problem added'}
# )
# return response
#
# def get_success_url(self):
# return reverse_lazy('emr:problem_detail', kwargs={'pk': self.object.pk})
#
#
# class ProblemListUpdateView(
# LoginRequiredMixin, FormKwargsMixin,
# SuccessMessageMixin, TenantMixin, UpdateView
# ):
# model = ProblemList
# form_class = ProblemListForm
# template_name = 'emr/problem_form.html'
# success_message = _('Problem updated successfully.')
#
# def form_valid(self, form):
# response = super().form_valid(form)
# AuditLogEntry.objects.create(
# tenant=self.request.user.tenant,
# user=self.request.user,
# action='UPDATE',
# model_name='ProblemList',
# object_id=str(self.object.pk),
# changes={'status': 'Problem updated'}
# )
# return response
#
# def get_success_url(self):
# return reverse_lazy('emr:problem_detail', kwargs={'pk': self.object.pk})
#
#
# class ProblemListDeleteView(
# LoginRequiredMixin, TenantMixin,
# SuccessMessageMixin, DeleteView
# ):
# model = ProblemList
# template_name = 'emr/problem_confirm_delete.html'
# success_url = reverse_lazy('emr:problem_list')
# success_message = _('Problem deleted successfully.')
#
# def delete(self, request, *args, **kwargs):
# prob = self.get_object()
# AuditLogEntry.objects.create(
# tenant=request.user.tenant,
# user=request.user,
# action='DELETE',
# model_name='ProblemList',
# object_id=str(prob.pk),
# changes={'status': 'Problem deleted'}
# )
# messages.success(request, self.success_message)
# return super().delete(request, *args, **kwargs)
#
#
# # CarePlan ------------------------------------------------------------------------
#
# class CarePlanListView(LoginRequiredMixin, TenantMixin, ListView):
# model = CarePlan
# template_name = 'emr/care_plan_list.html'
# context_object_name = 'care_plans'
# paginate_by = 20
#
#
# class CarePlanDetailView(LoginRequiredMixin, TenantMixin, DetailView):
# model = CarePlan
# template_name = 'emr/care_plan_detail.html'
# context_object_name = 'care_plan'
#
#
# class CarePlanCreateView(
# LoginRequiredMixin, FormKwargsMixin,
# SuccessMessageMixin, CreateView
# ):
# model = CarePlan
# form_class = CarePlanForm
# template_name = 'emr/care_plan_form.html'
# success_message = _('Care plan created successfully.')
#
# def form_valid(self, form):
# response = super().form_valid(form)
# AuditLogEntry.objects.create(
# tenant=self.request.user.tenant,
# user=self.request.user,
# action='CREATE',
# model_name='CarePlan',
# object_id=str(self.object.pk),
# changes={'status': 'Care plan created'}
# )
# return response
#
# def get_success_url(self):
# return reverse_lazy('emr:care_plan_detail', kwargs={'pk': self.object.pk})
#
#
# class CarePlanUpdateView(
# LoginRequiredMixin, FormKwargsMixin,
# SuccessMessageMixin, TenantMixin, UpdateView
# ):
# model = CarePlan
# form_class = CarePlanForm
# template_name = 'emr/care_plan_form.html'
# success_message = _('Care plan updated successfully.')
#
# def form_valid(self, form):
# response = super().form_valid(form)
# AuditLogEntry.objects.create(
# tenant=self.request.user.tenant,
# user=self.request.user,
# action='UPDATE',
# model_name='CarePlan',
# object_id=str(self.object.pk),
# changes={'status': 'Care plan updated'}
# )
# return response
#
# def get_success_url(self):
# return reverse_lazy('emr:care_plan_detail', kwargs={'pk': self.object.pk})
#
#
# class CarePlanDeleteView(
# LoginRequiredMixin, TenantMixin,
# SuccessMessageMixin, DeleteView
# ):
# model = CarePlan
# template_name = 'emr/care_plan_confirm_delete.html'
# success_url = reverse_lazy('emr:care_plan_list')
# success_message = _('Care plan deleted successfully.')
#
# def delete(self, request, *args, **kwargs):
# cp = self.get_object()
# AuditLogEntry.objects.create(
# tenant=request.user.tenant,
# user=request.user,
# action='DELETE',
# model_name='CarePlan',
# object_id=str(cp.pk),
# changes={'status': 'Care plan deleted'}
# )
# messages.success(request, self.success_message)
# return super().delete(request, *args, **kwargs)
#
#
# # ClinicalNote --------------------------------------------------------------------
#
# class ClinicalNoteListView(LoginRequiredMixin, TenantMixin, ListView):
# model = ClinicalNote
# template_name = 'emr/clinical_note_list.html'
# context_object_name = 'notes'
# paginate_by = 20
#
#
# class ClinicalNoteDetailView(LoginRequiredMixin, TenantMixin, DetailView):
# model = ClinicalNote
# template_name = 'emr/clinical_note_detail.html'
# context_object_name = 'note'
#
#
# class ClinicalNoteCreateView(
# LoginRequiredMixin, FormKwargsMixin,
# SuccessMessageMixin, CreateView
# ):
# model = ClinicalNote
# form_class = ClinicalNoteForm
# template_name = 'emr/clinical_note_form.html'
# success_message = _('Clinical note created successfully.')
#
# def form_valid(self, form):
# form.instance.author = self.request.user
# response = super().form_valid(form)
# AuditLogEntry.objects.create(
# tenant=self.request.user.tenant,
# user=self.request.user,
# action='CREATE',
# model_name='ClinicalNote',
# object_id=str(self.object.pk),
# changes={'status': 'Clinical note created'}
# )
# return response
#
# def get_success_url(self):
# return reverse_lazy('emr:clinical_note_detail', kwargs={'pk': self.object.pk})
#
#
# class ClinicalNoteUpdateView(
# LoginRequiredMixin, FormKwargsMixin,
# SuccessMessageMixin, TenantMixin, UpdateView
# ):
# model = ClinicalNote
# form_class = ClinicalNoteForm
# template_name = 'emr/clinical_note_form.html'
# success_message = _('Clinical note updated successfully.')
#
# def form_valid(self, form):
# response = super().form_valid(form)
# AuditLogEntry.objects.create(
# tenant=self.request.user.tenant,
# user=self.request.user,
# action='UPDATE',
# model_name='ClinicalNote',
# object_id=str(self.object.pk),
# changes={'status': 'Clinical note updated'}
# )
# return response
#
# def get_success_url(self):
# return reverse_lazy('emr:clinical_note_detail', kwargs={'pk': self.object.pk})
#
#
# class ClinicalNoteDeleteView(
# LoginRequiredMixin, TenantMixin,
# SuccessMessageMixin, DeleteView
# ):
# model = ClinicalNote
# template_name = 'emr/clinical_note_confirm_delete.html'
# success_url = reverse_lazy('emr:clinical_note_list')
# success_message = _('Clinical note deleted successfully.')
#
# def delete(self, request, *args, **kwargs):
# cn = self.get_object()
# AuditLogEntry.objects.create(
# tenant=request.user.tenant,
# user=request.user,
# action='DELETE',
# model_name='ClinicalNote',
# object_id=str(cn.pk),
# changes={'status': 'Clinical note deleted'}
# )
# messages.success(request, self.success_message)
# return super().delete(request, *args, **kwargs)
#
#
# # NoteTemplate -------------------------------------------------------------------
#
# class NoteTemplateListView(LoginRequiredMixin, TenantMixin, ListView):
# model = NoteTemplate
# template_name = 'emr/note_template_list.html'
# context_object_name = 'templates'
# paginate_by = 20
#
#
# class NoteTemplateDetailView(LoginRequiredMixin, TenantMixin, DetailView):
# model = NoteTemplate
# template_name = 'emr/note_template_detail.html'
# context_object_name = 'template'
#
#
# class NoteTemplateCreateView(
# LoginRequiredMixin, FormKwargsMixin,
# SuccessMessageMixin, CreateView
# ):
# model = NoteTemplate
# form_class = NoteTemplateForm
# template_name = 'emr/note_template_form.html'
# success_message = _('Note template created successfully.')
#
# def form_valid(self, form):
# form.instance.tenant = self.request.user.tenant
# form.instance.created_by = self.request.user
# response = super().form_valid(form)
# AuditLogEntry.objects.create(
# tenant=self.request.user.tenant,
# user=self.request.user,
# action='CREATE',
# model_name='NoteTemplate',
# object_id=str(self.object.pk),
# changes={'status': 'Template created'}
# )
# return response
#
# def get_success_url(self):
# return reverse_lazy('emr:note_template_detail', kwargs={'pk': self.object.pk})
#
#
# class NoteTemplateUpdateView(
# LoginRequiredMixin, FormKwargsMixin,
# SuccessMessageMixin, TenantMixin, UpdateView
# ):
# model = NoteTemplate
# form_class = NoteTemplateForm
# template_name = 'emr/note_template_form.html'
# success_message = _('Note template updated successfully.')
#
# def form_valid(self, form):
# response = super().form_valid(form)
# AuditLogEntry.objects.create(
# tenant=self.request.user.tenant,
# user=self.request.user,
# action='UPDATE',
# model_name='NoteTemplate',
# object_id=str(self.object.pk),
# changes={'status': 'Template updated'}
# )
# return response
#
# def get_success_url(self):
# return reverse_lazy('emr:note_template_detail', kwargs={'pk': self.object.pk})
#
#
# class NoteTemplateDeleteView(
# LoginRequiredMixin, TenantMixin,
# SuccessMessageMixin, DeleteView
# ):
# model = NoteTemplate
# template_name = 'emr/note_template_confirm_delete.html'
# success_url = reverse_lazy('emr:note_template_list')
# success_message = _('Note template deleted successfully.')
#
# def delete(self, request, *args, **kwargs):
# nt = self.get_object()
# if ClinicalNote.objects.filter(template=nt).exists():
# messages.error(request, _('Cannot delete a template in use.'))
# return redirect('emr:note_template_detail', pk=nt.pk)
#
# AuditLogEntry.objects.create(
# tenant=request.user.tenant,
# user=request.user,
# action='DELETE',
# model_name='NoteTemplate',
# object_id=str(nt.pk),
# changes={'status': 'Template deleted'}
# )
# messages.success(request, self.success_message)
# return super().delete(request, *args, **kwargs)
#
#
# # HTMX & Actions ------------------------------------------------------------------
#
# @login_required
# def htmx_emr_stats(request):
# tenant = request.user.tenant
# today = timezone.now().date()
# enc = Encounter.objects.filter(patient__tenant=tenant)
# vs = VitalSigns.objects.filter(encounter__patient__tenant=tenant)
# pb = ProblemList.objects.filter(patient__tenant=tenant)
# cp = CarePlan.objects.filter(patient__tenant=tenant)
# cn = ClinicalNote.objects.filter(encounter__patient__tenant=tenant)
#
# stats = {
# 'total_encounters': enc.count(),
# 'encounters_today': enc.filter(scheduled_datetime__date=today).count(),
# 'active_encounters': enc.filter(status='IN_PROGRESS').count(),
# 'total_vital_signs': vs.count(),
# 'vital_signs_today': vs.filter(recorded_at__date=today).count(),
# 'active_problems': pb.filter(status='ACTIVE').count(),
# 'active_care_plans': cp.filter(status='ACTIVE').count(),
# 'total_notes': cn.count(),
# 'notes_today': cn.filter(created_at__date=today).count(),
# }
# return JsonResponse(stats)
#
#
# @login_required
# def start_encounter(request, pk):
# enc = get_object_or_404(Encounter, pk=pk, patient__tenant=request.user.tenant)
# if enc.status == 'SCHEDULED':
# enc.status = 'IN_PROGRESS'; enc.save()
# AuditLogEntry.objects.create(
# tenant=request.user.tenant, user=request.user,
# action='UPDATE', model_name='Encounter',
# object_id=str(enc.pk), changes={'status': 'Encounter started'}
# )
# messages.success(request, _('Encounter started.'))
# else:
# messages.error(request, _('Only scheduled encounters can be started.'))
# return redirect('emr:encounter_detail', pk=pk)
#
#
# @login_required
# def complete_encounter(request, pk):
# enc = get_object_or_404(Encounter, pk=pk, patient__tenant=request.user.tenant)
# if enc.status == 'IN_PROGRESS':
# enc.status = 'COMPLETED'; enc.save()
# AuditLogEntry.objects.create(
# tenant=request.user.tenant, user=request.user,
# action='UPDATE', model_name='Encounter',
# object_id=str(enc.pk), changes={'status': 'Encounter completed'}
# )
# messages.success(request, _('Encounter completed.'))
# else:
# messages.error(request, _('Only in-progress encounters can be completed.'))
# return redirect('emr:encounter_detail', pk=pk)
#
#
@login_required
def resolve_problem(request, pk):
prob = get_object_or_404(ProblemList, pk=pk, patient__tenant=request.user.tenant)
if prob.status == 'ACTIVE':
prob.status = 'RESOLVED'; prob.save()
AuditLogEntry.objects.create(
tenant=request.user.tenant, user=request.user,
action='UPDATE', model_name='ProblemList',
object_id=str(prob.pk), changes={'status': 'Problem resolved'}
)
messages.success(request, _('Problem resolved.'))
else:
messages.error(request, _('Only active problems can be resolved.'))
return redirect('emr:problem_detail', pk=pk)
#
#
# @login_required
# def complete_care_plan(request, pk):
# cp = get_object_or_404(CarePlan, pk=pk, patient__tenant=request.user.tenant)
# if cp.status == 'ACTIVE':
# cp.status = 'COMPLETED'; cp.save()
# AuditLogEntry.objects.create(
# tenant=request.user.tenant, user=request.user,
# action='UPDATE', model_name='CarePlan',
# object_id=str(cp.pk), changes={'status': 'Care plan completed'}
# )
# messages.success(request, _('Care plan completed.'))
# else:
# messages.error(request, _('Only active care plans can be completed.'))
# return redirect('emr:care_plan_detail', pk=pk)
#
#
# @login_required
# def sign_note(request, pk):
# note = get_object_or_404(ClinicalNote, pk=pk, encounter__patient__tenant=request.user.tenant)
# if note.status == 'DRAFT' and note.author == request.user:
# note.status = 'SIGNED'; note.signed_at = timezone.now(); note.save()
# AuditLogEntry.objects.create(
# tenant=request.user.tenant, user=request.user,
# action='UPDATE', model_name='ClinicalNote',
# object_id=str(note.pk), changes={'status': 'Clinical note signed'}
# )
# messages.success(request, _('Note signed.'))
# else:
# messages.error(request, _('Only your draft notes can be signed.'))
# return redirect('emr:clinical_note_detail', pk=pk)
#
#
# class PatientEMRView(LoginRequiredMixin, DetailView):
# model = PatientProfile
# template_name = 'emr/patient_emr.html'
# pk_url_kwarg = 'patient_id'
#
# def get_queryset(self):
# return PatientProfile.objects.filter(tenant=self.request.user.tenant)
#
#
# @login_required
# def htmx_encounter_search(request):
# search = request.GET.get('search', '')
# encs = Encounter.objects.filter(patient__tenant=request.user.tenant)
# if search:
# encs = encs.filter(
# Q(patient__first_name__icontains=search) |
# Q(patient__last_name__icontains=search) |
# Q(chief_complaint__icontains=search)
# )
# encs = encs.order_by('-scheduled_datetime')[:10]
# return render(request, 'emr/partials/encounter_list.html', {'encounters': encs})
#
#
# @login_required
# def htmx_vital_signs_search(request):
# search = request.GET.get('search', '')
# vs = VitalSigns.objects.filter(encounter__patient__tenant=request.user.tenant)
# if search:
# vs = vs.filter(
# Q(encounter__patient__first_name__icontains=search) |
# Q(encounter__patient__last_name__icontains=search)
# )
# vs = vs.order_by('-recorded_at')[:10]
# return render(request, 'emr/partials/vital_signs_list.html', {'vital_signs': vs})
#
#
# class VitalSignsUpdateView(LoginRequiredMixin, FormKwargsMixin, SuccessMessageMixin, TenantMixin, UpdateView):
# model = VitalSigns
# form_class = VitalSignsForm
# template_name = 'emr/vital_signs_form.html'
# success_message = _('Vital signs updated successfully.')
# # implement form_valid, get_success_url, etc.
#
# class VitalSignsDeleteView(LoginRequiredMixin, TenantMixin, SuccessMessageMixin, DeleteView):
# model = VitalSigns
# template_name = 'emr/vital_signs_confirm_delete.html'
# success_url = reverse_lazy('emr:vitalsigns_list')
# success_message = _('Vital signs entry deleted.')
#
# @login_required
# def get_template_content(request, pk):
# """
# Return the raw content of a NoteTemplate (e.g. for AJAX/HTMX).
# """
# template = get_object_or_404(
# NoteTemplate,
# pk=pk,
# tenant=request.user.tenant
# )
# return JsonResponse({
# 'template_id': str(template.template_id),
# 'name': template.name,
# 'content': template.content,
# })
#