2687 lines
96 KiB
Python
2687 lines
96 KiB
Python
"""
|
|
Operating Theatre app views with healthcare-focused CRUD operations.
|
|
Implements appropriate access patterns for surgical and OR management workflows.
|
|
"""
|
|
|
|
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 (
|
|
ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView
|
|
)
|
|
from django.http import JsonResponse, HttpResponse
|
|
from django.db.models import Q, Count, Avg, Sum, F
|
|
from django.utils import timezone
|
|
from django.contrib import messages
|
|
from django.urls import reverse_lazy, reverse
|
|
from django.core.paginator import Paginator
|
|
from django.template.loader import render_to_string
|
|
from datetime import datetime, timedelta, date
|
|
import json
|
|
|
|
from core.utils import AuditLogger
|
|
from .models import (
|
|
OperatingRoom, ORBlock, SurgicalCase, SurgicalNote,
|
|
EquipmentUsage, SurgicalNoteTemplate
|
|
)
|
|
from .forms import (
|
|
OperatingRoomForm, ORBlockForm, SurgicalCaseForm, SurgicalNoteForm,
|
|
EquipmentUsageForm, SurgicalNoteTemplateForm
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# DASHBOARD AND OVERVIEW VIEWS
|
|
# ============================================================================
|
|
|
|
class OperatingTheatreDashboardView(LoginRequiredMixin, TemplateView):
|
|
"""
|
|
Main operating theatre dashboard with key metrics and recent activity.
|
|
"""
|
|
template_name = 'operating_theatre/dashboard.html'
|
|
|
|
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_rooms': OperatingRoom.objects.filter(
|
|
tenant=tenant,
|
|
is_active=True
|
|
).count(),
|
|
'rooms_available': OperatingRoom.objects.filter(
|
|
tenant=tenant,
|
|
is_active=True,
|
|
status='AVAILABLE'
|
|
).count(),
|
|
'rooms_in_use': OperatingRoom.objects.filter(
|
|
tenant=tenant,
|
|
is_active=True,
|
|
status='IN_USE'
|
|
).count(),
|
|
'rooms_maintenance': OperatingRoom.objects.filter(
|
|
tenant=tenant,
|
|
is_active=True,
|
|
status='MAINTENANCE'
|
|
).count(),
|
|
'cases_today': SurgicalCase.objects.filter(
|
|
admission__tenant=tenant,
|
|
scheduled_start__date=today
|
|
).count(),
|
|
'cases_in_progress': SurgicalCase.objects.filter(
|
|
admission__tenant=tenant,
|
|
status='IN_PROGRESS'
|
|
).count(),
|
|
'cases_completed_today': SurgicalCase.objects.filter(
|
|
admission__tenant=tenant,
|
|
actual_end__date=today,
|
|
status='COMPLETED'
|
|
).count(),
|
|
'emergency_cases_today': SurgicalCase.objects.filter(
|
|
admission__tenant=tenant,
|
|
scheduled_start__date=today,
|
|
case_type='EMERGENCY'
|
|
).count(),
|
|
'blocks_today': ORBlock.objects.filter(
|
|
operating_room__tenant=tenant,
|
|
date=today
|
|
).count(),
|
|
'equipment_in_use': EquipmentUsage.objects.filter(
|
|
surgical_case__admission__tenant=tenant,
|
|
# status='IN_USE'
|
|
).count(),
|
|
'notes_pending': SurgicalNote.objects.filter(
|
|
surgical_case__admission__tenant=tenant,
|
|
status='DRAFT'
|
|
).count(),
|
|
})
|
|
|
|
# Recent surgical cases
|
|
context['recent_cases'] = SurgicalCase.objects.filter(
|
|
admission__tenant=tenant
|
|
).select_related(
|
|
'patient', 'primary_surgeon', 'or_block__operating_room'
|
|
).order_by('-scheduled_start')[:10]
|
|
|
|
# Today's schedule
|
|
context['todays_schedule'] = SurgicalCase.objects.filter(
|
|
admission__tenant=tenant,
|
|
scheduled_start__date=today
|
|
).select_related(
|
|
'patient', 'primary_surgeon', 'or_block__operating_room'
|
|
).order_by('scheduled_start')
|
|
|
|
# Room utilization
|
|
# context['room_utilization'] = OperatingRoom.objects.filter(
|
|
# tenant=tenant,
|
|
# is_active=True
|
|
# ).annotate(
|
|
# cases_today=Count(
|
|
# 'or_block__surgical_cases',
|
|
# filter=Q(surgical_cases__scheduled_start__date=today)
|
|
# )
|
|
# ).order_by('room_number')
|
|
|
|
return context
|
|
|
|
|
|
# ============================================================================
|
|
# OPERATING ROOM VIEWS (FULL CRUD - Master Data)
|
|
# ============================================================================
|
|
|
|
class OperatingRoomListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List all operating rooms with filtering and search.
|
|
"""
|
|
model = OperatingRoom
|
|
template_name = 'operating_theatre/rooms/operating_room_list.html'
|
|
context_object_name = 'operating_rooms'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
queryset = OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
# Search functionality
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(room_number__icontains=search) |
|
|
Q(room_name__icontains=search) |
|
|
Q(location__icontains=search)
|
|
)
|
|
|
|
# Filter by room type
|
|
room_type = self.request.GET.get('room_type')
|
|
if room_type:
|
|
queryset = queryset.filter(room_type=room_type)
|
|
|
|
# Filter by status
|
|
status = self.request.GET.get('status')
|
|
if status:
|
|
queryset = queryset.filter(status=status)
|
|
|
|
# Filter by floor
|
|
floor = self.request.GET.get('floor')
|
|
if floor:
|
|
queryset = queryset.filter(floor=floor)
|
|
|
|
# Filter by active status
|
|
active_only = self.request.GET.get('active_only')
|
|
if active_only:
|
|
queryset = queryset.filter(is_active=True)
|
|
|
|
return queryset.order_by('room_number')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context.update({
|
|
'room_types': OperatingRoom._meta.get_field('room_type').choices,
|
|
'statuses': OperatingRoom._meta.get_field('status').choices,
|
|
'search_query': self.request.GET.get('search', ''),
|
|
})
|
|
return context
|
|
|
|
|
|
class OperatingRoomDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Display detailed information about an operating room.
|
|
"""
|
|
model = OperatingRoom
|
|
template_name = 'operating_theatre/rooms/operating_room_detail.html'
|
|
context_object_name = 'operating_room'
|
|
|
|
def get_queryset(self):
|
|
return OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
operating_room = self.object
|
|
today = timezone.now().date()
|
|
|
|
# Get today's cases for this room
|
|
context['todays_cases'] = operating_room.surgical_cases.filter(
|
|
scheduled_start_time__date=today
|
|
).select_related('patient', 'primary_surgeon').order_by('scheduled_start_time')
|
|
|
|
# Get recent cases
|
|
context['recent_cases'] = operating_room.surgical_cases.all().select_related(
|
|
'patient', 'primary_surgeon'
|
|
).order_by('-scheduled_start_time')[:10]
|
|
|
|
# Get equipment usage
|
|
context['equipment_usage'] = EquipmentUsage.objects.filter(
|
|
operating_room=operating_room,
|
|
tenant=self.request.user.tenant
|
|
).order_by('-start_time')[:10]
|
|
|
|
# Room statistics
|
|
context['room_stats'] = {
|
|
'total_cases': operating_room.surgical_cases.count(),
|
|
'cases_this_month': operating_room.surgical_cases.filter(
|
|
scheduled_start_time__month=timezone.now().month,
|
|
scheduled_start_time__year=timezone.now().year
|
|
).count(),
|
|
'average_case_duration': operating_room.surgical_cases.filter(
|
|
actual_end_time__isnull=False
|
|
).aggregate(
|
|
avg_duration=Avg(
|
|
F('actual_end_time') - F('actual_start_time')
|
|
)
|
|
)['avg_duration'],
|
|
}
|
|
|
|
return context
|
|
|
|
|
|
class OperatingRoomCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create a new operating room.
|
|
"""
|
|
model = OperatingRoom
|
|
form_class = OperatingRoomForm
|
|
template_name = 'operating_theatre/rooms/operating_room_form.html'
|
|
permission_required = 'operating_theatre.add_operatingroom'
|
|
success_url = reverse_lazy('operating_theatre:operating_room_list')
|
|
|
|
def form_valid(self, form):
|
|
form.instance.tenant = self.request.user.tenant
|
|
response = super().form_valid(form)
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=self.request.user,
|
|
action='OPERATING_ROOM_CREATED',
|
|
model='OperatingRoom',
|
|
object_id=str(self.object.id),
|
|
details={
|
|
'room_number': self.object.room_number,
|
|
'room_name': self.object.room_name,
|
|
'room_type': self.object.room_type
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, f'Operating room "{self.object.room_number}" created successfully.')
|
|
return response
|
|
|
|
|
|
class OperatingRoomUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
"""
|
|
Update an existing operating room.
|
|
"""
|
|
model = OperatingRoom
|
|
form_class = OperatingRoomForm
|
|
template_name = 'operating_theatre/rooms/operating_room_form.html'
|
|
permission_required = 'operating_theatre.change_operatingroom'
|
|
|
|
def get_queryset(self):
|
|
return OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_success_url(self):
|
|
return reverse('operating_theatre:operating_room_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=self.request.user,
|
|
action='OPERATING_ROOM_UPDATED',
|
|
model='OperatingRoom',
|
|
object_id=str(self.object.id),
|
|
details={
|
|
'room_number': self.object.room_number,
|
|
'changes': form.changed_data
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, f'Operating room "{self.object.room_number}" updated successfully.')
|
|
return response
|
|
|
|
|
|
class OperatingRoomDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
"""
|
|
Delete an operating room (soft delete by deactivating).
|
|
"""
|
|
model = OperatingRoom
|
|
template_name = 'operating_theatre/rooms/operating_room_confirm_delete.html'
|
|
permission_required = 'operating_theatre.delete_operatingroom'
|
|
success_url = reverse_lazy('operating_theatre:operating_room_list')
|
|
|
|
def get_queryset(self):
|
|
return OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
|
|
# Check if room has active cases
|
|
active_cases = self.object.surgical_cases.filter(
|
|
status__in=['SCHEDULED', 'IN_PROGRESS']
|
|
).count()
|
|
|
|
if active_cases > 0:
|
|
messages.error(
|
|
request,
|
|
f'Cannot deactivate room "{self.object.room_number}" - it has {active_cases} active cases.'
|
|
)
|
|
return redirect('operating_theatre:operating_room_detail', pk=self.object.pk)
|
|
|
|
# Soft delete by deactivating
|
|
self.object.is_active = False
|
|
self.object.status = 'OUT_OF_SERVICE'
|
|
self.object.save()
|
|
|
|
# Log the action
|
|
AuditLogger.log_event(
|
|
user=request.user,
|
|
action='OPERATING_ROOM_DEACTIVATED',
|
|
model='OperatingRoom',
|
|
object_id=str(self.object.id),
|
|
details={'room_number': self.object.room_number}
|
|
)
|
|
|
|
messages.success(request, f'Operating room "{self.object.room_number}" deactivated successfully.')
|
|
return redirect(self.success_url)
|
|
|
|
|
|
# ============================================================================
|
|
# SURGICAL NOTE TEMPLATE VIEWS (FULL CRUD - Master Data)
|
|
# ============================================================================
|
|
|
|
class SurgicalNoteTemplateListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List all surgical note templates with filtering and search.
|
|
"""
|
|
model = SurgicalNoteTemplate
|
|
template_name = 'operating_theatre/templates/surgical_note_template_list.html'
|
|
context_object_name = 'surgical_note_templates'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
queryset = SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
# Search functionality
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(template_name__icontains=search) |
|
|
Q(procedure_type__icontains=search) |
|
|
Q(specialty__icontains=search)
|
|
)
|
|
|
|
# Filter by specialty
|
|
specialty = self.request.GET.get('specialty')
|
|
if specialty:
|
|
queryset = queryset.filter(specialty=specialty)
|
|
|
|
# Filter by active status
|
|
active_only = self.request.GET.get('active_only')
|
|
if active_only:
|
|
queryset = queryset.filter(is_active=True)
|
|
|
|
return queryset.order_by('name')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context.update({
|
|
'specialties': SurgicalNoteTemplate._meta.get_field('specialty').choices,
|
|
'search_query': self.request.GET.get('search', ''),
|
|
})
|
|
return context
|
|
|
|
|
|
class SurgicalNoteTemplateDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Display detailed information about a surgical note template.
|
|
"""
|
|
model = SurgicalNoteTemplate
|
|
template_name = 'operating_theatre/templates/surgical_note_template_detail.html'
|
|
context_object_name = 'surgical_note_template'
|
|
|
|
def get_queryset(self):
|
|
return SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
template = self.object
|
|
|
|
# Get recent notes using this template
|
|
context['recent_notes'] = SurgicalNote.objects.filter(
|
|
template=template,
|
|
tenant=self.request.user.tenant
|
|
).select_related('surgical_case__patient').order_by('-created_at')[:10]
|
|
|
|
return context
|
|
|
|
|
|
class SurgicalNoteTemplateCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create a new surgical note template.
|
|
"""
|
|
model = SurgicalNoteTemplate
|
|
form_class = SurgicalNoteTemplateForm
|
|
template_name = 'operating_theatre/templates/surgical_note_template_form.html'
|
|
permission_required = 'operating_theatre.add_surgicalnotetemplate'
|
|
success_url = reverse_lazy('operating_theatre:surgical_note_template_list')
|
|
|
|
def form_valid(self, form):
|
|
form.instance.tenant = self.request.user.tenant
|
|
form.instance.created_by = self.request.user
|
|
response = super().form_valid(form)
|
|
|
|
# Log the action
|
|
AuditLogger.log_event(
|
|
user=self.request.user,
|
|
action='SURGICAL_NOTE_TEMPLATE_CREATED',
|
|
model='SurgicalNoteTemplate',
|
|
object_id=str(self.object.id),
|
|
details={
|
|
'template_name': self.object.template_name,
|
|
'specialty': self.object.specialty
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, f'Surgical note template "{self.object.template_name}" created successfully.')
|
|
return response
|
|
|
|
|
|
class SurgicalNoteTemplateUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
"""
|
|
Update an existing surgical note template.
|
|
"""
|
|
model = SurgicalNoteTemplate
|
|
form_class = SurgicalNoteTemplateForm
|
|
template_name = 'operating_theatre/templates/surgical_note_template_form.html'
|
|
permission_required = 'operating_theatre.change_surgicalnotetemplate'
|
|
|
|
def get_queryset(self):
|
|
return SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_success_url(self):
|
|
return reverse('operating_theatre:surgical_note_template_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=self.request.user,
|
|
action='SURGICAL_NOTE_TEMPLATE_UPDATED',
|
|
model='SurgicalNoteTemplate',
|
|
object_id=str(self.object.id),
|
|
details={
|
|
'template_name': self.object.template_name,
|
|
'changes': form.changed_data
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, f'Surgical note template "{self.object.template_name}" updated successfully.')
|
|
return response
|
|
|
|
|
|
class SurgicalNoteTemplateDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
"""
|
|
Delete a surgical note template (soft delete by deactivating).
|
|
"""
|
|
model = SurgicalNoteTemplate
|
|
template_name = 'operating_theatre/templates/surgical_note_template_confirm_delete.html'
|
|
permission_required = 'operating_theatre.delete_surgicalnotetemplate'
|
|
success_url = reverse_lazy('operating_theatre:surgical_note_template_list')
|
|
|
|
def get_queryset(self):
|
|
return SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def delete(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
|
|
# Soft delete by deactivating
|
|
self.object.is_active = False
|
|
self.object.save()
|
|
|
|
# Log the action
|
|
AuditLogger.log_event(
|
|
user=request.user,
|
|
action='SURGICAL_NOTE_TEMPLATE_DEACTIVATED',
|
|
model='SurgicalNoteTemplate',
|
|
object_id=str(self.object.id),
|
|
details={'template_name': self.object.template_name}
|
|
)
|
|
|
|
messages.success(request, f'Surgical note template "{self.object.template_name}" deactivated successfully.')
|
|
return redirect(self.success_url)
|
|
|
|
|
|
# ============================================================================
|
|
# OR BLOCK VIEWS (LIMITED CRUD - Operational Data)
|
|
# ============================================================================
|
|
|
|
class ORBlockListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List all OR blocks with filtering and search.
|
|
"""
|
|
model = ORBlock
|
|
template_name = 'operating_theatre/blocks/block_list.html'
|
|
context_object_name = 'or_blocks'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
queryset = ORBlock.objects.filter(operating_room__tenant=self.request.user.tenant)
|
|
|
|
# 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(date__gte=date_from)
|
|
if date_to:
|
|
queryset = queryset.filter(date__lte=date_to)
|
|
|
|
# Filter by surgeon
|
|
surgeon_id = self.request.GET.get('surgeon')
|
|
if surgeon_id:
|
|
queryset = queryset.filter(surgeon_id=surgeon_id)
|
|
|
|
# Filter by operating room
|
|
room_id = self.request.GET.get('room')
|
|
if room_id:
|
|
queryset = queryset.filter(operating_room_id=room_id)
|
|
|
|
return queryset.select_related(
|
|
'operating_room', 'surgeon'
|
|
).order_by('-date', 'start_time')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context.update({
|
|
'operating_rooms': OperatingRoom.objects.filter(
|
|
tenant=self.request.user.tenant,
|
|
is_active=True
|
|
).order_by('room_number'),
|
|
})
|
|
return context
|
|
|
|
|
|
class ORBlockDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Display detailed information about an OR block.
|
|
"""
|
|
model = ORBlock
|
|
template_name = 'operating_theatre/blocks/block_detail.html'
|
|
context_object_name = 'or_block'
|
|
|
|
def get_queryset(self):
|
|
return ORBlock.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
or_block = self.object
|
|
|
|
# Get cases scheduled in this block
|
|
context['scheduled_cases'] = SurgicalCase.objects.filter(
|
|
operating_room=or_block.operating_room,
|
|
scheduled_start_time__date=or_block.date,
|
|
scheduled_start_time__time__gte=or_block.start_time,
|
|
scheduled_start_time__time__lt=or_block.end_time,
|
|
tenant=self.request.user.tenant
|
|
).select_related('patient', 'primary_surgeon').order_by('scheduled_start_time')
|
|
|
|
# Calculate utilization
|
|
total_block_minutes = (
|
|
timezone.datetime.combine(timezone.now().date(), or_block.end_time) -
|
|
timezone.datetime.combine(timezone.now().date(), or_block.start_time)
|
|
).total_seconds() / 60
|
|
|
|
used_minutes = 0
|
|
for case in context['scheduled_cases']:
|
|
if case.estimated_duration_minutes:
|
|
used_minutes += case.estimated_duration_minutes
|
|
|
|
context['utilization_percentage'] = (
|
|
(used_minutes / total_block_minutes) * 100 if total_block_minutes > 0 else 0
|
|
)
|
|
|
|
return context
|
|
|
|
|
|
class ORBlockCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create a new OR block.
|
|
"""
|
|
model = ORBlock
|
|
form_class = ORBlockForm
|
|
template_name = 'operating_theatre/blocks/block_form.html'
|
|
permission_required = 'operating_theatre.add_orblock'
|
|
success_url = reverse_lazy('operating_theatre:or_block_list')
|
|
|
|
def form_valid(self, form):
|
|
form.instance.tenant = self.request.user.tenant
|
|
response = super().form_valid(form)
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=self.request.user,
|
|
action='OR_BLOCK_CREATED',
|
|
model='ORBlock',
|
|
object_id=str(self.object.id),
|
|
details={
|
|
'date': str(self.object.date),
|
|
'operating_room': self.object.operating_room.room_number,
|
|
'surgeon': f"{self.object.surgeon.first_name} {self.object.surgeon.last_name}"
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, 'OR block created successfully.')
|
|
return response
|
|
|
|
|
|
class ORBlockUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
"""
|
|
Update an OR block (limited to notes and time adjustments).
|
|
"""
|
|
model = ORBlock
|
|
fields = ['start_time', 'end_time', 'notes'] # Restricted fields
|
|
template_name = 'operating_theatre/or_block_update_form.html'
|
|
permission_required = 'operating_theatre.change_orblock'
|
|
|
|
def get_queryset(self):
|
|
return ORBlock.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_success_url(self):
|
|
return reverse('operating_theatre:or_block_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=self.request.user,
|
|
action='OR_BLOCK_UPDATED',
|
|
model='ORBlock',
|
|
object_id=str(self.object.id),
|
|
details={
|
|
'date': str(self.object.date),
|
|
'changes': form.changed_data
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, 'OR block updated successfully.')
|
|
return response
|
|
|
|
|
|
# ============================================================================
|
|
# SURGICAL CASE VIEWS (RESTRICTED CRUD - Clinical Data)
|
|
# ============================================================================
|
|
|
|
class SurgicalCaseListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List all surgical cases with filtering and search.
|
|
"""
|
|
model = SurgicalCase
|
|
template_name = 'operating_theatre/surgical_case_list.html'
|
|
context_object_name = 'surgical_cases'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
tenant = self.request.user.tenant
|
|
queryset = SurgicalCase.objects.filter(admission__tenant=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(procedure_name__icontains=search)
|
|
)
|
|
|
|
# Filter by status
|
|
status = self.request.GET.get('status')
|
|
if status:
|
|
queryset = queryset.filter(status=status)
|
|
|
|
# Filter by priority
|
|
priority = self.request.GET.get('priority')
|
|
if priority:
|
|
queryset = queryset.filter(priority=priority)
|
|
|
|
# Filter by surgeon
|
|
surgeon_id = self.request.GET.get('surgeon')
|
|
if surgeon_id:
|
|
queryset = queryset.filter(primary_surgeon_id=surgeon_id)
|
|
|
|
# Filter by operating room
|
|
room_id = self.request.GET.get('room')
|
|
if room_id:
|
|
queryset = queryset.filter(operating_room_id=room_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(scheduled_start__date__gte=date_from)
|
|
if date_to:
|
|
queryset = queryset.filter(scheduled_start__date__lte=date_to)
|
|
|
|
return queryset.select_related(
|
|
'patient', 'primary_surgeon', 'or_block__operating_room'
|
|
).order_by('-scheduled_start')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context.update({
|
|
'statuses': SurgicalCase.STATUS_CHOICES,
|
|
'priorities': SurgicalCase.CASE_TYPE_CHOICES,
|
|
'operating_rooms': OperatingRoom.objects.filter(
|
|
tenant=self.request.user.tenant,
|
|
is_active=True
|
|
).order_by('room_number'),
|
|
})
|
|
return context
|
|
|
|
|
|
class SurgicalCaseDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Display detailed information about a surgical case.
|
|
"""
|
|
model = SurgicalCase
|
|
template_name = 'operating_theatre/cases/surgical_case_detail.html'
|
|
context_object_name = 'surgical_case'
|
|
|
|
def get_queryset(self):
|
|
return SurgicalCase.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
surgical_case = self.object
|
|
|
|
# Get surgical notes for this case
|
|
context['surgical_notes'] = surgical_case.surgical_notes.all().order_by('-created_at')
|
|
|
|
# Get equipment usage for this case
|
|
context['equipment_usage'] = EquipmentUsage.objects.filter(
|
|
surgical_case=surgical_case,
|
|
tenant=self.request.user.tenant
|
|
).order_by('-start_time')
|
|
|
|
# Calculate actual duration if case is completed
|
|
if surgical_case.actual_start_time and surgical_case.actual_end_time:
|
|
context['actual_duration'] = surgical_case.actual_end_time - surgical_case.actual_start_time
|
|
|
|
return context
|
|
|
|
|
|
class SurgicalCaseCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create a new surgical case.
|
|
"""
|
|
model = SurgicalCase
|
|
form_class = SurgicalCaseForm
|
|
template_name = 'operating_theatre/cases/surgical_case_form.html'
|
|
permission_required = 'operating_theatre.add_surgicalcase'
|
|
success_url = reverse_lazy('operating_theatre:surgical_case_list')
|
|
|
|
def form_valid(self, form):
|
|
form.instance.tenant = self.request.user.tenant
|
|
response = super().form_valid(form)
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=self.request.user,
|
|
action='SURGICAL_CASE_CREATED',
|
|
model='SurgicalCase',
|
|
object_id=str(self.object.case_id),
|
|
details={
|
|
'patient_name': f"{self.object.patient.first_name} {self.object.patient.last_name}",
|
|
'procedure_name': self.object.procedure_name,
|
|
'priority': self.object.priority
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, 'Surgical case created successfully.')
|
|
return response
|
|
|
|
|
|
class SurgicalCaseUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
"""
|
|
Update surgical case (limited to status and notes after surgery starts).
|
|
"""
|
|
model = SurgicalCase
|
|
template_name = 'operating_theatre/cases/surgical_case_form.html'
|
|
permission_required = 'operating_theatre.change_surgicalcase'
|
|
|
|
def get_queryset(self):
|
|
return SurgicalCase.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_form_class(self):
|
|
# Limit fields based on case status
|
|
if self.object.status in ['IN_PROGRESS', 'COMPLETED']:
|
|
# Limited fields for cases that have started
|
|
class RestrictedSurgicalCaseForm(SurgicalCaseForm):
|
|
class Meta(SurgicalCaseForm.Meta):
|
|
fields = ['status', 'notes', 'complications']
|
|
return RestrictedSurgicalCaseForm
|
|
else:
|
|
return SurgicalCaseForm
|
|
|
|
def get_success_url(self):
|
|
return reverse('operating_theatre:surgical_case_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log the action
|
|
AuditLogger.log_event(
|
|
user=self.request.user,
|
|
action='SURGICAL_CASE_UPDATED',
|
|
model='SurgicalCase',
|
|
object_id=str(self.object.case_id),
|
|
details={
|
|
'patient_name': f"{self.object.patient.first_name} {self.object.patient.last_name}",
|
|
'changes': form.changed_data
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, 'Surgical case updated successfully.')
|
|
return response
|
|
|
|
|
|
# ============================================================================
|
|
# SURGICAL NOTE VIEWS (APPEND-ONLY - Clinical Records)
|
|
# ============================================================================
|
|
|
|
class SurgicalNoteListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List all surgical notes with filtering and search.
|
|
"""
|
|
model = SurgicalNote
|
|
template_name = 'operating_theatre/notes/operative_note_list.html'
|
|
context_object_name = 'surgical_notes'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
queryset = SurgicalNote.objects.filter(surgeon__tenant=self.request.user.tenant)
|
|
|
|
# Search functionality
|
|
search = self.request.GET.get('search')
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(surgical_case__patient__first_name__icontains=search) |
|
|
Q(surgical_case__patient__last_name__icontains=search) |
|
|
Q(surgical_case__patient__mrn__icontains=search) |
|
|
Q(surgical_case__procedure_name__icontains=search) |
|
|
Q(note_content__icontains=search)
|
|
)
|
|
|
|
# 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 surgeon
|
|
surgeon_id = self.request.GET.get('surgeon')
|
|
if surgeon_id:
|
|
queryset = queryset.filter(surgeon_id=surgeon_id)
|
|
|
|
return queryset.select_related(
|
|
'surgical_case__patient', 'surgeon', 'template'
|
|
).order_by('-created_at')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context.update({
|
|
# 'note_types': SurgicalNote._meta.get_field('note_type').choices,
|
|
'statuses': SurgicalNote.STATUS_CHOICES,
|
|
})
|
|
return context
|
|
|
|
|
|
class SurgicalNoteDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Display detailed information about a surgical note.
|
|
"""
|
|
model = SurgicalNote
|
|
template_name = 'operating_theatre/notes/operative_note_detail.html'
|
|
context_object_name = 'surgical_note'
|
|
|
|
def get_queryset(self):
|
|
return SurgicalNote.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
|
|
class SurgicalNoteCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create a new surgical note.
|
|
"""
|
|
model = SurgicalNote
|
|
form_class = SurgicalNoteForm
|
|
template_name = 'operating_theatre/notes/surgical_note_form.html'
|
|
permission_required = 'operating_theatre.add_surgicalnote'
|
|
success_url = reverse_lazy('operating_theatre:surgical_note_list')
|
|
|
|
def form_valid(self, form):
|
|
form.instance.tenant = self.request.user.tenant
|
|
form.instance.surgeon = self.request.user
|
|
response = super().form_valid(form)
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=self.request.user,
|
|
action='SURGICAL_NOTE_CREATED',
|
|
model='SurgicalNote',
|
|
object_id=str(self.object.note_id),
|
|
details={
|
|
'patient_name': f"{self.object.surgical_case.patient.first_name} {self.object.surgical_case.patient.last_name}",
|
|
'note_type': self.object.note_type
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, 'Surgical note created successfully.')
|
|
return response
|
|
|
|
|
|
# Note: No UpdateView or DeleteView for SurgicalNote - Append-only for clinical records
|
|
|
|
|
|
# ============================================================================
|
|
# EQUIPMENT USAGE VIEWS (LIMITED CRUD - Operational Data)
|
|
# ============================================================================
|
|
|
|
class EquipmentUsageListView(LoginRequiredMixin, ListView):
|
|
"""
|
|
List all equipment usage records with filtering and search.
|
|
"""
|
|
model = EquipmentUsage
|
|
template_name = 'operating_theatre/equipment/equipment_list.html'
|
|
context_object_name = 'equipment_usage_records'
|
|
paginate_by = 25
|
|
|
|
def get_queryset(self):
|
|
queryset = EquipmentUsage.objects.filter(surgical_case__admission__tenant=self.request.user.tenant)
|
|
|
|
# Filter by equipment type
|
|
equipment_type = self.request.GET.get('equipment_type')
|
|
if equipment_type:
|
|
queryset = queryset.filter(equipment_type=equipment_type)
|
|
|
|
# Filter by status
|
|
status = self.request.GET.get('status')
|
|
if status:
|
|
queryset = queryset.filter(status=status)
|
|
|
|
# Filter by operating room
|
|
room_id = self.request.GET.get('room')
|
|
if room_id:
|
|
queryset = queryset.filter(operating_room_id=room_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_time__date__gte=date_from)
|
|
if date_to:
|
|
queryset = queryset.filter(start_time__date__lte=date_to)
|
|
|
|
return queryset.select_related(
|
|
'operating_room', 'surgical_case__patient'
|
|
).order_by('-start_time')
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context.update({
|
|
'equipment_types': EquipmentUsage.EQUIPMENT_TYPE_CHOICES,
|
|
# 'statuses': EquipmentUsage._meta.get_field('status').choices,
|
|
'operating_rooms': OperatingRoom.objects.filter(
|
|
tenant=self.request.user.tenant,
|
|
is_active=True
|
|
).order_by('room_number'),
|
|
})
|
|
return context
|
|
|
|
|
|
class EquipmentUsageDetailView(LoginRequiredMixin, DetailView):
|
|
"""
|
|
Display detailed information about equipment usage.
|
|
"""
|
|
model = EquipmentUsage
|
|
template_name = 'operating_theatre/equipment_usage_detail.html'
|
|
context_object_name = 'equipment_usage'
|
|
|
|
def get_queryset(self):
|
|
return EquipmentUsage.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
equipment_usage = self.object
|
|
|
|
# Calculate usage duration if ended
|
|
if equipment_usage.end_time:
|
|
context['usage_duration'] = equipment_usage.end_time - equipment_usage.start_time
|
|
|
|
return context
|
|
|
|
|
|
class EquipmentUsageCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
"""
|
|
Create a new equipment usage record.
|
|
"""
|
|
model = EquipmentUsage
|
|
form_class = EquipmentUsageForm
|
|
template_name = 'operating_theatre/equipment/equipment_form.html'
|
|
permission_required = 'operating_theatre.add_equipmentusage'
|
|
success_url = reverse_lazy('operating_theatre:equipment_usage_list')
|
|
|
|
def form_valid(self, form):
|
|
form.instance.tenant = self.request.user.tenant
|
|
response = super().form_valid(form)
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=self.request.user,
|
|
action='EQUIPMENT_USAGE_CREATED',
|
|
model='EquipmentUsage',
|
|
object_id=str(self.object.id),
|
|
details={
|
|
'equipment_name': self.object.equipment_name,
|
|
'equipment_type': self.object.equipment_type,
|
|
'operating_room': self.object.operating_room.room_number
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, 'Equipment usage record created successfully.')
|
|
return response
|
|
|
|
|
|
class EquipmentUsageUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
"""
|
|
Update equipment usage record (limited to status and end time).
|
|
"""
|
|
model = EquipmentUsage
|
|
fields = ['status', 'end_time', 'notes'] # Restricted fields
|
|
template_name = 'operating_theatre/equipment_usage_update_form.html'
|
|
permission_required = 'operating_theatre.change_equipmentusage'
|
|
|
|
def get_queryset(self):
|
|
return EquipmentUsage.objects.filter(tenant=self.request.user.tenant)
|
|
|
|
def get_success_url(self):
|
|
return reverse('operating_theatre:equipment_usage_detail', kwargs={'pk': self.object.pk})
|
|
|
|
def form_valid(self, form):
|
|
response = super().form_valid(form)
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=self.request.user,
|
|
action='EQUIPMENT_USAGE_UPDATED',
|
|
model='EquipmentUsage',
|
|
object_id=str(self.object.id),
|
|
details={
|
|
'equipment_name': self.object.equipment_name,
|
|
'changes': form.changed_data
|
|
}
|
|
)
|
|
|
|
messages.success(self.request, 'Equipment usage record updated successfully.')
|
|
return response
|
|
|
|
|
|
# ============================================================================
|
|
# HTMX VIEWS FOR REAL-TIME UPDATES
|
|
# ============================================================================
|
|
|
|
@login_required
|
|
def operating_theatre_stats(request):
|
|
"""
|
|
HTMX endpoint for operating theatre statistics.
|
|
"""
|
|
tenant = request.user.tenant
|
|
today = timezone.now().date()
|
|
|
|
stats = {
|
|
'rooms_available': OperatingRoom.objects.filter(
|
|
tenant=tenant,
|
|
is_active=True,
|
|
status='AVAILABLE'
|
|
).count(),
|
|
'rooms_in_use': OperatingRoom.objects.filter(
|
|
tenant=tenant,
|
|
is_active=True,
|
|
status='IN_USE'
|
|
).count(),
|
|
'cases_in_progress': SurgicalCase.objects.filter(
|
|
admission__tenant=tenant,
|
|
status='IN_PROGRESS'
|
|
).count(),
|
|
'cases_completed_today': SurgicalCase.objects.filter(
|
|
admission__tenant=tenant,
|
|
actual_end__date=today,
|
|
status='COMPLETED'
|
|
).count(),
|
|
'emergency_cases_today': SurgicalCase.objects.filter(
|
|
admission__tenant=tenant,
|
|
scheduled_start__date=today,
|
|
case_type='EMERGENCY'
|
|
).count(),
|
|
}
|
|
|
|
return render(request, 'operating_theatre/partials/or_stats.html', {'stats': stats})
|
|
|
|
|
|
@login_required
|
|
def case_search(request):
|
|
"""
|
|
HTMX endpoint for surgical case search.
|
|
"""
|
|
search = request.GET.get('search', '')
|
|
status = request.GET.get('status', '')
|
|
priority = request.GET.get('priority', '')
|
|
|
|
queryset = SurgicalCase.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(procedure_name__icontains=search)
|
|
)
|
|
|
|
if status:
|
|
queryset = queryset.filter(status=status)
|
|
|
|
if priority:
|
|
queryset = queryset.filter(priority=priority)
|
|
|
|
cases = queryset.select_related(
|
|
'patient', 'primary_surgeon', 'operating_room'
|
|
).order_by('-scheduled_start_time')[:20]
|
|
|
|
return render(request, 'operating_theatre/partials/case_list.html', {'cases': cases})
|
|
|
|
|
|
# ============================================================================
|
|
# ACTION VIEWS
|
|
# ============================================================================
|
|
|
|
@login_required
|
|
def start_case(request, case_id):
|
|
"""
|
|
Start a surgical case.
|
|
"""
|
|
if request.method == 'POST':
|
|
case = get_object_or_404(
|
|
SurgicalCase,
|
|
id=case_id,
|
|
tenant=request.user.tenant
|
|
)
|
|
|
|
case.status = 'IN_PROGRESS'
|
|
case.actual_start_time = timezone.now()
|
|
case.save()
|
|
|
|
# Update room status
|
|
if case.operating_room:
|
|
case.operating_room.status = 'IN_USE'
|
|
case.operating_room.save()
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=request.user,
|
|
action='SURGICAL_CASE_STARTED',
|
|
model='SurgicalCase',
|
|
object_id=str(case.case_id),
|
|
details={
|
|
'patient_name': f"{case.patient.first_name} {case.patient.last_name}",
|
|
'procedure_name': case.procedure_name
|
|
}
|
|
)
|
|
|
|
messages.success(request, 'Surgical case started successfully.')
|
|
|
|
if request.headers.get('HX-Request'):
|
|
return render(request, 'operating_theatre/partials/case_status.html', {'case': case})
|
|
|
|
return redirect('operating_theatre:surgical_case_detail', pk=case.pk)
|
|
|
|
return JsonResponse({'success': False})
|
|
|
|
|
|
@login_required
|
|
def complete_case(request, case_id):
|
|
"""
|
|
Complete a surgical case.
|
|
"""
|
|
if request.method == 'POST':
|
|
case = get_object_or_404(
|
|
SurgicalCase,
|
|
id=case_id,
|
|
tenant=request.user.tenant
|
|
)
|
|
|
|
case.status = 'COMPLETED'
|
|
case.actual_end_time = timezone.now()
|
|
case.save()
|
|
|
|
# Update room status
|
|
if case.operating_room:
|
|
case.operating_room.status = 'CLEANING'
|
|
case.operating_room.save()
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=request.user,
|
|
action='SURGICAL_CASE_COMPLETED',
|
|
model='SurgicalCase',
|
|
object_id=str(case.case_id),
|
|
details={
|
|
'patient_name': f"{case.patient.first_name} {case.patient.last_name}",
|
|
'procedure_name': case.procedure_name,
|
|
'duration': str(case.actual_end_time - case.actual_start_time) if case.actual_start_time else None
|
|
}
|
|
)
|
|
|
|
messages.success(request, 'Surgical case completed successfully.')
|
|
|
|
if request.headers.get('HX-Request'):
|
|
return render(request, 'operating_theatre/partials/case_status.html', {'case': case})
|
|
|
|
return redirect('operating_theatre:surgical_case_detail', pk=case.pk)
|
|
|
|
return JsonResponse({'success': False})
|
|
|
|
|
|
@login_required
|
|
def sign_note(request, note_id):
|
|
"""
|
|
Sign a surgical note.
|
|
"""
|
|
if request.method == 'POST':
|
|
note = get_object_or_404(
|
|
SurgicalNote,
|
|
id=note_id,
|
|
tenant=request.user.tenant
|
|
)
|
|
|
|
# Only allow signing if note is in draft status
|
|
if note.status != 'DRAFT':
|
|
messages.error(request, 'Only draft notes can be signed.')
|
|
return redirect('operating_theatre:surgical_note_detail', pk=note.pk)
|
|
|
|
note.status = 'SIGNED'
|
|
note.signed_datetime = timezone.now()
|
|
note.signed_by = request.user
|
|
note.save()
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=request.user,
|
|
action='SURGICAL_NOTE_SIGNED',
|
|
model='SurgicalNote',
|
|
object_id=str(note.note_id),
|
|
details={
|
|
'patient_name': f"{note.surgical_case.patient.first_name} {note.surgical_case.patient.last_name}",
|
|
'note_type': note.note_type
|
|
}
|
|
)
|
|
|
|
messages.success(request, 'Surgical note signed successfully.')
|
|
|
|
if request.headers.get('HX-Request'):
|
|
return render(request, 'operating_theatre/partials/note_status.html', {'note': note})
|
|
|
|
return redirect('operating_theatre:surgical_note_detail', pk=note.pk)
|
|
|
|
return JsonResponse({'success': False})
|
|
|
|
|
|
@login_required
|
|
def update_room_status(request, room_id):
|
|
"""
|
|
Update operating room status.
|
|
"""
|
|
if request.method == 'POST':
|
|
room = get_object_or_404(
|
|
OperatingRoom,
|
|
id=room_id,
|
|
tenant=request.user.tenant
|
|
)
|
|
|
|
new_status = request.POST.get('status')
|
|
if new_status in dict(OperatingRoom._meta.get_field('status').choices):
|
|
old_status = room.status
|
|
room.status = new_status
|
|
room.save()
|
|
|
|
# Log the action
|
|
AuditLogger.log_action(
|
|
user=request.user,
|
|
action='OPERATING_ROOM_STATUS_UPDATED',
|
|
model='OperatingRoom',
|
|
object_id=str(room.id),
|
|
details={
|
|
'room_number': room.room_number,
|
|
'old_status': old_status,
|
|
'new_status': new_status
|
|
}
|
|
)
|
|
|
|
messages.success(request, f'Room {room.room_number} status updated to {room.get_status_display()}.')
|
|
|
|
if request.headers.get('HX-Request'):
|
|
return render(request, 'operating_theatre/partials/room_status.html', {'room': room})
|
|
|
|
return redirect('operating_theatre:operating_room_detail', pk=room.pk)
|
|
|
|
return JsonResponse({'success': False})
|
|
|
|
#
|
|
# """
|
|
# Operating Theatre app views with healthcare-focused CRUD operations.
|
|
# Implements appropriate access patterns for surgical and OR management workflows.
|
|
# """
|
|
#
|
|
# 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 (
|
|
# ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView
|
|
# )
|
|
# from django.http import JsonResponse, HttpResponse
|
|
# from django.db.models import Q, Count, Avg, Sum, F
|
|
# from django.utils import timezone
|
|
# from django.contrib import messages
|
|
# from django.urls import reverse_lazy, reverse
|
|
# from django.core.paginator import Paginator
|
|
# from django.template.loader import render_to_string
|
|
# from datetime import datetime, timedelta, date
|
|
# import json
|
|
#
|
|
# from core.utils import AuditLogger
|
|
# from .models import (
|
|
# OperatingRoom, ORBlock, SurgicalCase, SurgicalNote,
|
|
# EquipmentUsage, SurgicalNoteTemplate
|
|
# )
|
|
# from .forms import (
|
|
# OperatingRoomForm, ORBlockForm, SurgicalCaseForm, SurgicalNoteForm,
|
|
# EquipmentUsageForm, SurgicalNoteTemplateForm
|
|
# )
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # DASHBOARD AND OVERVIEW VIEWS
|
|
# # ============================================================================
|
|
#
|
|
# class OperatingTheatreDashboardView(LoginRequiredMixin, TemplateView):
|
|
# """
|
|
# Main operating theatre dashboard with key metrics and recent activity.
|
|
# """
|
|
# template_name = 'operating_theatre/dashboard.html'
|
|
#
|
|
# 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_rooms': OperatingRoom.objects.filter(
|
|
# tenant=tenant,
|
|
# is_active=True
|
|
# ).count(),
|
|
# 'rooms_available': OperatingRoom.objects.filter(
|
|
# tenant=tenant,
|
|
# is_active=True,
|
|
# status='AVAILABLE'
|
|
# ).count(),
|
|
# 'rooms_in_use': OperatingRoom.objects.filter(
|
|
# tenant=tenant,
|
|
# is_active=True,
|
|
# status='IN_USE'
|
|
# ).count(),
|
|
# 'rooms_maintenance': OperatingRoom.objects.filter(
|
|
# tenant=tenant,
|
|
# is_active=True,
|
|
# status='MAINTENANCE'
|
|
# ).count(),
|
|
# 'cases_today': SurgicalCase.objects.filter(
|
|
# tenant=tenant,
|
|
# scheduled_start_time__date=today
|
|
# ).count(),
|
|
# 'cases_in_progress': SurgicalCase.objects.filter(
|
|
# tenant=tenant,
|
|
# status='IN_PROGRESS'
|
|
# ).count(),
|
|
# 'cases_completed_today': SurgicalCase.objects.filter(
|
|
# tenant=tenant,
|
|
# actual_end_time__date=today,
|
|
# status='COMPLETED'
|
|
# ).count(),
|
|
# 'emergency_cases_today': SurgicalCase.objects.filter(
|
|
# tenant=tenant,
|
|
# scheduled_start_time__date=today,
|
|
# priority='EMERGENCY'
|
|
# ).count(),
|
|
# 'blocks_today': ORBlock.objects.filter(
|
|
# tenant=tenant,
|
|
# date=today
|
|
# ).count(),
|
|
# 'equipment_in_use': EquipmentUsage.objects.filter(
|
|
# tenant=tenant,
|
|
# status='IN_USE'
|
|
# ).count(),
|
|
# 'notes_pending': SurgicalNote.objects.filter(
|
|
# tenant=tenant,
|
|
# status='DRAFT'
|
|
# ).count(),
|
|
# })
|
|
#
|
|
# # Recent surgical cases
|
|
# context['recent_cases'] = SurgicalCase.objects.filter(
|
|
# tenant=tenant
|
|
# ).select_related(
|
|
# 'patient', 'primary_surgeon', 'operating_room'
|
|
# ).order_by('-scheduled_start_time')[:10]
|
|
#
|
|
# # Today's schedule
|
|
# context['todays_schedule'] = SurgicalCase.objects.filter(
|
|
# tenant=tenant,
|
|
# scheduled_start_time__date=today
|
|
# ).select_related(
|
|
# 'patient', 'primary_surgeon', 'operating_room'
|
|
# ).order_by('scheduled_start_time')
|
|
#
|
|
# # Room utilization
|
|
# context['room_utilization'] = OperatingRoom.objects.filter(
|
|
# tenant=tenant,
|
|
# is_active=True
|
|
# ).annotate(
|
|
# cases_today=Count(
|
|
# 'surgical_cases',
|
|
# filter=Q(surgical_cases__scheduled_start_time__date=today)
|
|
# )
|
|
# ).order_by('room_number')
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # OPERATING ROOM VIEWS (FULL CRUD - Master Data)
|
|
# # ============================================================================
|
|
#
|
|
# class OperatingRoomListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List all operating rooms with filtering and search.
|
|
# """
|
|
# model = OperatingRoom
|
|
# template_name = 'operating_theatre/operating_room_list.html'
|
|
# context_object_name = 'operating_rooms'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# # Search functionality
|
|
# search = self.request.GET.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(room_number__icontains=search) |
|
|
# Q(room_name__icontains=search) |
|
|
# Q(location__icontains=search)
|
|
# )
|
|
#
|
|
# # Filter by room type
|
|
# room_type = self.request.GET.get('room_type')
|
|
# if room_type:
|
|
# queryset = queryset.filter(room_type=room_type)
|
|
#
|
|
# # Filter by status
|
|
# status = self.request.GET.get('status')
|
|
# if status:
|
|
# queryset = queryset.filter(status=status)
|
|
#
|
|
# # Filter by floor
|
|
# floor = self.request.GET.get('floor')
|
|
# if floor:
|
|
# queryset = queryset.filter(floor=floor)
|
|
#
|
|
# # Filter by active status
|
|
# active_only = self.request.GET.get('active_only')
|
|
# if active_only:
|
|
# queryset = queryset.filter(is_active=True)
|
|
#
|
|
# return queryset.order_by('room_number')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# context.update({
|
|
# 'room_types': OperatingRoom._meta.get_field('room_type').choices,
|
|
# 'statuses': OperatingRoom._meta.get_field('status').choices,
|
|
# 'search_query': self.request.GET.get('search', ''),
|
|
# })
|
|
# return context
|
|
#
|
|
#
|
|
# class OperatingRoomDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Display detailed information about an operating room.
|
|
# """
|
|
# model = OperatingRoom
|
|
# template_name = 'operating_theatre/operating_room_detail.html'
|
|
# context_object_name = 'operating_room'
|
|
#
|
|
# def get_queryset(self):
|
|
# return OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# operating_room = self.object
|
|
# today = timezone.now().date()
|
|
#
|
|
# # Get today's cases for this room
|
|
# context['todays_cases'] = operating_room.surgical_cases.filter(
|
|
# scheduled_start_time__date=today
|
|
# ).select_related('patient', 'primary_surgeon').order_by('scheduled_start_time')
|
|
#
|
|
# # Get recent cases
|
|
# context['recent_cases'] = operating_room.surgical_cases.all().select_related(
|
|
# 'patient', 'primary_surgeon'
|
|
# ).order_by('-scheduled_start_time')[:10]
|
|
#
|
|
# # Get equipment usage
|
|
# context['equipment_usage'] = EquipmentUsage.objects.filter(
|
|
# operating_room=operating_room,
|
|
# tenant=self.request.user.tenant
|
|
# ).order_by('-start_time')[:10]
|
|
#
|
|
# # Room statistics
|
|
# context['room_stats'] = {
|
|
# 'total_cases': operating_room.surgical_cases.count(),
|
|
# 'cases_this_month': operating_room.surgical_cases.filter(
|
|
# scheduled_start_time__month=timezone.now().month,
|
|
# scheduled_start_time__year=timezone.now().year
|
|
# ).count(),
|
|
# 'average_case_duration': operating_room.surgical_cases.filter(
|
|
# actual_end_time__isnull=False
|
|
# ).aggregate(
|
|
# avg_duration=Avg(
|
|
# F('actual_end_time') - F('actual_start_time')
|
|
# )
|
|
# )['avg_duration'],
|
|
# }
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class OperatingRoomCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create a new operating room.
|
|
# """
|
|
# model = OperatingRoom
|
|
# form_class = OperatingRoomForm
|
|
# template_name = 'operating_theatre/operating_room_form.html'
|
|
# permission_required = 'operating_theatre.add_operatingroom'
|
|
# success_url = reverse_lazy('operating_theatre:operating_room_list')
|
|
#
|
|
# def form_valid(self, form):
|
|
# form.instance.tenant = self.request.user.tenant
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=self.request.user,
|
|
# action='OPERATING_ROOM_CREATED',
|
|
# model='OperatingRoom',
|
|
# object_id=str(self.object.id),
|
|
# details={
|
|
# 'room_number': self.object.room_number,
|
|
# 'room_name': self.object.room_name,
|
|
# 'room_type': self.object.room_type
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Operating room "{self.object.room_number}" created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class OperatingRoomUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
# """
|
|
# Update an existing operating room.
|
|
# """
|
|
# model = OperatingRoom
|
|
# form_class = OperatingRoomForm
|
|
# template_name = 'operating_theatre/operating_room_form.html'
|
|
# permission_required = 'operating_theatre.change_operatingroom'
|
|
#
|
|
# def get_queryset(self):
|
|
# return OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('operating_theatre:operating_room_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=self.request.user,
|
|
# action='OPERATING_ROOM_UPDATED',
|
|
# model='OperatingRoom',
|
|
# object_id=str(self.object.id),
|
|
# details={
|
|
# 'room_number': self.object.room_number,
|
|
# 'changes': form.changed_data
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Operating room "{self.object.room_number}" updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class OperatingRoomDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
# """
|
|
# Delete an operating room (soft delete by deactivating).
|
|
# """
|
|
# model = OperatingRoom
|
|
# template_name = 'operating_theatre/operating_room_confirm_delete.html'
|
|
# permission_required = 'operating_theatre.delete_operatingroom'
|
|
# success_url = reverse_lazy('operating_theatre:operating_room_list')
|
|
#
|
|
# def get_queryset(self):
|
|
# return OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def delete(self, request, *args, **kwargs):
|
|
# self.object = self.get_object()
|
|
#
|
|
# # Check if room has active cases
|
|
# active_cases = self.object.surgical_cases.filter(
|
|
# status__in=['SCHEDULED', 'IN_PROGRESS']
|
|
# ).count()
|
|
#
|
|
# if active_cases > 0:
|
|
# messages.error(
|
|
# request,
|
|
# f'Cannot deactivate room "{self.object.room_number}" - it has {active_cases} active cases.'
|
|
# )
|
|
# return redirect('operating_theatre:operating_room_detail', pk=self.object.pk)
|
|
#
|
|
# # Soft delete by deactivating
|
|
# self.object.is_active = False
|
|
# self.object.status = 'OUT_OF_SERVICE'
|
|
# self.object.save()
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=request.user,
|
|
# action='OPERATING_ROOM_DEACTIVATED',
|
|
# model='OperatingRoom',
|
|
# object_id=str(self.object.id),
|
|
# details={'room_number': self.object.room_number}
|
|
# )
|
|
#
|
|
# messages.success(request, f'Operating room "{self.object.room_number}" deactivated successfully.')
|
|
# return redirect(self.success_url)
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # SURGICAL NOTE TEMPLATE VIEWS (FULL CRUD - Master Data)
|
|
# # ============================================================================
|
|
#
|
|
# class SurgicalNoteTemplateListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List all surgical note templates with filtering and search.
|
|
# """
|
|
# model = SurgicalNoteTemplate
|
|
# template_name = 'operating_theatre/surgical_note_template_list.html'
|
|
# context_object_name = 'surgical_note_templates'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# # Search functionality
|
|
# search = self.request.GET.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(template_name__icontains=search) |
|
|
# Q(procedure_type__icontains=search) |
|
|
# Q(specialty__icontains=search)
|
|
# )
|
|
#
|
|
# # Filter by specialty
|
|
# specialty = self.request.GET.get('specialty')
|
|
# if specialty:
|
|
# queryset = queryset.filter(specialty=specialty)
|
|
#
|
|
# # Filter by active status
|
|
# active_only = self.request.GET.get('active_only')
|
|
# if active_only:
|
|
# queryset = queryset.filter(is_active=True)
|
|
#
|
|
# return queryset.order_by('template_name')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# context.update({
|
|
# 'specialties': SurgicalNoteTemplate._meta.get_field('specialty').choices,
|
|
# 'search_query': self.request.GET.get('search', ''),
|
|
# })
|
|
# return context
|
|
#
|
|
#
|
|
# class SurgicalNoteTemplateDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Display detailed information about a surgical note template.
|
|
# """
|
|
# model = SurgicalNoteTemplate
|
|
# template_name = 'operating_theatre/surgical_note_template_detail.html'
|
|
# context_object_name = 'surgical_note_template'
|
|
#
|
|
# def get_queryset(self):
|
|
# return SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# template = self.object
|
|
#
|
|
# # Get recent notes using this template
|
|
# context['recent_notes'] = SurgicalNote.objects.filter(
|
|
# template=template,
|
|
# tenant=self.request.user.tenant
|
|
# ).select_related('surgical_case__patient').order_by('-created_at')[:10]
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class SurgicalNoteTemplateCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create a new surgical note template.
|
|
# """
|
|
# model = SurgicalNoteTemplate
|
|
# form_class = SurgicalNoteTemplateForm
|
|
# template_name = 'operating_theatre/surgical_note_template_form.html'
|
|
# permission_required = 'operating_theatre.add_surgicalnotetemplate'
|
|
# success_url = reverse_lazy('operating_theatre:surgical_note_template_list')
|
|
#
|
|
# def form_valid(self, form):
|
|
# form.instance.tenant = self.request.user.tenant
|
|
# form.instance.created_by = self.request.user
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=self.request.user,
|
|
# action='SURGICAL_NOTE_TEMPLATE_CREATED',
|
|
# model='SurgicalNoteTemplate',
|
|
# object_id=str(self.object.id),
|
|
# details={
|
|
# 'template_name': self.object.template_name,
|
|
# 'specialty': self.object.specialty
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Surgical note template "{self.object.template_name}" created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class SurgicalNoteTemplateUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
# """
|
|
# Update an existing surgical note template.
|
|
# """
|
|
# model = SurgicalNoteTemplate
|
|
# form_class = SurgicalNoteTemplateForm
|
|
# template_name = 'operating_theatre/surgical_note_template_form.html'
|
|
# permission_required = 'operating_theatre.change_surgicalnotetemplate'
|
|
#
|
|
# def get_queryset(self):
|
|
# return SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('operating_theatre:surgical_note_template_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=self.request.user,
|
|
# action='SURGICAL_NOTE_TEMPLATE_UPDATED',
|
|
# model='SurgicalNoteTemplate',
|
|
# object_id=str(self.object.id),
|
|
# details={
|
|
# 'template_name': self.object.template_name,
|
|
# 'changes': form.changed_data
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, f'Surgical note template "{self.object.template_name}" updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class SurgicalNoteTemplateDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
|
# """
|
|
# Delete a surgical note template (soft delete by deactivating).
|
|
# """
|
|
# model = SurgicalNoteTemplate
|
|
# template_name = 'operating_theatre/surgical_note_template_confirm_delete.html'
|
|
# permission_required = 'operating_theatre.delete_surgicalnotetemplate'
|
|
# success_url = reverse_lazy('operating_theatre:surgical_note_template_list')
|
|
#
|
|
# def get_queryset(self):
|
|
# return SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def delete(self, request, *args, **kwargs):
|
|
# self.object = self.get_object()
|
|
#
|
|
# # Soft delete by deactivating
|
|
# self.object.is_active = False
|
|
# self.object.save()
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=request.user,
|
|
# action='SURGICAL_NOTE_TEMPLATE_DEACTIVATED',
|
|
# model='SurgicalNoteTemplate',
|
|
# object_id=str(self.object.id),
|
|
# details={'template_name': self.object.template_name}
|
|
# )
|
|
#
|
|
# messages.success(request, f'Surgical note template "{self.object.template_name}" deactivated successfully.')
|
|
# return redirect(self.success_url)
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # OR BLOCK VIEWS (LIMITED CRUD - Operational Data)
|
|
# # ============================================================================
|
|
#
|
|
# class ORBlockListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List all OR blocks with filtering and search.
|
|
# """
|
|
# model = ORBlock
|
|
# template_name = 'operating_theatre/or_block_list.html'
|
|
# context_object_name = 'or_blocks'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = ORBlock.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# # 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(date__gte=date_from)
|
|
# if date_to:
|
|
# queryset = queryset.filter(date__lte=date_to)
|
|
#
|
|
# # Filter by surgeon
|
|
# surgeon_id = self.request.GET.get('surgeon')
|
|
# if surgeon_id:
|
|
# queryset = queryset.filter(surgeon_id=surgeon_id)
|
|
#
|
|
# # Filter by operating room
|
|
# room_id = self.request.GET.get('room')
|
|
# if room_id:
|
|
# queryset = queryset.filter(operating_room_id=room_id)
|
|
#
|
|
# return queryset.select_related(
|
|
# 'operating_room', 'surgeon'
|
|
# ).order_by('-date', 'start_time')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# context.update({
|
|
# 'operating_rooms': OperatingRoom.objects.filter(
|
|
# tenant=self.request.user.tenant,
|
|
# is_active=True
|
|
# ).order_by('room_number'),
|
|
# })
|
|
# return context
|
|
#
|
|
#
|
|
# class ORBlockDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Display detailed information about an OR block.
|
|
# """
|
|
# model = ORBlock
|
|
# template_name = 'operating_theatre/or_block_detail.html'
|
|
# context_object_name = 'or_block'
|
|
#
|
|
# def get_queryset(self):
|
|
# return ORBlock.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# or_block = self.object
|
|
#
|
|
# # Get cases scheduled in this block
|
|
# context['scheduled_cases'] = SurgicalCase.objects.filter(
|
|
# operating_room=or_block.operating_room,
|
|
# scheduled_start_time__date=or_block.date,
|
|
# scheduled_start_time__time__gte=or_block.start_time,
|
|
# scheduled_start_time__time__lt=or_block.end_time,
|
|
# tenant=self.request.user.tenant
|
|
# ).select_related('patient', 'primary_surgeon').order_by('scheduled_start_time')
|
|
#
|
|
# # Calculate utilization
|
|
# total_block_minutes = (
|
|
# timezone.datetime.combine(timezone.now().date(), or_block.end_time) -
|
|
# timezone.datetime.combine(timezone.now().date(), or_block.start_time)
|
|
# ).total_seconds() / 60
|
|
#
|
|
# used_minutes = 0
|
|
# for case in context['scheduled_cases']:
|
|
# if case.estimated_duration_minutes:
|
|
# used_minutes += case.estimated_duration_minutes
|
|
#
|
|
# context['utilization_percentage'] = (
|
|
# (used_minutes / total_block_minutes) * 100 if total_block_minutes > 0 else 0
|
|
# )
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class ORBlockCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create a new OR block.
|
|
# """
|
|
# model = ORBlock
|
|
# form_class = ORBlockForm
|
|
# template_name = 'operating_theatre/or_block_form.html'
|
|
# permission_required = 'operating_theatre.add_orblock'
|
|
# success_url = reverse_lazy('operating_theatre:or_block_list')
|
|
#
|
|
# def form_valid(self, form):
|
|
# form.instance.tenant = self.request.user.tenant
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=self.request.user,
|
|
# action='OR_BLOCK_CREATED',
|
|
# model='ORBlock',
|
|
# object_id=str(self.object.id),
|
|
# details={
|
|
# 'date': str(self.object.date),
|
|
# 'operating_room': self.object.operating_room.room_number,
|
|
# 'surgeon': f"{self.object.surgeon.first_name} {self.object.surgeon.last_name}"
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'OR block created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class ORBlockUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
# """
|
|
# Update an OR block (limited to notes and time adjustments).
|
|
# """
|
|
# model = ORBlock
|
|
# fields = ['start_time', 'end_time', 'notes'] # Restricted fields
|
|
# template_name = 'operating_theatre/or_block_update_form.html'
|
|
# permission_required = 'operating_theatre.change_orblock'
|
|
#
|
|
# def get_queryset(self):
|
|
# return ORBlock.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('operating_theatre:or_block_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=self.request.user,
|
|
# action='OR_BLOCK_UPDATED',
|
|
# model='ORBlock',
|
|
# object_id=str(self.object.id),
|
|
# details={
|
|
# 'date': str(self.object.date),
|
|
# 'changes': form.changed_data
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'OR block updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # SURGICAL CASE VIEWS (RESTRICTED CRUD - Clinical Data)
|
|
# # ============================================================================
|
|
#
|
|
# class SurgicalCaseListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List all surgical cases with filtering and search.
|
|
# """
|
|
# model = SurgicalCase
|
|
# template_name = 'operating_theatre/surgical_case_list.html'
|
|
# context_object_name = 'surgical_cases'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = SurgicalCase.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(procedure_name__icontains=search)
|
|
# )
|
|
#
|
|
# # Filter by status
|
|
# status = self.request.GET.get('status')
|
|
# if status:
|
|
# queryset = queryset.filter(status=status)
|
|
#
|
|
# # Filter by priority
|
|
# priority = self.request.GET.get('priority')
|
|
# if priority:
|
|
# queryset = queryset.filter(priority=priority)
|
|
#
|
|
# # Filter by surgeon
|
|
# surgeon_id = self.request.GET.get('surgeon')
|
|
# if surgeon_id:
|
|
# queryset = queryset.filter(primary_surgeon_id=surgeon_id)
|
|
#
|
|
# # Filter by operating room
|
|
# room_id = self.request.GET.get('room')
|
|
# if room_id:
|
|
# queryset = queryset.filter(operating_room_id=room_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(scheduled_start_time__date__gte=date_from)
|
|
# if date_to:
|
|
# queryset = queryset.filter(scheduled_start_time__date__lte=date_to)
|
|
#
|
|
# return queryset.select_related(
|
|
# 'patient', 'primary_surgeon', 'operating_room'
|
|
# ).order_by('-scheduled_start_time')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# context.update({
|
|
# 'statuses': SurgicalCase._meta.get_field('status').choices,
|
|
# 'priorities': SurgicalCase._meta.get_field('priority').choices,
|
|
# 'operating_rooms': OperatingRoom.objects.filter(
|
|
# tenant=self.request.user.tenant,
|
|
# is_active=True
|
|
# ).order_by('room_number'),
|
|
# })
|
|
# return context
|
|
#
|
|
#
|
|
# class SurgicalCaseDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Display detailed information about a surgical case.
|
|
# """
|
|
# model = SurgicalCase
|
|
# template_name = 'operating_theatre/surgical_case_detail.html'
|
|
# context_object_name = 'surgical_case'
|
|
#
|
|
# def get_queryset(self):
|
|
# return SurgicalCase.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# surgical_case = self.object
|
|
#
|
|
# # Get surgical notes for this case
|
|
# context['surgical_notes'] = surgical_case.surgical_notes.all().order_by('-created_at')
|
|
#
|
|
# # Get equipment usage for this case
|
|
# context['equipment_usage'] = EquipmentUsage.objects.filter(
|
|
# surgical_case=surgical_case,
|
|
# tenant=self.request.user.tenant
|
|
# ).order_by('-start_time')
|
|
#
|
|
# # Calculate actual duration if case is completed
|
|
# if surgical_case.actual_start_time and surgical_case.actual_end_time:
|
|
# context['actual_duration'] = surgical_case.actual_end_time - surgical_case.actual_start_time
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class SurgicalCaseCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create a new surgical case.
|
|
# """
|
|
# model = SurgicalCase
|
|
# form_class = SurgicalCaseForm
|
|
# template_name = 'operating_theatre/surgical_case_form.html'
|
|
# permission_required = 'operating_theatre.add_surgicalcase'
|
|
# success_url = reverse_lazy('operating_theatre:surgical_case_list')
|
|
#
|
|
# def form_valid(self, form):
|
|
# form.instance.tenant = self.request.user.tenant
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=self.request.user,
|
|
# action='SURGICAL_CASE_CREATED',
|
|
# model='SurgicalCase',
|
|
# object_id=str(self.object.case_id),
|
|
# details={
|
|
# 'patient_name': f"{self.object.patient.first_name} {self.object.patient.last_name}",
|
|
# 'procedure_name': self.object.procedure_name,
|
|
# 'priority': self.object.priority
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'Surgical case created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class SurgicalCaseUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
# """
|
|
# Update surgical case (limited to status and notes after surgery starts).
|
|
# """
|
|
# model = SurgicalCase
|
|
# template_name = 'operating_theatre/surgical_case_update_form.html'
|
|
# permission_required = 'operating_theatre.change_surgicalcase'
|
|
#
|
|
# def get_queryset(self):
|
|
# return SurgicalCase.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_form_class(self):
|
|
# # Limit fields based on case status
|
|
# if self.object.status in ['IN_PROGRESS', 'COMPLETED']:
|
|
# # Limited fields for cases that have started
|
|
# class RestrictedSurgicalCaseForm(SurgicalCaseForm):
|
|
# class Meta(SurgicalCaseForm.Meta):
|
|
# fields = ['status', 'notes', 'complications']
|
|
#
|
|
# return RestrictedSurgicalCaseForm
|
|
# else:
|
|
# return SurgicalCaseForm
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('operating_theatre:surgical_case_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=self.request.user,
|
|
# action='SURGICAL_CASE_UPDATED',
|
|
# model='SurgicalCase',
|
|
# object_id=str(self.object.case_id),
|
|
# details={
|
|
# 'patient_name': f"{self.object.patient.first_name} {self.object.patient.last_name}",
|
|
# 'changes': form.changed_data
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'Surgical case updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # SURGICAL NOTE VIEWS (APPEND-ONLY - Clinical Records)
|
|
# # ============================================================================
|
|
#
|
|
# class SurgicalNoteListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List all surgical notes with filtering and search.
|
|
# """
|
|
# model = SurgicalNote
|
|
# template_name = 'operating_theatre/surgical_note_list.html'
|
|
# context_object_name = 'surgical_notes'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = SurgicalNote.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# # Search functionality
|
|
# search = self.request.GET.get('search')
|
|
# if search:
|
|
# queryset = queryset.filter(
|
|
# Q(surgical_case__patient__first_name__icontains=search) |
|
|
# Q(surgical_case__patient__last_name__icontains=search) |
|
|
# Q(surgical_case__patient__mrn__icontains=search) |
|
|
# Q(surgical_case__procedure_name__icontains=search) |
|
|
# Q(note_content__icontains=search)
|
|
# )
|
|
#
|
|
# # 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 surgeon
|
|
# surgeon_id = self.request.GET.get('surgeon')
|
|
# if surgeon_id:
|
|
# queryset = queryset.filter(surgeon_id=surgeon_id)
|
|
#
|
|
# return queryset.select_related(
|
|
# 'surgical_case__patient', 'surgeon', 'template'
|
|
# ).order_by('-created_at')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# context.update({
|
|
# 'note_types': SurgicalNote._meta.get_field('note_type').choices,
|
|
# 'statuses': SurgicalNote._meta.get_field('status').choices,
|
|
# })
|
|
# return context
|
|
#
|
|
#
|
|
# class SurgicalNoteDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Display detailed information about a surgical note.
|
|
# """
|
|
# model = SurgicalNote
|
|
# template_name = 'operating_theatre/surgical_note_detail.html'
|
|
# context_object_name = 'surgical_note'
|
|
#
|
|
# def get_queryset(self):
|
|
# return SurgicalNote.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
#
|
|
# class SurgicalNoteCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create a new surgical note.
|
|
# """
|
|
# model = SurgicalNote
|
|
# form_class = SurgicalNoteForm
|
|
# template_name = 'operating_theatre/surgical_note_form.html'
|
|
# permission_required = 'operating_theatre.add_surgicalnote'
|
|
# success_url = reverse_lazy('operating_theatre:surgical_note_list')
|
|
#
|
|
# def form_valid(self, form):
|
|
# form.instance.tenant = self.request.user.tenant
|
|
# form.instance.surgeon = self.request.user
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=self.request.user,
|
|
# action='SURGICAL_NOTE_CREATED',
|
|
# model='SurgicalNote',
|
|
# object_id=str(self.object.note_id),
|
|
# details={
|
|
# 'patient_name': f"{self.object.surgical_case.patient.first_name} {self.object.surgical_case.patient.last_name}",
|
|
# 'note_type': self.object.note_type
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'Surgical note created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# # Note: No UpdateView or DeleteView for SurgicalNote - Append-only for clinical records
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # EQUIPMENT USAGE VIEWS (LIMITED CRUD - Operational Data)
|
|
# # ============================================================================
|
|
#
|
|
# class EquipmentUsageListView(LoginRequiredMixin, ListView):
|
|
# """
|
|
# List all equipment usage records with filtering and search.
|
|
# """
|
|
# model = EquipmentUsage
|
|
# template_name = 'operating_theatre/equipment_usage_list.html'
|
|
# context_object_name = 'equipment_usage_records'
|
|
# paginate_by = 25
|
|
#
|
|
# def get_queryset(self):
|
|
# queryset = EquipmentUsage.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# # Filter by equipment type
|
|
# equipment_type = self.request.GET.get('equipment_type')
|
|
# if equipment_type:
|
|
# queryset = queryset.filter(equipment_type=equipment_type)
|
|
#
|
|
# # Filter by status
|
|
# status = self.request.GET.get('status')
|
|
# if status:
|
|
# queryset = queryset.filter(status=status)
|
|
#
|
|
# # Filter by operating room
|
|
# room_id = self.request.GET.get('room')
|
|
# if room_id:
|
|
# queryset = queryset.filter(operating_room_id=room_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_time__date__gte=date_from)
|
|
# if date_to:
|
|
# queryset = queryset.filter(start_time__date__lte=date_to)
|
|
#
|
|
# return queryset.select_related(
|
|
# 'operating_room', 'surgical_case__patient'
|
|
# ).order_by('-start_time')
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# context.update({
|
|
# 'equipment_types': EquipmentUsage._meta.get_field('equipment_type').choices,
|
|
# 'statuses': EquipmentUsage._meta.get_field('status').choices,
|
|
# 'operating_rooms': OperatingRoom.objects.filter(
|
|
# tenant=self.request.user.tenant,
|
|
# is_active=True
|
|
# ).order_by('room_number'),
|
|
# })
|
|
# return context
|
|
#
|
|
#
|
|
# class EquipmentUsageDetailView(LoginRequiredMixin, DetailView):
|
|
# """
|
|
# Display detailed information about equipment usage.
|
|
# """
|
|
# model = EquipmentUsage
|
|
# template_name = 'operating_theatre/equipment_usage_detail.html'
|
|
# context_object_name = 'equipment_usage'
|
|
#
|
|
# def get_queryset(self):
|
|
# return EquipmentUsage.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_context_data(self, **kwargs):
|
|
# context = super().get_context_data(**kwargs)
|
|
# equipment_usage = self.object
|
|
#
|
|
# # Calculate usage duration if ended
|
|
# if equipment_usage.end_time:
|
|
# context['usage_duration'] = equipment_usage.end_time - equipment_usage.start_time
|
|
#
|
|
# return context
|
|
#
|
|
#
|
|
# class EquipmentUsageCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
|
# """
|
|
# Create a new equipment usage record.
|
|
# """
|
|
# model = EquipmentUsage
|
|
# form_class = EquipmentUsageForm
|
|
# template_name = 'operating_theatre/equipment_usage_form.html'
|
|
# permission_required = 'operating_theatre.add_equipmentusage'
|
|
# success_url = reverse_lazy('operating_theatre:equipment_usage_list')
|
|
#
|
|
# def form_valid(self, form):
|
|
# form.instance.tenant = self.request.user.tenant
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=self.request.user,
|
|
# action='EQUIPMENT_USAGE_CREATED',
|
|
# model='EquipmentUsage',
|
|
# object_id=str(self.object.id),
|
|
# details={
|
|
# 'equipment_name': self.object.equipment_name,
|
|
# 'equipment_type': self.object.equipment_type,
|
|
# 'operating_room': self.object.operating_room.room_number
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'Equipment usage record created successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# class EquipmentUsageUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
|
# """
|
|
# Update equipment usage record (limited to status and end time).
|
|
# """
|
|
# model = EquipmentUsage
|
|
# fields = ['status', 'end_time', 'notes'] # Restricted fields
|
|
# template_name = 'operating_theatre/equipment_usage_update_form.html'
|
|
# permission_required = 'operating_theatre.change_equipmentusage'
|
|
#
|
|
# def get_queryset(self):
|
|
# return EquipmentUsage.objects.filter(tenant=self.request.user.tenant)
|
|
#
|
|
# def get_success_url(self):
|
|
# return reverse('operating_theatre:equipment_usage_detail', kwargs={'pk': self.object.pk})
|
|
#
|
|
# def form_valid(self, form):
|
|
# response = super().form_valid(form)
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=self.request.user,
|
|
# action='EQUIPMENT_USAGE_UPDATED',
|
|
# model='EquipmentUsage',
|
|
# object_id=str(self.object.id),
|
|
# details={
|
|
# 'equipment_name': self.object.equipment_name,
|
|
# 'changes': form.changed_data
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(self.request, 'Equipment usage record updated successfully.')
|
|
# return response
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # HTMX VIEWS FOR REAL-TIME UPDATES
|
|
# # ============================================================================
|
|
#
|
|
# @login_required
|
|
# def operating_theatre_stats(request):
|
|
# """
|
|
# HTMX endpoint for operating theatre statistics.
|
|
# """
|
|
# tenant = request.user.tenant
|
|
# today = timezone.now().date()
|
|
#
|
|
# stats = {
|
|
# 'rooms_available': OperatingRoom.objects.filter(
|
|
# tenant=tenant,
|
|
# is_active=True,
|
|
# status='AVAILABLE'
|
|
# ).count(),
|
|
# 'rooms_in_use': OperatingRoom.objects.filter(
|
|
# tenant=tenant,
|
|
# is_active=True,
|
|
# status='IN_USE'
|
|
# ).count(),
|
|
# 'cases_in_progress': SurgicalCase.objects.filter(
|
|
# tenant=tenant,
|
|
# status='IN_PROGRESS'
|
|
# ).count(),
|
|
# 'cases_completed_today': SurgicalCase.objects.filter(
|
|
# tenant=tenant,
|
|
# actual_end_time__date=today,
|
|
# status='COMPLETED'
|
|
# ).count(),
|
|
# 'emergency_cases_today': SurgicalCase.objects.filter(
|
|
# tenant=tenant,
|
|
# scheduled_start_time__date=today,
|
|
# priority='EMERGENCY'
|
|
# ).count(),
|
|
# }
|
|
#
|
|
# return render(request, 'operating_theatre/partials/or_stats.html', {'stats': stats})
|
|
#
|
|
#
|
|
# @login_required
|
|
# def case_search(request):
|
|
# """
|
|
# HTMX endpoint for surgical case search.
|
|
# """
|
|
# search = request.GET.get('search', '')
|
|
# status = request.GET.get('status', '')
|
|
# priority = request.GET.get('priority', '')
|
|
#
|
|
# queryset = SurgicalCase.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(procedure_name__icontains=search)
|
|
# )
|
|
#
|
|
# if status:
|
|
# queryset = queryset.filter(status=status)
|
|
#
|
|
# if priority:
|
|
# queryset = queryset.filter(priority=priority)
|
|
#
|
|
# cases = queryset.select_related(
|
|
# 'patient', 'primary_surgeon', 'operating_room'
|
|
# ).order_by('-scheduled_start_time')[:20]
|
|
#
|
|
# return render(request, 'operating_theatre/partials/case_list.html', {'cases': cases})
|
|
#
|
|
#
|
|
# # ============================================================================
|
|
# # ACTION VIEWS
|
|
# # ============================================================================
|
|
#
|
|
# @login_required
|
|
# def start_case(request, case_id):
|
|
# """
|
|
# Start a surgical case.
|
|
# """
|
|
# if request.method == 'POST':
|
|
# case = get_object_or_404(
|
|
# SurgicalCase,
|
|
# id=case_id,
|
|
# tenant=request.user.tenant
|
|
# )
|
|
#
|
|
# case.status = 'IN_PROGRESS'
|
|
# case.actual_start_time = timezone.now()
|
|
# case.save()
|
|
#
|
|
# # Update room status
|
|
# if case.operating_room:
|
|
# case.operating_room.status = 'IN_USE'
|
|
# case.operating_room.save()
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=request.user,
|
|
# action='SURGICAL_CASE_STARTED',
|
|
# model='SurgicalCase',
|
|
# object_id=str(case.case_id),
|
|
# details={
|
|
# 'patient_name': f"{case.patient.first_name} {case.patient.last_name}",
|
|
# 'procedure_name': case.procedure_name
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(request, 'Surgical case started successfully.')
|
|
#
|
|
# if request.headers.get('HX-Request'):
|
|
# return render(request, 'operating_theatre/partials/case_status.html', {'case': case})
|
|
#
|
|
# return redirect('operating_theatre:surgical_case_detail', pk=case.pk)
|
|
#
|
|
# return JsonResponse({'success': False})
|
|
#
|
|
#
|
|
# @login_required
|
|
# def complete_case(request, case_id):
|
|
# """
|
|
# Complete a surgical case.
|
|
# """
|
|
# if request.method == 'POST':
|
|
# case = get_object_or_404(
|
|
# SurgicalCase,
|
|
# id=case_id,
|
|
# tenant=request.user.tenant
|
|
# )
|
|
#
|
|
# case.status = 'COMPLETED'
|
|
# case.actual_end_time = timezone.now()
|
|
# case.save()
|
|
#
|
|
# # Update room status
|
|
# if case.operating_room:
|
|
# case.operating_room.status = 'CLEANING'
|
|
# case.operating_room.save()
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=request.user,
|
|
# action='SURGICAL_CASE_COMPLETED',
|
|
# model='SurgicalCase',
|
|
# object_id=str(case.case_id),
|
|
# details={
|
|
# 'patient_name': f"{case.patient.first_name} {case.patient.last_name}",
|
|
# 'procedure_name': case.procedure_name,
|
|
# 'duration': str(case.actual_end_time - case.actual_start_time) if case.actual_start_time else None
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(request, 'Surgical case completed successfully.')
|
|
#
|
|
# if request.headers.get('HX-Request'):
|
|
# return render(request, 'operating_theatre/partials/case_status.html', {'case': case})
|
|
#
|
|
# return redirect('operating_theatre:surgical_case_detail', pk=case.pk)
|
|
#
|
|
# return JsonResponse({'success': False})
|
|
#
|
|
#
|
|
# @login_required
|
|
# def sign_note(request, note_id):
|
|
# """
|
|
# Sign a surgical note.
|
|
# """
|
|
# if request.method == 'POST':
|
|
# note = get_object_or_404(
|
|
# SurgicalNote,
|
|
# id=note_id,
|
|
# tenant=request.user.tenant
|
|
# )
|
|
#
|
|
# # Only allow signing if note is in draft status
|
|
# if note.status != 'DRAFT':
|
|
# messages.error(request, 'Only draft notes can be signed.')
|
|
# return redirect('operating_theatre:surgical_note_detail', pk=note.pk)
|
|
#
|
|
# note.status = 'SIGNED'
|
|
# note.signed_datetime = timezone.now()
|
|
# note.signed_by = request.user
|
|
# note.save()
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=request.user,
|
|
# action='SURGICAL_NOTE_SIGNED',
|
|
# model='SurgicalNote',
|
|
# object_id=str(note.note_id),
|
|
# details={
|
|
# 'patient_name': f"{note.surgical_case.patient.first_name} {note.surgical_case.patient.last_name}",
|
|
# 'note_type': note.note_type
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(request, 'Surgical note signed successfully.')
|
|
#
|
|
# if request.headers.get('HX-Request'):
|
|
# return render(request, 'operating_theatre/partials/note_status.html', {'note': note})
|
|
#
|
|
# return redirect('operating_theatre:surgical_note_detail', pk=note.pk)
|
|
#
|
|
# return JsonResponse({'success': False})
|
|
#
|
|
#
|
|
# @login_required
|
|
# def update_room_status(request, room_id):
|
|
# """
|
|
# Update operating room status.
|
|
# """
|
|
# if request.method == 'POST':
|
|
# room = get_object_or_404(
|
|
# OperatingRoom,
|
|
# id=room_id,
|
|
# tenant=request.user.tenant
|
|
# )
|
|
#
|
|
# new_status = request.POST.get('status')
|
|
# if new_status in dict(OperatingRoom._meta.get_field('status').choices):
|
|
# old_status = room.status
|
|
# room.status = new_status
|
|
# room.save()
|
|
#
|
|
# # Log the action
|
|
# AuditLogger.log_action(
|
|
# user=request.user,
|
|
# action='OPERATING_ROOM_STATUS_UPDATED',
|
|
# model='OperatingRoom',
|
|
# object_id=str(room.id),
|
|
# details={
|
|
# 'room_number': room.room_number,
|
|
# 'old_status': old_status,
|
|
# 'new_status': new_status
|
|
# }
|
|
# )
|
|
#
|
|
# messages.success(request, f'Room {room.room_number} status updated to {room.get_status_display()}.')
|
|
#
|
|
# if request.headers.get('HX-Request'):
|
|
# return render(request, 'operating_theatre/partials/room_status.html', {'room': room})
|
|
#
|
|
# return redirect('operating_theatre:operating_room_detail', pk=room.pk)
|
|
#
|
|
# return JsonResponse({'success': False})
|
|
#
|
|
#
|
|
#
|
|
#
|