Marwan Alwali 2b4c58aa34 update
2025-09-17 14:55:43 +03:00

3057 lines
103 KiB
Python

"""
Views for inpatients app.
"""
from django.conf.locale import te
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy, reverse
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.http import JsonResponse, HttpResponse
from django.db.models import Q, Count, Sum, F, ExpressionWrapper, fields
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.utils.translation import gettext_lazy as _
from core.utils import AuditLogger
from .forms import *
from .models import *
class InpatientDashboardView(LoginRequiredMixin, ListView):
"""
Main dashboard for inpatient management.
"""
template_name = 'inpatients/dashboard.html'
context_object_name = 'wards'
def get_queryset(self):
"""Get wards for current tenant."""
return Ward.objects.filter(
tenant=self.request.user.tenant,
is_active=True
).select_related('nurse_manager').prefetch_related('beds')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
tenant = self.request.user.tenant
# Dashboard statistics
context.update({
'total_beds': Bed.objects.filter(ward__tenant=tenant).count(),
'occupied_beds': Bed.objects.filter(ward__tenant=tenant, status='OCCUPIED').count(),
'available_beds': Bed.objects.filter(ward__tenant=tenant, status='AVAILABLE').count(),
'maintenance_beds': Bed.objects.filter(ward__tenant=tenant, status='MAINTENANCE').count(),
'active_admissions': Admission.objects.filter(tenant=tenant, status='ADMITTED').count(),
'pending_transfers': Transfer.objects.filter(
admission__tenant=tenant,
status__in=['REQUESTED', 'APPROVED', 'SCHEDULED']
).count(),
'pending_surgeries': SurgerySchedule.objects.filter(
admission__tenant=tenant,
status__in=['SCHEDULED', 'CONFIRMED']
).count(),
'pending_discharges': Admission.objects.filter(
tenant=tenant,
status='READY_FOR_DISCHARGE'
),
})
# Recent admissions
context['recent_admissions'] = Admission.objects.filter(
tenant=tenant
).select_related(
'patient', 'admitting_physician', 'current_ward', 'current_bed'
).order_by('-admission_datetime')[:5]
# Upcoming transfers
context['upcoming_transfers'] = Transfer.objects.filter(
admission__tenant=tenant,
status__in=['APPROVED', 'SCHEDULED']
).select_related(
'patient', 'from_ward', 'to_ward', 'requested_by'
).order_by('requested_datetime')[:5]
# Upcoming surgeries
context['upcoming_surgeries'] = SurgerySchedule.objects.filter(
admission__tenant=tenant,
status__in=['SCHEDULED', 'CONFIRMED'],
scheduled_date__gte=timezone.now().date()
).select_related('patient', 'primary_surgeon').order_by('scheduled_date', 'scheduled_start_time')[:5]
return context
class WardListView(LoginRequiredMixin, ListView):
"""
List view for wards.
"""
model = Ward
template_name = 'inpatients/wards/ward_list.html'
context_object_name = 'wards'
paginate_by = 20
def get_queryset(self):
"""Filter wards by tenant and search query."""
queryset = Ward.objects.filter(
tenant=self.request.user.tenant
).select_related('nurse_manager').annotate(
bed_count=Count('beds'),
occupied_bed_count=Count('beds', filter=Q(beds__status='OCCUPIED'))
)
# Handle search query
search_query = self.request.GET.get('search', '')
if search_query:
queryset = queryset.filter(
Q(name__icontains=search_query) |
Q(description__icontains=search_query) |
Q(ward_type__icontains=search_query) |
Q(building__icontains=search_query) |
Q(floor__icontains=search_query) |
Q(nurse_manager__first_name__icontains=search_query) |
Q(nurse_manager__last_name__icontains=search_query)
)
# Handle filter by ward_type
ward_type = self.request.GET.get('ward_type', '')
if ward_type:
queryset = queryset.filter(ward_type=ward_type)
# Handle filter by building
building = self.request.GET.get('building', '')
if building:
queryset = queryset.filter(building=building)
# Handle filter by floor
floor = self.request.GET.get('floor', '')
if floor:
queryset = queryset.filter(floor=floor)
# Handle filter by active status
is_active = self.request.GET.get('is_active', '')
if is_active == 'true':
queryset = queryset.filter(is_active=True)
elif is_active == 'false':
queryset = queryset.filter(is_active=False)
# Handle sort
sort_by = self.request.GET.get('sort', 'name')
if sort_by.startswith('-'):
sort_field = sort_by[1:]
sort_dir = '-'
else:
sort_field = sort_by
sort_dir = ''
if sort_field == 'occupancy':
# Calculate occupancy percentage for sorting
queryset = queryset.annotate(
occupancy=ExpressionWrapper(
(F('occupied_bed_count') * 100.0) / F('bed_count'),
output_field=fields.FloatField()
)
)
queryset = queryset.order_by(f'{sort_dir}occupancy')
else:
queryset = queryset.order_by(sort_by)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
tenant = self.request.user.tenant
# Get unique values for filters
context['ward_types'] = Ward.objects.filter(
tenant=tenant
).values_list('ward_type', flat=True).distinct()
print(context['ward_types'])
context['buildings'] = Ward.objects.filter(
tenant=tenant
).exclude(building__isnull=True).exclude(building='').values_list(
'building', flat=True
).distinct()
context['floors'] = Ward.objects.filter(
tenant=tenant
).exclude(floor__isnull=True).exclude(floor='').values_list(
'floor', flat=True
).distinct()
# Add search query to context
context['search_query'] = self.request.GET.get('search', '')
context['ward_type_filter'] = self.request.GET.get('ward_type', '')
context['building_filter'] = self.request.GET.get('building', '')
context['floor_filter'] = self.request.GET.get('floor', '')
context['is_active_filter'] = self.request.GET.get('is_active', '')
context['sort_by'] = self.request.GET.get('sort', 'name')
return context
class WardDetailView(LoginRequiredMixin, DetailView):
"""
Detail view for a ward.
"""
model = Ward
template_name = 'inpatients/wards/ward_detail.html'
context_object_name = 'ward'
def get_queryset(self):
"""Filter wards by tenant."""
return Ward.objects.filter(
tenant=self.request.user.tenant
).select_related('nurse_manager')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
ward = self.get_object()
# Get beds for this ward with patient information
context['beds'] = Bed.objects.filter(
ward=ward
).select_related(
'current_patient', 'current_admission'
).order_by('room_number', 'bed_number')
# Group beds by room for display
rooms = {}
for bed in context['beds']:
room_num = bed.room_number
if room_num not in rooms:
rooms[room_num] = []
rooms[room_num].append(bed)
context['rooms'] = rooms
# Get ward statistics
context['total_beds'] = context['beds'].count()
context['available_beds'] = context['beds'].filter(status='AVAILABLE').count()
context['occupied_beds'] = context['beds'].filter(status='OCCUPIED').count()
context['maintenance_beds'] = context['beds'].filter(
status__in=['MAINTENANCE', 'OUT_OF_ORDER', 'CLEANING']
).count()
if context['total_beds'] > 0:
context['occupancy_rate'] = (context['occupied_beds'] / context['total_beds']) * 100
else:
context['occupancy_rate'] = 0
# Get recent admissions to this ward
context['recent_admissions'] = Admission.objects.filter(
Q(current_ward=ward) | Q(current_bed__ward=ward),
status__in=['ADMITTED', 'READY_FOR_DISCHARGE']
).select_related(
'patient', 'admitting_physician'
).order_by('-admission_datetime')[:10]
return context
class WardCreateView(LoginRequiredMixin, CreateView):
"""
Create view for a ward.
"""
model = Ward
form_class = WardForm
template_name = 'inpatients/wards/ward_form.html'
permission_required = 'inpatients.add_ward'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.tenant = self.request.user.tenant
form.instance.created_by = self.request.user
messages.success(self.request, _('Ward created successfully'))
return super().form_valid(form)
def get_success_url(self):
return reverse('inpatients:ward_detail', kwargs={'pk': self.object.pk})
class WardUpdateView(LoginRequiredMixin, UpdateView):
"""
Update view for a ward.
"""
model = Ward
form_class = WardForm
template_name = 'inpatients/wards/ward_form.html'
permission_required = 'inpatients.change_ward'
def get_queryset(self):
"""Filter wards by tenant."""
return Ward.objects.filter(tenant=self.request.user.tenant)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
messages.success(self.request, _('Ward updated successfully'))
return super().form_valid(form)
def get_success_url(self):
return reverse('inpatients:ward_detail', kwargs={'pk': self.object.pk})
class WardDeleteView(LoginRequiredMixin, DeleteView):
"""
Delete view for a ward.
"""
model = Ward
template_name = 'inpatients/wards/ward_confirm_delete.html'
permission_required = 'inpatients.delete_ward'
success_url = reverse_lazy('inpatients:ward_list')
def get_queryset(self):
"""Filter wards by tenant."""
return Ward.objects.filter(tenant=self.request.user.tenant)
def delete(self, request, *args, **kwargs):
ward = self.get_object()
# Check if there are beds in this ward
if ward.beds.exists():
messages.error(request, _('Cannot delete ward with beds. Remove beds first.'))
return redirect('inpatients:ward_detail', pk=ward.pk)
messages.success(request, _('Ward deleted successfully'))
return super().delete(request, *args, **kwargs)
class BedListView(LoginRequiredMixin, ListView):
"""
List view for beds.
"""
model = Bed
template_name = 'inpatients/beds/bed_list.html'
context_object_name = 'beds'
paginate_by = 20
def get_queryset(self):
"""Filter beds by tenant and search query."""
queryset = Bed.objects.filter(
ward__tenant=self.request.user.tenant
).select_related('ward', 'current_patient')
# Handle search query
search_query = self.request.GET.get('search', '')
if search_query:
queryset = queryset.filter(
Q(bed_number__icontains=search_query) |
Q(room_number__icontains=search_query) |
Q(ward__name__icontains=search_query) |
Q(current_patient__first_name__icontains=search_query) |
Q(current_patient__last_name__icontains=search_query)
)
# Handle filter by ward
ward_id = self.request.GET.get('ward', '')
if ward_id:
queryset = queryset.filter(ward_id=ward_id)
# Handle filter by bed_type
bed_type = self.request.GET.get('bed_type', '')
if bed_type:
queryset = queryset.filter(bed_type=bed_type)
# Handle filter by room_type
room_type = self.request.GET.get('room_type', '')
if room_type:
queryset = queryset.filter(room_type=room_type)
# Handle filter by status
status = self.request.GET.get('status', '')
if status:
queryset = queryset.filter(status=status)
# Handle sort
sort_by = self.request.GET.get('sort', 'ward__name,room_number,bed_number')
if ',' in sort_by:
# Multiple sort fields
sort_fields = sort_by.split(',')
queryset = queryset.order_by(*sort_fields)
else:
queryset = queryset.order_by(sort_by)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
tenant = self.request.user.tenant
# Get wards for filter dropdown
context['wards'] = Ward.objects.filter(
tenant=tenant,
is_active=True
).order_by('name')
# Get bed types for filter dropdown
context['bed_types'] = Bed.BED_TYPE_CHOICES
# Get room types for filter dropdown
context['room_types'] = Bed.ROOM_TYPE_CHOICES
# Get statuses for filter dropdown
context['statuses'] = Bed.STATUS_CHOICES
# Add search query to context
context['search_query'] = self.request.GET.get('search', '')
context['ward_filter'] = self.request.GET.get('ward', '')
context['bed_type_filter'] = self.request.GET.get('bed_type', '')
context['room_type_filter'] = self.request.GET.get('room_type', '')
context['status_filter'] = self.request.GET.get('status', '')
context['sort_by'] = self.request.GET.get('sort', 'ward__name,room_number,bed_number')
return context
class BedDetailView(LoginRequiredMixin, DetailView):
"""
Detail view for a bed.
"""
model = Bed
template_name = 'inpatients/beds/bed_detail.html'
context_object_name = 'bed'
def get_queryset(self):
"""Filter beds by tenant."""
return Bed.objects.filter(
ward__tenant=self.request.user.tenant
).select_related(
'ward', 'current_patient', 'current_admission',
'cleaned_by', 'blocked_by', 'created_by'
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
bed = self.get_object()
# Get bed history - admissions that used this bed
context['admission_history'] = Admission.objects.filter(
Q(current_bed=bed) | Q(current_bed=bed)
).select_related(
'patient', 'admitting_physician'
).order_by('-admission_datetime')[:10]
# Get maintenance history if available
# This would require a model to track maintenance events
return context
class BedCreateView(LoginRequiredMixin, CreateView):
"""
Create view for a bed.
"""
model = Bed
form_class = BedForm
template_name = 'inpatients/beds/bed_form.html'
permission_required = 'inpatients.add_bed'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.created_by = self.request.user
messages.success(self.request, _('Bed created successfully'))
return super().form_valid(form)
def get_success_url(self):
return reverse('inpatients:bed_detail', kwargs={'pk': self.object.pk})
class BedUpdateView(LoginRequiredMixin, UpdateView):
"""
Update view for a bed.
"""
model = Bed
form_class = BedForm
template_name = 'inpatients/beds/bed_form.html'
permission_required = 'inpatients.change_bed'
def get_queryset(self):
"""Filter beds by tenant."""
return Bed.objects.filter(ward__tenant=self.request.user.tenant)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
messages.success(self.request, _('Bed updated successfully'))
return super().form_valid(form)
def get_success_url(self):
return reverse('inpatients:bed_detail', kwargs={'pk': self.object.pk})
class BedDeleteView(LoginRequiredMixin, DeleteView):
"""
Delete view for a bed.
"""
model = Bed
template_name = 'inpatients/beds/bed_confirm_delete.html'
permission_required = 'inpatients.delete_bed'
def get_queryset(self):
"""Filter beds by tenant."""
return Bed.objects.filter(ward__tenant=self.request.user.tenant)
def delete(self, request, *args, **kwargs):
bed = self.get_object()
# Check if the bed is occupied
if bed.status == 'OCCUPIED':
messages.error(request, _('Cannot delete an occupied bed.'))
return redirect('inpatients:bed_detail', pk=bed.pk)
messages.success(request, _('Bed deleted successfully'))
return super().delete(request, *args, **kwargs)
def get_success_url(self):
return reverse('inpatients:ward_detail', kwargs={'pk': self.object.ward.pk})
class BedManagementView(LoginRequiredMixin, ListView):
"""
Bed management view with real-time status.
"""
model = Bed
template_name = 'inpatients/beds/bed_management.html'
context_object_name = 'beds'
paginate_by = 50
def get_queryset(self):
queryset = Bed.objects.filter(ward__tenant=self.request.user.tenant)
# Filter by ward
ward_id = self.request.GET.get('ward')
if ward_id:
queryset = queryset.filter(ward_id=ward_id)
# Filter by status
status = self.request.GET.get('status')
if status:
queryset = queryset.filter(status=status)
# Filter by bed type
bed_type = self.request.GET.get('bed_type')
if bed_type:
queryset = queryset.filter(bed_type=bed_type)
# Search functionality
search = self.request.GET.get('search')
if search:
queryset = queryset.filter(
Q(bed_number__icontains=search) |
Q(room_number__icontains=search) |
Q(current_patient__first_name__icontains=search) |
Q(current_patient__last_name__icontains=search) |
Q(current_patient__mrn__icontains=search)
)
return queryset.select_related(
'ward', 'current_patient', 'current_admission'
).order_by('ward__name', 'room_number', 'bed_number')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
tenant = self.request.user.tenant
wards = Ward.objects.filter(tenant=tenant, is_active=True)
occupancy_rates = []
for ward in wards:
try:
occupancy_rates.append(ward.occupancy_rate)
except Exception:
pass # Optional: handle edge cases if any
average_occupancy = (
sum(occupancy_rates) / len(occupancy_rates)
if occupancy_rates else 0
)
context.update({
'wards': Ward.objects.filter(tenant=tenant, is_active=True),
'bed_statuses': Bed.STATUS_CHOICES,
'bed_types': Bed.BED_TYPE_CHOICES,
'total_beds': Bed.objects.filter(ward__tenant=tenant).count(),
'available_beds': Bed.objects.filter(ward__tenant=tenant, status='AVAILABLE').count(),
'occupied_beds': Bed.objects.filter(ward__tenant=tenant, status='OCCUPIED').count(),
'maintenance_beds': Bed.objects.filter(ward__tenant=tenant, status='MAINTENANCE').count(),
'occupancy_rate': average_occupancy,
})
return context
class AdmissionListView(LoginRequiredMixin, ListView):
"""
List view for admissions.
"""
model = Admission
template_name = 'inpatients/admissions/admission_list.html'
context_object_name = 'admissions'
paginate_by = 25
def get_queryset(self):
queryset = Admission.objects.filter(tenant=self.request.user.tenant)
# Filter by status
status = self.request.GET.get('status')
if status:
queryset = queryset.filter(status=status)
# Filter by ward
ward_id = self.request.GET.get('ward')
if ward_id:
queryset = queryset.filter(current_ward_id=ward_id)
# Filter by admission type
admission_type = self.request.GET.get('admission_type')
if admission_type:
queryset = queryset.filter(admission_type=admission_type)
# 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(chief_complaint__icontains=search) |
Q(admitting_diagnosis__icontains=search)
)
return queryset.select_related(
'patient', 'current_ward', 'current_bed', 'attending_physician'
).order_by('-admission_datetime')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
tenant = self.request.user.tenant
total_admissions = Admission.objects.filter(tenant=tenant).count()
active_admissions = Admission.objects.filter(tenant=tenant, status='ADMITTED').count()
context.update({
'admission_statuses': Admission.STATUS_CHOICES,
'admission_types': Admission.ADMISSION_TYPE_CHOICES,
'wards': Ward.objects.filter(tenant=tenant, is_active=True),
'total_admissions': total_admissions,
'active_admissions': active_admissions,
})
return context
class AdmissionDetailView(LoginRequiredMixin, DetailView):
"""
Detail view for admission.
"""
model = Admission
template_name = 'inpatients/admissions/admission_detail.html'
context_object_name = 'admission'
def get_queryset(self):
return Admission.objects.filter(tenant=self.request.user.tenant)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
admission = self.object
# Get related transfers
context['transfers'] = admission.transfers.all().order_by('-requested_datetime')
# Get surgery schedules
context['surgeries'] = admission.surgeries.all().order_by('scheduled_date')
# Check if discharge summary exists
context['has_discharge_summary'] = hasattr(admission, 'discharge_summary')
return context
class AdmissionCreateView(LoginRequiredMixin, CreateView):
"""
Create view for an admission.
"""
model = Admission
form_class = AdmissionForm
template_name = 'inpatients/admissions/admission_form.html'
permission_required = 'inpatients.add_admission'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
form.instance.tenant = self.request.user.tenant
form.instance.created_by = self.request.user
# Create the admission
response = super().form_valid(form)
# If admission status is ADMITTED, assign the bed
if form.instance.status == 'ADMITTED' and form.instance.initial_bed:
form.instance.initial_bed.assign_patient(form.instance.patient, form.instance)
messages.success(self.request, _('Patient admitted and assigned to bed successfully'))
else:
messages.success(self.request, _('Admission created successfully'))
return response
def get_success_url(self):
return reverse('inpatients:admission_detail', kwargs={'pk': self.object.pk})
class AdmissionUpdateView(LoginRequiredMixin, UpdateView):
"""
Update view for an admission.
"""
model = Admission
form_class = AdmissionForm
template_name = 'inpatients/admissions/admission_form.html'
permission_required = 'inpatients.change_admission'
def get_queryset(self):
"""Filter admissions by tenant."""
return Admission.objects.filter(tenant=self.request.user.tenant)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
# Check if status is changing to ADMITTED
old_status = self.get_object().status
new_status = form.instance.status
response = super().form_valid(form)
# If changing to ADMITTED, assign the bed
if old_status != 'ADMITTED' and new_status == 'ADMITTED' and form.instance.initial_bed:
form.instance.initial_bed.assign_patient(form.instance.patient, form.instance)
messages.success(self.request, _('Patient admitted and assigned to bed successfully'))
else:
messages.success(self.request, _('Admission updated successfully'))
return response
def get_success_url(self):
return reverse('inpatients:admission_detail', kwargs={'pk': self.object.pk})
class TransferManagementView(LoginRequiredMixin, ListView):
"""
Transfer management view.
"""
model = Transfer
template_name = 'inpatients/transfers/transfer_management.html'
context_object_name = 'transfers'
paginate_by = 25
def get_queryset(self):
tenant = self.request.user.tenant
queryset = Transfer.objects.filter(admission__tenant=tenant)
# 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 date
date_filter = self.request.GET.get('date_filter')
if date_filter == 'today':
queryset = queryset.filter(requested_datetime__date=timezone.now().date())
elif date_filter == 'pending':
queryset = queryset.filter(status__in=['REQUESTED', 'APPROVED', 'SCHEDULED'])
# 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(reason__icontains=search)
)
return queryset.select_related(
'patient', 'admission', 'from_ward', 'to_ward', 'from_bed', 'to_bed',
'requested_by', 'approved_by'
).order_by('-requested_datetime')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'transfer_statuses': Transfer._meta.get_field('status').choices,
'priorities': Transfer._meta.get_field('priority').choices,
})
return context
class SurgeryScheduleView(LoginRequiredMixin, ListView):
"""
Surgery schedule view.
"""
model = SurgerySchedule
template_name = 'inpatients/surgery_schedule.html'
context_object_name = 'surgeries'
paginate_by = 25
def get_queryset(self):
queryset = SurgerySchedule.objects.filter(tenant=self.request.user.tenant)
# Filter by date
date_filter = self.request.GET.get('date_filter')
if date_filter == 'today':
queryset = queryset.filter(scheduled_date=timezone.now().date())
elif date_filter == 'tomorrow':
queryset = queryset.filter(scheduled_date=timezone.now().date() + timedelta(days=1))
elif date_filter == 'this_week':
start_week = timezone.now().date() - timedelta(days=timezone.now().weekday())
end_week = start_week + timedelta(days=6)
queryset = queryset.filter(scheduled_date__range=[start_week, end_week])
# Filter by status
status = self.request.GET.get('status')
if status:
queryset = queryset.filter(status=status)
# Filter by surgery type
surgery_type = self.request.GET.get('surgery_type')
if surgery_type:
queryset = queryset.filter(surgery_type=surgery_type)
# 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) |
Q(primary_surgeon__first_name__icontains=search) |
Q(primary_surgeon__last_name__icontains=search)
)
return queryset.select_related(
'patient', 'admission', 'primary_surgeon', 'anesthesiologist'
).order_by('scheduled_date', 'scheduled_start_time')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'surgery_statuses': SurgerySchedule._meta.get_field('status').choices,
'surgery_types': SurgerySchedule._meta.get_field('surgery_type').choices,
})
return context
class SurgeryDetailView(LoginRequiredMixin, DetailView):
model = SurgerySchedule
template_name = 'inpatients/surgeries/surgery_detail.html'
context_object_name = 'surgery'
def get_queryset(self):
tenant = self.request.user.tenant
queryset = get_object_or_404(SurgerySchedule, pk=self.kwargs['pk'], tenant=tenant)
return queryset
class SurgeryScheduleListView(LoginRequiredMixin, ListView):
"""
List view for surgery schedules.
"""
model = SurgerySchedule
template_name = 'inpatients/surgeries/surgery_schedule.html'
context_object_name = 'surgeries'
paginate_by = 20
def get_queryset(self):
"""Filter surgeries by tenant and search query."""
queryset = SurgerySchedule.objects.filter(
admission__tenant=self.request.user.tenant
).select_related(
'patient', 'admission', 'primary_surgeon',
'anesthesiologist',
)
# Handle search query
search_query = self.request.GET.get('search', '')
if search_query:
queryset = queryset.filter(
Q(surgery_name__icontains=search_query) |
Q(patient__first_name__icontains=search_query) |
Q(patient__last_name__icontains=search_query) |
Q(surgeon__first_name__icontains=search_query) |
Q(surgeon__last_name__icontains=search_query) |
Q(description__icontains=search_query)
)
# Handle filter by status
status = self.request.GET.get('status', '')
if status:
queryset = queryset.filter(status=status)
# Handle filter by priority
priority = self.request.GET.get('priority', '')
if priority:
queryset = queryset.filter(priority=priority)
# Handle filter by surgeon
surgeon_id = self.request.GET.get('surgeon', '')
if surgeon_id:
queryset = queryset.filter(surgeon_id=surgeon_id)
# Handle filter by operating_room
operating_room_id = self.request.GET.get('operating_room', '')
if operating_room_id:
queryset = queryset.filter(operating_room_id=operating_room_id)
# Handle filter by date range
date_from = self.request.GET.get('date_from', '')
if date_from:
queryset = queryset.filter(scheduled_date__gte=date_from)
date_to = self.request.GET.get('date_to', '')
if date_to:
queryset = queryset.filter(scheduled_date__lte=date_to)
# Handle sort
sort_by = self.request.GET.get('sort', 'scheduled_date,scheduled_start_time')
if ',' in sort_by:
# Multiple sort fields
sort_fields = sort_by.split(',')
queryset = queryset.order_by(*sort_fields)
else:
queryset = queryset.order_by(sort_by)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
tenant = self.request.user.tenant
# Get statuses for filter dropdown
context['statuses'] = SurgerySchedule.STATUS_CHOICES
# Get priorities for filter dropdown
context['priorities'] = SurgerySchedule.PRIORITY_CHOICES
# Get surgeons for filter dropdown
context['surgeons'] = User.objects.filter(
tenant=tenant,
is_active=True,
employee_profile__role__in=['SURGEON', 'PHYSICIAN_ASSISTANT']
).order_by('last_name', 'first_name')
# Get operating rooms for filter dropdown
try:
from operating_theatre.models import OperatingRoom
context['operating_rooms'] = OperatingRoom.objects.filter(
tenant=tenant,
is_active=True
).order_by('room_name')
except ImportError:
context['operating_rooms'] = []
# Add search query to context
context['search_query'] = self.request.GET.get('search', '')
context['status_filter'] = self.request.GET.get('status', '')
context['priority_filter'] = self.request.GET.get('priority', '')
context['surgeon_filter'] = self.request.GET.get('surgeon', '')
context['operating_room_filter'] = self.request.GET.get('operating_room', '')
context['date_from'] = self.request.GET.get('date_from', '')
context['date_to'] = self.request.GET.get('date_to', '')
context['sort_by'] = self.request.GET.get('sort', 'scheduled_date,scheduled_time')
return context
class SurgeryScheduleDetailView(LoginRequiredMixin, DetailView):
"""
Detail view for a surgery schedule.
"""
model = SurgerySchedule
template_name = 'inpatients/surgeries/surgery_detail.html'
# context_object_name = 'surgery'
def get_queryset(self):
tenant = self.request.user.tenant
"""Filter surgeries by tenant."""
return SurgerySchedule.objects.filter(
admission__tenant=tenant
).select_related(
'patient', 'admission', 'primary_surgeon',
'anesthesiologist',
)
class SurgeryScheduleCreateView(LoginRequiredMixin, CreateView):
"""
Create view for a surgery schedule.
"""
model = SurgerySchedule
form_class = SurgeryScheduleForm
template_name = 'inpatients/surgeries/surgery_form.html'
permission_required = 'inpatients.add_surgeryschedule'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
# If we have an admission ID in the URL, pre-fill the form
admission_id = self.kwargs.get('admission_id')
if admission_id:
try:
admission = Admission.objects.get(
pk=admission_id,
tenant=self.request.user.tenant,
status__in=['ADMITTED', 'TRANSFERRED']
)
kwargs['initial'] = {
'admission': admission,
'patient': admission.patient
}
except Admission.DoesNotExist:
pass
return kwargs
def form_valid(self, form):
form.instance.created_by = self.request.user
messages.success(self.request, _('Surgery scheduled successfully'))
return super().form_valid(form)
def get_success_url(self):
return reverse('inpatients:surgery_detail', kwargs={'pk': self.object.pk})
class SurgeryScheduleUpdateView(LoginRequiredMixin, UpdateView):
"""
Update view for a surgery schedule.
"""
model = SurgerySchedule
form_class = SurgeryScheduleForm
template_name = 'inpatients/surgeries/surgery_form.html'
permission_required = 'inpatients.change_surgeryschedule'
def get_queryset(self):
"""Filter surgeries by tenant."""
return SurgerySchedule.objects.filter(admission__tenant=self.request.user.tenant)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
messages.success(self.request, _('Surgery schedule updated successfully'))
return super().form_valid(form)
def get_success_url(self):
return reverse('inpatients:surgery_detail', kwargs={'pk': self.object.pk})
@login_required
def ward_stats(request):
"""
HTMX endpoint for ward statistics.
"""
tenant = request.user.tenant
stats = {
'total_beds': Bed.objects.filter(ward__tenant=tenant).count(),
'occupied_beds': Bed.objects.filter(ward__tenant=tenant, status='OCCUPIED').count(),
'available_beds': Bed.objects.filter(ward__tenant=tenant, status='AVAILABLE').count(),
'maintenance_beds': Bed.objects.filter(ward__tenant=tenant, status='MAINTENANCE').count(),
}
if stats['total_beds'] > 0:
stats['occupancy_rate'] = (stats['occupied_beds'] / stats['total_beds']) * 100
else:
stats['occupancy_rate'] = 0
return render(request, 'inpatients/partials/ward_stats.html', {'stats': stats})
@login_required
def bed_grid(request):
"""
HTMX endpoint for bed grid view.
"""
ward_id = request.GET.get('ward_id')
if ward_id:
beds = Bed.objects.filter(
ward_id=ward_id,
ward__tenant=request.user.tenant
).select_related('current_patient', 'current_admission').order_by('room_number', 'bed_number')
else:
beds = Bed.objects.filter(
ward__tenant=request.user.tenant
).select_related('ward', 'current_patient', 'current_admission').order_by(
'ward__name', 'room_number', 'bed_number'
)
return render(request, 'inpatients/partials/bed_grid.html', {'beds': beds})
@login_required
def admission_search(request):
"""
HTMX endpoint for admission search.
"""
search = request.GET.get('search', '')
status = request.GET.get('status', '')
ward_id = request.GET.get('ward', '')
queryset = Admission.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 ward_id:
queryset = queryset.filter(current_ward_id=ward_id)
admissions = queryset.select_related(
'patient', 'current_ward', 'current_bed', 'attending_physician'
).order_by('-admission_datetime')[:20]
return render(request, 'inpatients/partials/admission_list.html', {'admissions': admissions})
@login_required
def surgery_calendar(request):
"""
HTMX endpoint for surgery calendar view.
"""
tenant = request.user.tenant
# Get date range parameters
start_date_str = request.GET.get('start_date')
end_date_str = request.GET.get('end_date')
# Default to current week if not specified
if not start_date_str:
today = timezone.now().date()
start_date = today - timedelta(days=today.weekday()) # Monday of current week
end_date = start_date + timedelta(days=6) # Sunday of current week
else:
try:
start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date()
end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date() if end_date_str else start_date + timedelta(days=6)
except ValueError:
# Handle invalid date format
today = timezone.now().date()
start_date = today - timedelta(days=today.weekday())
end_date = start_date + timedelta(days=6)
# Get surgeries for the date range
surgeries = SurgerySchedule.objects.filter(
tenant=tenant,
scheduled_date__range=[start_date, end_date]
).select_related(
'patient', 'primary_surgeon', 'anesthesiologist'
).order_by('scheduled_date', 'scheduled_start_time')
# Group surgeries by date and operating room
calendar_data = {}
for day in range((end_date - start_date).days + 1):
current_date = start_date + timedelta(days=day)
calendar_data[current_date] = {}
for surgery in surgeries:
if surgery.scheduled_date not in calendar_data:
continue
if surgery.operating_room not in calendar_data[surgery.scheduled_date]:
calendar_data[surgery.scheduled_date][surgery.operating_room] = []
calendar_data[surgery.scheduled_date][surgery.operating_room].append(surgery)
context = {
'calendar_data': calendar_data,
'start_date': start_date,
'end_date': end_date,
'date_range': [(start_date + timedelta(days=i)) for i in range((end_date - start_date).days + 1)],
'operating_rooms': sorted(set(surgery.operating_room for surgery in surgeries))
}
return render(request, 'inpatients/partials/surgery_calendar.html', context)
@login_required
def transfer_patient(request, admission_id):
"""
HTMX endpoint for patient transfer.
"""
if request.method == 'POST':
admission = get_object_or_404(Admission, id=admission_id, tenant=request.user.tenant)
to_ward_id = request.POST.get('to_ward')
to_bed_id = request.POST.get('to_bed')
reason = request.POST.get('reason')
priority = request.POST.get('priority', 'ROUTINE')
if to_ward_id:
to_ward = get_object_or_404(Ward, id=to_ward_id, tenant=request.user.tenant)
# Create transfer request
transfer = Transfer(
admission=admission,
patient=admission.patient,
transfer_type='INTERNAL',
from_ward=admission.current_ward,
from_bed=admission.current_bed,
to_ward=to_ward,
reason=reason,
priority=priority,
requested_datetime=timezone.now(),
requested_by=request.user,
status='REQUESTED'
)
if to_bed_id:
to_bed = get_object_or_404(Bed, id=to_bed_id, ward=to_ward, status='AVAILABLE')
transfer.to_bed = to_bed
transfer.save()
# Log the action
AuditLogger.log_event(
actor=request.user,
action='TRANSFER_REQUESTED',
target=admission,
target_repr=str(admission),
description=f"Transfer requested for {admission.patient.get_full_name()} from {admission.current_ward.name} to {to_ward.name}"
)
messages.success(request, f"Transfer request created for {admission.patient.get_full_name()}")
return redirect('inpatients:admission_detail', pk=admission_id)
else:
messages.error(request, "Destination ward is required")
return redirect('inpatients:admission_detail', pk=admission_id)
# GET request - show form
admission = get_object_or_404(Admission, id=admission_id, tenant=request.user.tenant)
wards = Ward.objects.filter(
tenant=request.user.tenant,
is_active=True,
is_accepting_admissions=True
).exclude(id=admission.current_ward_id if admission.current_ward else None)
context = {
'admission': admission,
'wards': wards,
'priorities': Transfer._meta.get_field('priority').choices
}
return render(request, 'inpatients/patient_transfer.html', context)
@login_required
def approve_transfer(request, transfer_id):
"""
HTMX endpoint for approving a transfer.
"""
print("transfer clicked")
transfer = get_object_or_404(Transfer, id=transfer_id, admission__tenant=request.user.tenant)
if request.method == 'POST':
if transfer.status != 'REQUESTED':
messages.error(request, f"Transfer cannot be approved. Current status: {transfer.get_status_display()}")
return redirect('inpatients:transfer_management')
scheduled_datetime = request.POST.get('scheduled_datetime')
transfer.status = 'APPROVED'
transfer.approved_by = request.user
if scheduled_datetime:
try:
transfer.scheduled_datetime = datetime.strptime(scheduled_datetime, '%Y-%m-%dT%H:%M')
except ValueError:
messages.error(request, "Invalid scheduled date/time format")
return redirect('inpatients:transfer_management')
transfer.save()
# Log the action
AuditLogger.log_event(
actor=request.user,
action='TRANSFER_APPROVED',
target=transfer,
target_repr=str(transfer),
description=f"Transfer approved for {transfer.patient.get_full_name()} from {transfer.from_ward.name} to {transfer.to_ward.name}"
)
messages.success(request, f"Transfer approved for {transfer.patient.get_full_name()}")
return redirect('inpatients:transfer_management')
# GET request - show form
context = {
'transfer': transfer
}
return render(request, 'inpatients/partials/approve_transfer_form.html', context)
@login_required
def complete_transfer(request, transfer_id):
"""
HTMX endpoint for completing a transfer.
"""
transfer = get_object_or_404(Transfer, id=transfer_id, admission__tenant=request.user.tenant)
if request.method == 'POST':
if transfer.status not in ['APPROVED', 'SCHEDULED']:
messages.error(request, f"Transfer cannot be completed. Current status: {transfer.get_status_display()}")
return redirect('inpatients:transfer_management')
if not transfer.to_bed:
messages.error(request, "Destination bed must be assigned before completing transfer")
return redirect('inpatients:transfer_management')
# Update transfer
transfer.status = 'COMPLETED'
transfer.actual_datetime = timezone.now()
transfer.completed_by = request.user
transfer.save()
# Update admission
admission = transfer.admission
# Free up the old bed
if admission.current_bed:
old_bed = admission.current_bed
old_bed.status = 'AVAILABLE'
old_bed.current_patient = None
old_bed.current_admission = None
old_bed.occupied_since = None
old_bed.save()
# Assign new bed
new_bed = transfer.to_bed
new_bed.status = 'OCCUPIED'
new_bed.current_patient = admission.patient
new_bed.current_admission = admission
new_bed.occupied_since = timezone.now()
new_bed.save()
# Update admission with new location
admission.current_ward = transfer.to_ward
admission.current_bed = new_bed
admission.save()
# Log the action
AuditLogger.log_event(
actor=request.user,
action='TRANSFER_COMPLETED',
target=transfer,
target_repr=str(transfer),
description=f"Transfer completed for {transfer.patient.get_full_name()} from {transfer.from_ward.name} to {transfer.to_ward.name}"
)
messages.success(request, f"Transfer completed for {transfer.patient.get_full_name()}")
return redirect('inpatients:transfer_management')
# GET request - show form
context = {
'transfer': transfer
}
return render(request, 'inpatients/partials/complete_transfer_form.html', context)
@login_required
def update_bed_status(request, bed_id):
"""
HTMX endpoint for updating bed status.
"""
bed = get_object_or_404(Bed, id=bed_id, ward__tenant=request.user.tenant)
if request.method == 'POST':
new_status = request.POST.get('status')
notes = request.POST.get('notes', '')
if new_status and new_status in dict(Bed._meta.get_field('status').choices):
old_status = bed.status
bed.status = new_status
# Handle special cases
if new_status == 'CLEANING':
bed.last_cleaned = None
elif new_status == 'AVAILABLE' and old_status == 'CLEANING':
bed.last_cleaned = timezone.now()
bed.cleaned_by = request.user
elif new_status == 'MAINTENANCE':
bed.last_maintenance = timezone.now()
if notes:
bed.notes = notes
bed.save()
# Log the action
AuditLogger.log_event(
actor=request.user,
action='BED_STATUS_UPDATED',
target=bed,
target_repr=str(bed),
description=f"Bed status updated from {dict(Bed._meta.get_field('status').choices)[old_status]} to {dict(Bed._meta.get_field('status').choices)[new_status]}"
)
messages.success(request, f"Bed status updated to {dict(Bed._meta.get_field('status').choices)[new_status]}")
else:
messages.error(request, "Invalid bed status")
return redirect('inpatients:bed_management')
# GET request - show form
context = {
'bed': bed,
'statuses': Bed._meta.get_field('status').choices
}
return render(request, 'inpatients/partials/update_bed_status_form.html', context)
@login_required
def get_available_beds(request):
"""
AJAX view to get available beds for a ward.
"""
ward_id = request.GET.get('ward_id')
if not ward_id:
return JsonResponse({'error': 'Ward ID is required'}, status=400)
try:
ward = Ward.objects.get(
pk=ward_id,
tenant=request.user.tenant,
is_active=True
)
except Ward.DoesNotExist:
return JsonResponse({'error': 'Ward not found'}, status=404)
# Get available beds for this ward
beds = Bed.objects.filter(
ward=ward,
status='AVAILABLE'
).order_by('room_number', 'bed_number')
beds_data = [
{
'id': bed.id,
'name': f"Room {bed.room_number}, Bed {bed.bed_number}",
'bed_type': bed.get_bed_type_display(),
'room_type': bed.get_room_type_display()
}
for bed in beds
]
return JsonResponse({'beds': beds_data})
@login_required
def bed_status_board(request):
"""
View for the bed status board.
"""
tenant = request.user.tenant
# Get all wards for this tenant
wards = Ward.objects.filter(
tenant=tenant,
is_active=True
).order_by('name')
# Build ward and bed data
ward_data = []
for ward in wards:
beds = Bed.objects.filter(ward=ward).order_by('room_number', 'bed_number')
# Group beds by room
rooms = {}
for bed in beds:
room_num = bed.room_number
if room_num not in rooms:
rooms[room_num] = []
rooms[room_num].append(bed)
# Sort rooms by room number
sorted_rooms = sorted(rooms.items())
ward_data.append({
'ward': ward,
'rooms': sorted_rooms,
'total_beds': beds.count(),
'available_beds': beds.filter(status='AVAILABLE').count(),
'occupied_beds': beds.filter(status='OCCUPIED').count()
})
return render(request, 'inpatients/bed_status_board.html', {
'ward_data': ward_data
})
@login_required
def clean_bed(request, pk):
"""
Mark a bed as cleaned.
"""
bed = get_object_or_404(
Bed,
pk=pk,
ward__tenant=request.user.tenant
)
# Only beds with status CLEANING can be marked as cleaned
if bed.status != 'CLEANING':
messages.error(request, _('Only beds with status "Being Cleaned" can be marked as cleaned'))
return redirect('inpatients:bed_detail', pk=bed.pk)
if request.method == 'POST':
cleaning_level = request.POST.get('cleaning_level', 'ROUTINE')
bed.mark_cleaned(request.user, cleaning_level)
messages.success(request, _('Bed marked as cleaned successfully'))
# Redirect to referring page if available
return redirect(request.POST.get('next', 'inpatients:bed_detail'), pk=bed.pk)
return render(request, 'inpatients/clean_bed.html', {
'bed': bed,
'cleaning_levels': Bed.CLEANING_LEVEL_CHOICES,
'next': request.GET.get('next', reverse('inpatients:bed_detail', kwargs={'pk': bed.pk}))
})
@login_required
# @permission_required('inpatients.change_bed')
def block_bed(request, pk):
"""
Block a bed from being used.
"""
bed = get_object_or_404(
Bed,
pk=pk,
ward__tenant=request.user.tenant
)
# Only available beds can be blocked
if bed.status != 'AVAILABLE':
messages.error(request, _('Only available beds can be blocked'))
return redirect('inpatients:bed_detail', pk=bed.pk)
if request.method == 'POST':
reason = request.POST.get('reason')
blocked_until = request.POST.get('blocked_until')
if not reason:
messages.error(request, _('Reason is required'))
return redirect('inpatients:block_bed', pk=bed.pk)
bed.status = 'BLOCKED'
bed.blocked_reason = reason
bed.blocked_by = request.user
if blocked_until:
try:
bed.blocked_until = timezone.datetime.fromisoformat(blocked_until)
except ValueError:
messages.error(request, _('Invalid date format'))
return redirect('inpatients:block_bed', pk=bed.pk)
bed.save()
messages.success(request, _('Bed blocked successfully'))
return redirect('inpatients:bed_detail', pk=bed.pk)
return render(request, 'inpatients/block_bed.html', {
'bed': bed
})
@login_required
# @permission_required('inpatients.change_bed')
def unblock_bed(request, pk):
"""
Unblock a bed.
"""
bed = get_object_or_404(
Bed,
pk=pk,
ward__tenant=request.user.tenant
)
# Only blocked beds can be unblocked
if bed.status != 'BLOCKED':
messages.error(request, _('Only blocked beds can be unblocked'))
return redirect('inpatients:bed_detail', pk=bed.pk)
bed.status = 'AVAILABLE'
bed.blocked_reason = None
bed.blocked_until = None
bed.save()
messages.success(request, _('Bed unblocked successfully'))
return redirect('inpatients:bed_detail', pk=bed.pk)
@login_required
# @permission_required('inpatients.change_bed')
def maintenance_bed(request, pk):
"""
Mark a bed for maintenance.
"""
bed = get_object_or_404(
Bed,
pk=pk,
ward__tenant=request.user.tenant
)
# Only available beds can be marked for maintenance
if bed.status != 'AVAILABLE':
messages.error(request, _('Only available beds can be marked for maintenance'))
return redirect('inpatients:bed_detail', pk=bed.pk)
if request.method == 'POST':
notes = request.POST.get('notes')
bed.mark_maintenance(notes)
messages.success(request, _('Bed marked for maintenance successfully'))
return redirect('inpatients:bed_detail', pk=bed.pk)
return render(request, 'inpatients/maintenance_bed.html', {
'bed': bed
})
#
# # inpatients/views.py
# from django.shortcuts import render, get_object_or_404, redirect
# from django.urls import reverse, reverse_lazy
# from django.contrib import messages
# from django.contrib.auth.decorators import login_required, permission_required
# from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
# from django.views.generic import (
# ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView
# )
# from django.utils import timezone
# from django.utils.translation import gettext_lazy as _
# from django.db.models import Q, Count, Sum, F, ExpressionWrapper, fields
# from django.http import JsonResponse
# from django.core.paginator import Paginator
#
# from accounts.models import User
# from patients.models import PatientProfile
# from .models import Ward, Bed, Admission, Transfer, DischargeSummary, SurgerySchedule
# from .forms import (
# WardForm, BedForm, AdmissionForm, TransferForm,
# DischargeSummaryForm, SurgeryScheduleForm
# )
#
#
#
#
# class WardListView(LoginRequiredMixin, ListView):
# """
# List view for wards.
# """
# model = Ward
# template_name = 'inpatients/ward_list.html'
# context_object_name = 'wards'
# paginate_by = 20
#
# def get_queryset(self):
# """Filter wards by tenant and search query."""
# queryset = Ward.objects.filter(
# tenant=self.request.user.tenant
# ).select_related('nurse_manager').annotate(
# bed_count=Count('beds'),
# occupied_bed_count=Count('beds', filter=Q(beds__status='OCCUPIED'))
# )
#
# # Handle search query
# search_query = self.request.GET.get('search', '')
# if search_query:
# queryset = queryset.filter(
# Q(name__icontains=search_query) |
# Q(description__icontains=search_query) |
# Q(ward_type__icontains=search_query) |
# Q(building__icontains=search_query) |
# Q(floor__icontains=search_query) |
# Q(nurse_manager__first_name__icontains=search_query) |
# Q(nurse_manager__last_name__icontains=search_query)
# )
#
# # Handle filter by ward_type
# ward_type = self.request.GET.get('ward_type', '')
# if ward_type:
# queryset = queryset.filter(ward_type=ward_type)
#
# # Handle filter by building
# building = self.request.GET.get('building', '')
# if building:
# queryset = queryset.filter(building=building)
#
# # Handle filter by floor
# floor = self.request.GET.get('floor', '')
# if floor:
# queryset = queryset.filter(floor=floor)
#
# # Handle filter by active status
# is_active = self.request.GET.get('is_active', '')
# if is_active == 'true':
# queryset = queryset.filter(is_active=True)
# elif is_active == 'false':
# queryset = queryset.filter(is_active=False)
#
# # Handle sort
# sort_by = self.request.GET.get('sort', 'name')
# if sort_by.startswith('-'):
# sort_field = sort_by[1:]
# sort_dir = '-'
# else:
# sort_field = sort_by
# sort_dir = ''
#
# if sort_field == 'occupancy':
# # Calculate occupancy percentage for sorting
# queryset = queryset.annotate(
# occupancy=ExpressionWrapper(
# (F('occupied_bed_count') * 100.0) / F('bed_count'),
# output_field=fields.FloatField()
# )
# )
# queryset = queryset.order_by(f'{sort_dir}occupancy')
# else:
# queryset = queryset.order_by(sort_by)
#
# return queryset
#
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# tenant = self.request.user.tenant
#
# # Get unique values for filters
# context['ward_types'] = Ward.objects.filter(
# tenant=tenant
# ).values_list('ward_type', flat=True).distinct()
#
# context['buildings'] = Ward.objects.filter(
# tenant=tenant
# ).exclude(building__isnull=True).exclude(building='').values_list(
# 'building', flat=True
# ).distinct()
#
# context['floors'] = Ward.objects.filter(
# tenant=tenant
# ).exclude(floor__isnull=True).exclude(floor='').values_list(
# 'floor', flat=True
# ).distinct()
#
# # Add search query to context
# context['search_query'] = self.request.GET.get('search', '')
# context['ward_type_filter'] = self.request.GET.get('ward_type', '')
# context['building_filter'] = self.request.GET.get('building', '')
# context['floor_filter'] = self.request.GET.get('floor', '')
# context['is_active_filter'] = self.request.GET.get('is_active', '')
# context['sort_by'] = self.request.GET.get('sort', 'name')
#
# return context
#
#
# class WardDetailView(LoginRequiredMixin, DetailView):
# """
# Detail view for a ward.
# """
# model = Ward
# template_name = 'inpatients/ward_detail.html'
# context_object_name = 'ward'
#
# def get_queryset(self):
# """Filter wards by tenant."""
# return Ward.objects.filter(
# tenant=self.request.user.tenant
# ).select_related('nurse_manager')
#
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# ward = self.get_object()
#
# # Get beds for this ward with patient information
# context['beds'] = Bed.objects.filter(
# ward=ward
# ).select_related(
# 'current_patient', 'current_admission'
# ).order_by('room_number', 'bed_number')
#
# # Group beds by room for display
# rooms = {}
# for bed in context['beds']:
# room_num = bed.room_number
# if room_num not in rooms:
# rooms[room_num] = []
# rooms[room_num].append(bed)
# context['rooms'] = rooms
#
# # Get ward statistics
# context['total_beds'] = context['beds'].count()
# context['available_beds'] = context['beds'].filter(status='AVAILABLE').count()
# context['occupied_beds'] = context['beds'].filter(status='OCCUPIED').count()
# context['maintenance_beds'] = context['beds'].filter(
# status__in=['MAINTENANCE', 'OUT_OF_ORDER', 'CLEANING']
# ).count()
#
# if context['total_beds'] > 0:
# context['occupancy_rate'] = (context['occupied_beds'] / context['total_beds']) * 100
# else:
# context['occupancy_rate'] = 0
#
# # Get recent admissions to this ward
# context['recent_admissions'] = Admission.objects.filter(
# Q(initial_ward=ward) | Q(current_bed__ward=ward),
# status__in=['ADMITTED', 'READY_FOR_DISCHARGE']
# ).select_related(
# 'patient', 'admitting_physician'
# ).order_by('-admitted_at')[:10]
#
# return context
#
#
# class WardCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
# """
# Create view for a ward.
# """
# model = Ward
# form_class = WardForm
# template_name = 'inpatients/ward_form.html'
# permission_required = 'inpatients.add_ward'
#
# def get_form_kwargs(self):
# kwargs = super().get_form_kwargs()
# kwargs['user'] = self.request.user
# return kwargs
#
# def form_valid(self, form):
# form.instance.tenant = self.request.user.tenant
# form.instance.created_by = self.request.user
# messages.success(self.request, _('Ward created successfully'))
# return super().form_valid(form)
#
# def get_success_url(self):
# return reverse('inpatients:ward_detail', kwargs={'pk': self.object.pk})
#
#
# class WardUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
# """
# Update view for a ward.
# """
# model = Ward
# form_class = WardForm
# template_name = 'inpatients/ward_form.html'
# permission_required = 'inpatients.change_ward'
#
# def get_queryset(self):
# """Filter wards by tenant."""
# return Ward.objects.filter(tenant=self.request.user.tenant)
#
# def get_form_kwargs(self):
# kwargs = super().get_form_kwargs()
# kwargs['user'] = self.request.user
# return kwargs
#
# def form_valid(self, form):
# messages.success(self.request, _('Ward updated successfully'))
# return super().form_valid(form)
#
# def get_success_url(self):
# return reverse('inpatients:ward_detail', kwargs={'pk': self.object.pk})
#
#
#
#
# class BedListView(LoginRequiredMixin, ListView):
# """
# List view for beds.
# """
# model = Bed
# template_name = 'inpatients/bed_list.html'
# context_object_name = 'beds'
# paginate_by = 20
#
# def get_queryset(self):
# """Filter beds by tenant and search query."""
# queryset = Bed.objects.filter(
# ward__tenant=self.request.user.tenant
# ).select_related('ward', 'current_patient')
#
# # Handle search query
# search_query = self.request.GET.get('search', '')
# if search_query:
# queryset = queryset.filter(
# Q(bed_number__icontains=search_query) |
# Q(room_number__icontains=search_query) |
# Q(ward__name__icontains=search_query) |
# Q(current_patient__first_name__icontains=search_query) |
# Q(current_patient__last_name__icontains=search_query)
# )
#
# # Handle filter by ward
# ward_id = self.request.GET.get('ward', '')
# if ward_id:
# queryset = queryset.filter(ward_id=ward_id)
#
# # Handle filter by bed_type
# bed_type = self.request.GET.get('bed_type', '')
# if bed_type:
# queryset = queryset.filter(bed_type=bed_type)
#
# # Handle filter by room_type
# room_type = self.request.GET.get('room_type', '')
# if room_type:
# queryset = queryset.filter(room_type=room_type)
#
# # Handle filter by status
# status = self.request.GET.get('status', '')
# if status:
# queryset = queryset.filter(status=status)
#
# # Handle sort
# sort_by = self.request.GET.get('sort', 'ward__name,room_number,bed_number')
# if ',' in sort_by:
# # Multiple sort fields
# sort_fields = sort_by.split(',')
# queryset = queryset.order_by(*sort_fields)
# else:
# queryset = queryset.order_by(sort_by)
#
# return queryset
#
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# tenant = self.request.user.tenant
#
# # Get wards for filter dropdown
# context['wards'] = Ward.objects.filter(
# tenant=tenant,
# is_active=True
# ).order_by('name')
#
# # Get bed types for filter dropdown
# context['bed_types'] = Bed.BED_TYPE_CHOICES
#
# # Get room types for filter dropdown
# context['room_types'] = Bed.ROOM_TYPE_CHOICES
#
# # Get statuses for filter dropdown
# context['statuses'] = Bed.STATUS_CHOICES
#
# # Add search query to context
# context['search_query'] = self.request.GET.get('search', '')
# context['ward_filter'] = self.request.GET.get('ward', '')
# context['bed_type_filter'] = self.request.GET.get('bed_type', '')
# context['room_type_filter'] = self.request.GET.get('room_type', '')
# context['status_filter'] = self.request.GET.get('status', '')
# context['sort_by'] = self.request.GET.get('sort', 'ward__name,room_number,bed_number')
#
# return context
#
#
# class BedDetailView(LoginRequiredMixin, DetailView):
# """
# Detail view for a bed.
# """
# model = Bed
# template_name = 'inpatients/bed_detail.html'
# context_object_name = 'bed'
#
# def get_queryset(self):
# """Filter beds by tenant."""
# return Bed.objects.filter(
# ward__tenant=self.request.user.tenant
# ).select_related(
# 'ward', 'current_patient', 'current_admission',
# 'cleaned_by', 'blocked_by', 'created_by'
# )
#
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# bed = self.get_object()
#
# # Get bed history - admissions that used this bed
# context['admission_history'] = Admission.objects.filter(
# Q(initial_bed=bed) | Q(current_bed=bed)
# ).select_related(
# 'patient', 'admitting_physician'
# ).order_by('-admitted_at')[:10]
#
# # Get maintenance history if available
# # This would require a model to track maintenance events
#
# return context
#
#
# class BedCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
# """
# Create view for a bed.
# """
# model = Bed
# form_class = BedForm
# template_name = 'inpatients/bed_form.html'
# permission_required = 'inpatients.add_bed'
#
# def get_form_kwargs(self):
# kwargs = super().get_form_kwargs()
# kwargs['user'] = self.request.user
# return kwargs
#
# def form_valid(self, form):
# form.instance.created_by = self.request.user
# messages.success(self.request, _('Bed created successfully'))
# return super().form_valid(form)
#
# def get_success_url(self):
# return reverse('inpatients:bed_detail', kwargs={'pk': self.object.pk})
#
#
# class BedUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
# """
# Update view for a bed.
# """
# model = Bed
# form_class = BedForm
# template_name = 'inpatients/bed_form.html'
# permission_required = 'inpatients.change_bed'
#
# def get_queryset(self):
# """Filter beds by tenant."""
# return Bed.objects.filter(ward__tenant=self.request.user.tenant)
#
# def get_form_kwargs(self):
# kwargs = super().get_form_kwargs()
# kwargs['user'] = self.request.user
# return kwargs
#
# def form_valid(self, form):
# messages.success(self.request, _('Bed updated successfully'))
# return super().form_valid(form)
#
# def get_success_url(self):
# return reverse('inpatients:bed_detail', kwargs={'pk': self.object.pk})
#
#
# class BedDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
# """
# Delete view for a bed.
# """
# model = Bed
# template_name = 'inpatients/bed_confirm_delete.html'
# permission_required = 'inpatients.delete_bed'
#
# def get_queryset(self):
# """Filter beds by tenant."""
# return Bed.objects.filter(ward__tenant=self.request.user.tenant)
#
# def delete(self, request, *args, **kwargs):
# bed = self.get_object()
#
# # Check if the bed is occupied
# if bed.status == 'OCCUPIED':
# messages.error(request, _('Cannot delete an occupied bed.'))
# return redirect('inpatients:bed_detail', pk=bed.pk)
#
# messages.success(request, _('Bed deleted successfully'))
# return super().delete(request, *args, **kwargs)
#
# def get_success_url(self):
# return reverse('inpatients:ward_detail', kwargs={'pk': self.object.ward.pk})
#
#
# class AdmissionListView(LoginRequiredMixin, ListView):
# """
# List view for admissions.
# """
# model = Admission
# template_name = 'inpatients/admission_list.html'
# context_object_name = 'admissions'
# paginate_by = 20
#
# def get_queryset(self):
# """Filter admissions by tenant and search query."""
# queryset = Admission.objects.filter(
# tenant=self.request.user.tenant
# ).select_related(
# 'patient', 'admitting_physician', 'initial_ward', 'initial_bed'
# )
#
# # Handle search query
# search_query = self.request.GET.get('search', '')
# if search_query:
# queryset = queryset.filter(
# Q(admission_number__icontains=search_query) |
# Q(patient__first_name__icontains=search_query) |
# Q(patient__last_name__icontains=search_query) |
# Q(patient__id_number__icontains=search_query) |
# Q(admitting_diagnosis__icontains=search_query)
# )
#
# # Handle filter by status
# status = self.request.GET.get('status', '')
# if status:
# queryset = queryset.filter(status=status)
#
# # Handle filter by admission_type
# admission_type = self.request.GET.get('admission_type', '')
# if admission_type:
# queryset = queryset.filter(admission_type=admission_type)
#
# # Handle filter by priority
# priority = self.request.GET.get('priority', '')
# if priority:
# queryset = queryset.filter(priority=priority)
#
# # Handle filter by ward
# ward_id = self.request.GET.get('ward', '')
# if ward_id:
# # This requires a subquery to get current ward
# queryset = queryset.filter(
# Q(initial_ward_id=ward_id) | Q(current_bed__ward_id=ward_id)
# )
#
# # Handle filter by admitting physician
# physician_id = self.request.GET.get('physician', '')
# if physician_id:
# queryset = queryset.filter(
# Q(admitting_physician_id=physician_id) | Q(attending_physician_id=physician_id)
# )
#
# # Handle filter by date range
# date_from = self.request.GET.get('date_from', '')
# if date_from:
# queryset = queryset.filter(admitted_at__gte=date_from)
#
# date_to = self.request.GET.get('date_to', '')
# if date_to:
# queryset = queryset.filter(admitted_at__lte=date_to)
#
# # Handle sort
# sort_by = self.request.GET.get('sort', '-admitted_at')
# queryset = queryset.order_by(sort_by)
#
# return queryset
#
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# tenant = self.request.user.tenant
#
# # Get statuses for filter dropdown
# context['statuses'] = Admission.STATUS_CHOICES
#
# # Get admission types for filter dropdown
# context['admission_types'] = Admission.ADMISSION_TYPE_CHOICES
#
# # Get priorities for filter dropdown
# context['priorities'] = Admission.PRIORITY_CHOICES
#
# # Get wards for filter dropdown
# context['wards'] = Ward.objects.filter(
# tenant=tenant,
# is_active=True
# ).order_by('name')
#
# # Get physicians for filter dropdown
# context['physicians'] = User.objects.filter(
# tenant=tenant,
# is_active=True,
# role__in=['DOCTOR', 'SPECIALIST']
# ).order_by('last_name', 'first_name')
#
# # Add search query to context
# context['search_query'] = self.request.GET.get('search', '')
# context['status_filter'] = self.request.GET.get('status', '')
# context['admission_type_filter'] = self.request.GET.get('admission_type', '')
# context['priority_filter'] = self.request.GET.get('priority', '')
# context['ward_filter'] = self.request.GET.get('ward', '')
# context['physician_filter'] = self.request.GET.get('physician', '')
# context['date_from'] = self.request.GET.get('date_from', '')
# context['date_to'] = self.request.GET.get('date_to', '')
# context['sort_by'] = self.request.GET.get('sort', '-admitted_at')
#
# return context
#
#
# class AdmissionDetailView(LoginRequiredMixin, DetailView):
# """
# Detail view for an admission.
# """
# model = Admission
# template_name = 'inpatients/admission_detail.html'
# context_object_name = 'admission'
#
# def get_queryset(self):
# """Filter admissions by tenant."""
# return Admission.objects.filter(
# tenant=self.request.user.tenant
# ).select_related(
# 'patient', 'admitting_physician', 'attending_physician',
# 'initial_ward', 'initial_bed', 'discharge_summary',
# 'created_by'
# )
#
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# admission = self.get_object()
#
# # Get transfers for this admission
# context['transfers'] = Transfer.objects.filter(
# admission=admission
# ).select_related(
# 'from_ward', 'to_ward', 'from_bed', 'to_bed',
# 'requested_by', 'approved_by'
# ).order_by('-requested_at')
#
# # Get scheduled surgeries for this admission
# context['surgeries'] = SurgerySchedule.objects.filter(
# admission=admission
# ).select_related(
# 'surgeon', 'anesthesiologist', 'operating_room'
# ).order_by('scheduled_date', 'scheduled_time')
#
# # Get current bed if any
# if hasattr(admission, 'current_bed'):
# context['current_bed'] = admission.current_bed
# context['current_ward'] = admission.current_bed.ward
# else:
# # Fall back to initial bed/ward
# context['current_bed'] = admission.initial_bed
# context['current_ward'] = admission.initial_ward
#
# # Check if a discharge summary exists
# context['has_discharge_summary'] = hasattr(admission, 'discharge_summary')
#
# return context
#
#
@login_required
@permission_required('inpatients.change_admission')
def discharge_patient(request, admission_id):
print("function is right")
print(admission_id)
"""
View to discharge a patient.
"""
admission = get_object_or_404(
Admission,
pk=admission_id,
tenant=request.user.tenant
)
print(admission.status)
# Only admitted patients can be discharged
if not admission.status == 'ADMITTED':
messages.error(request, _('Only admitted patients or patients ready for discharge can be discharged'))
return redirect('inpatients:admission_detail', pk=admission.pk)
if request.method == 'POST':
summary_form = DischargeSummaryForm(
request.POST,
user=request.user,
admission=admission
)
if summary_form.is_valid():
summary = summary_form.save(commit=False)
summary.patient = admission.patient
summary.admission = admission
summary.created_by = request.user
summary.save()
# Link summary to admission and discharge
admission.discharge_summary = summary
# admission.status = 'DISCHARGED'
messages.success(request, _('Patient discharged successfully'))
return redirect('inpatients:admission_detail', pk=admission.pk)
else:
initial = {
'patient': admission.patient,
'admission': admission,
'discharge_diagnosis': admission.admitting_diagnosis,
'doctor_name': request.user_employee_profile.get_full_name() if request.user_employee_profile.role in ['DOCTOR', 'SPECIALIST'] else ''
}
summary_form = DischargeSummaryForm(
initial=initial,
user=request.user,
# admission=admission
)
return render(request, 'inpatients/discharges/discharge_planning.html', {
'admission': admission,
'form': summary_form
})
@login_required
@permission_required('inpatients.change_admission')
def mark_ready_for_discharge(request, pk):
"""
Mark a patient as ready for discharge.
"""
admission = get_object_or_404(
Admission,
pk=pk,
tenant=request.user.tenant
)
# Only admitted patients can be marked ready for discharge
if admission.status != 'ADMITTED':
messages.error(request, _('Only admitted patients can be marked ready for discharge'))
return redirect('inpatients:admission_detail', pk=admission.pk)
admission.mark_ready_for_discharge()
messages.success(request, _('Patient marked ready for discharge'))
return redirect('inpatients:admission_detail', pk=admission.pk)
class TransferListView(LoginRequiredMixin, ListView):
"""
List view for transfers.
"""
model = Transfer
template_name = 'inpatients/transfer_list.html'
context_object_name = 'transfers'
paginate_by = 20
def get_queryset(self):
"""Filter transfers by tenant and search query."""
queryset = Transfer.objects.filter(
admission__tenant=self.request.user.tenant
).select_related(
'patient', 'admission', 'from_ward', 'to_ward',
'from_bed', 'to_bed', 'requested_by', 'approved_by'
)
# Handle search query
search_query = self.request.GET.get('search', '')
if search_query:
queryset = queryset.filter(
Q(transfer_number__icontains=search_query) |
Q(patient__first_name__icontains=search_query) |
Q(patient__last_name__icontains=search_query) |
Q(admission__admission_number__icontains=search_query) |
Q(reason__icontains=search_query)
)
# Handle filter by status
status = self.request.GET.get('status', '')
if status:
queryset = queryset.filter(status=status)
# Handle filter by transfer_type
transfer_type = self.request.GET.get('transfer_type', '')
if transfer_type:
queryset = queryset.filter(transfer_type=transfer_type)
# Handle filter by priority
priority = self.request.GET.get('priority', '')
if priority:
queryset = queryset.filter(priority=priority)
# Handle filter by from_ward
from_ward = self.request.GET.get('from_ward', '')
if from_ward:
queryset = queryset.filter(from_ward_id=from_ward)
# Handle filter by to_ward
to_ward = self.request.GET.get('to_ward', '')
if to_ward:
queryset = queryset.filter(to_ward_id=to_ward)
# Handle filter by date range
date_from = self.request.GET.get('date_from', '')
if date_from:
queryset = queryset.filter(requested_at__gte=date_from)
date_to = self.request.GET.get('date_to', '')
if date_to:
queryset = queryset.filter(requested_at__lte=date_to)
# Handle sort
sort_by = self.request.GET.get('sort', '-requested_at')
queryset = queryset.order_by(sort_by)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
tenant = self.request.user.tenant
# Get statuses for filter dropdown
context['statuses'] = Transfer.STATUS_CHOICES
# Get transfer types for filter dropdown
context['transfer_types'] = Transfer.TRANSFER_TYPE_CHOICES
# Get priorities for filter dropdown
context['priorities'] = Transfer.PRIORITY_CHOICES
# Get wards for filter dropdown
context['wards'] = Ward.objects.filter(
tenant=tenant,
is_active=True
).order_by('name')
# Add search query to context
context['search_query'] = self.request.GET.get('search', '')
context['status_filter'] = self.request.GET.get('status', '')
context['transfer_type_filter'] = self.request.GET.get('transfer_type', '')
context['priority_filter'] = self.request.GET.get('priority', '')
context['from_ward_filter'] = self.request.GET.get('from_ward', '')
context['to_ward_filter'] = self.request.GET.get('to_ward', '')
context['date_from'] = self.request.GET.get('date_from', '')
context['date_to'] = self.request.GET.get('date_to', '')
context['sort_by'] = self.request.GET.get('sort', '-requested_at')
return context
class TransferDetailView(LoginRequiredMixin, DetailView):
"""
Detail view for a transfer.
"""
model = Transfer
template_name = 'inpatients/transfer_detail.html'
context_object_name = 'transfer'
def get_queryset(self):
"""Filter transfers by tenant."""
return Transfer.objects.filter(
admission__tenant=self.request.user.tenant
).select_related(
'patient', 'admission', 'from_ward', 'to_ward',
'from_bed', 'to_bed', 'requested_by', 'approved_by'
)
class TransferCreateView(LoginRequiredMixin, CreateView):
"""
Create view for a transfer.
"""
model = Transfer
form_class = TransferForm
template_name = 'inpatients/transfer_form.html'
permission_required = 'inpatients.add_transfer'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
# If we have an admission ID in the URL, pre-fill the form
admission_id = self.kwargs.get('admission_id')
if admission_id:
try:
admission = Admission.objects.get(
pk=admission_id,
tenant=self.request.user.tenant,
status='ADMITTED'
)
# Determine current bed and ward
if hasattr(admission, 'current_bed') and admission.current_bed:
current_bed = admission.current_bed
current_ward = current_bed.ward
else:
current_bed = admission.initial_bed
current_ward = admission.initial_ward
kwargs['initial'] = {
'admission': admission,
'patient': admission.patient,
'from_bed': current_bed,
'from_ward': current_ward
}
except Admission.DoesNotExist:
pass
return kwargs
def form_valid(self, form):
form.instance.requested_by = self.request.user
messages.success(self.request, _('Transfer request created successfully'))
return super().form_valid(form)
def get_success_url(self):
return reverse('inpatients:transfer_detail', kwargs={'pk': self.object.pk})
class TransferUpdateView(LoginRequiredMixin, UpdateView):
"""
Update view for a transfer.
"""
model = Transfer
form_class = TransferForm
template_name = 'inpatients/transfer_form.html'
permission_required = 'inpatients.change_transfer'
def get_queryset(self):
"""Filter transfers by tenant."""
return Transfer.objects.filter(admission__tenant=self.request.user.tenant)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
messages.success(self.request, _('Transfer updated successfully'))
return super().form_valid(form)
def get_success_url(self):
return reverse('inpatients:transfer_detail', kwargs={'pk': self.object.pk})
#
# @login_required
# @permission_required('inpatients.change_transfer')
# def approve_transfer(request, pk):
# """
# Approve a transfer request.
# """
# transfer = get_object_or_404(
# Transfer,
# pk=pk,
# admission__tenant=request.user.tenant
# )
#
# # Only requested transfers can be approved
# if transfer.status != 'REQUESTED':
# messages.error(request, _('Only requested transfers can be approved'))
# return redirect('inpatients:transfer_detail', pk=transfer.pk)
#
# if request.method == 'POST':
# scheduled_time = request.POST.get('scheduled_time')
#
# try:
# transfer.approve(request.user, scheduled_time)
# messages.success(request, _('Transfer approved successfully'))
# except ValueError as e:
# messages.error(request, str(e))
#
# return redirect('inpatients:transfer_detail', pk=transfer.pk)
#
# return render(request, 'inpatients/approve_transfer.html', {
# 'transfer': transfer
# })
#
#
# @login_required
# @permission_required('inpatients.change_transfer')
# def complete_transfer(request, pk):
# """
# Complete a transfer.
# """
# transfer = get_object_or_404(
# Transfer,
# pk=pk,
# admission__tenant=request.user.tenant
# )
#
# # Only approved or scheduled transfers can be completed
# if transfer.status not in ['APPROVED', 'SCHEDULED', 'IN_PROGRESS']:
# messages.error(request, _('Only approved, scheduled, or in-progress transfers can be completed'))
# return redirect('inpatients:transfer_detail', pk=transfer.pk)
#
# try:
# transfer.complete()
# messages.success(request, _('Transfer completed successfully'))
# except ValueError as e:
# messages.error(request, str(e))
#
# return redirect('inpatients:transfer_detail', pk=transfer.pk)
#
#
@login_required
# @permission_required('inpatients.change_transfer')
def cancel_transfer(request, pk):
"""
Cancel a transfer.
"""
transfer = get_object_or_404(
Transfer,
pk=pk,
admission__tenant=request.user.tenant
)
# Cannot cancel completed or already cancelled transfers
if transfer.status in ['COMPLETED', 'CANCELLED']:
messages.error(request, _('Cannot cancel a completed or already cancelled transfer'))
return redirect('inpatients:transfer_detail', pk=transfer.pk)
if request.method == 'POST':
reason = request.POST.get('reason')
transfer.status = 'CANCELLED'
if reason:
transfer.notes = (
transfer.notes or "") + f"\n\nCancellation Reason ({timezone.now().strftime('%Y-%m-%d %H:%M')}):\n{reason}"
transfer.save()
messages.success(request, _('Transfer cancelled successfully'))
return redirect('inpatients:transfer_detail', pk=transfer.pk)
return render(request, 'inpatients/transfers/cancel_transfer.html', {
'transfer': transfer
})
@login_required
# @permission_required('inpatients.change_surgeryschedule')
def mark_surgery_completed(request, pk):
"""
Mark a surgery as completed.
"""
surgery = get_object_or_404(
SurgerySchedule,
pk=pk,
admission__tenant=request.user.tenant
)
# Only scheduled, confirmed, or in-progress surgeries can be completed
if surgery.status not in ['SCHEDULED', 'CONFIRMED', 'IN_PROGRESS']:
messages.error(request, _('Only scheduled, confirmed, or in-progress surgeries can be marked as completed'))
return redirect('inpatients:surgery_detail', pk=surgery.pk)
if request.method == 'POST':
notes = request.POST.get('notes')
try:
surgery.mark_completed(notes)
messages.success(request, _('Surgery marked as completed successfully'))
except ValueError as e:
messages.error(request, str(e))
return redirect('inpatients:surgery_detail', pk=surgery.pk)
return render(request, 'inpatients/complete_surgery.html', {
'surgery': surgery
})
@login_required
# @permission_required('inpatients.change_surgeryschedule')
def reschedule_surgery(request, pk):
"""
Reschedule a surgery.
"""
surgery = get_object_or_404(
SurgerySchedule,
pk=pk,
admission__tenant=request.user.tenant
)
# Cannot reschedule completed or cancelled surgeries
if surgery.status in ['COMPLETED', 'CANCELLED']:
messages.error(request, _('Cannot reschedule completed or cancelled surgeries'))
return redirect('inpatients:surgery_detail', pk=surgery.pk)
if request.method == 'POST':
new_date = request.POST.get('scheduled_date')
new_time = request.POST.get('scheduled_time')
reason = request.POST.get('reason')
try:
surgery.reschedule(new_date, new_time, reason)
messages.success(request, _('Surgery rescheduled successfully'))
except ValueError as e:
messages.error(request, str(e))
return redirect('inpatients:surgery_detail', pk=surgery.pk)
return render(request, 'inpatients/reschedule_surgery.html', {
'surgery': surgery
})
@login_required
# @permission_required('inpatients.change_surgeryschedule')
def cancel_surgery(request, pk):
"""
Cancel a surgery.
"""
surgery = get_object_or_404(
SurgerySchedule,
pk=pk,
admission__tenant=request.user.tenant
)
# Cannot cancel completed or already cancelled surgeries
if surgery.status in ['COMPLETED', 'CANCELLED']:
messages.error(request, _('Cannot cancel a completed or already cancelled surgery'))
return redirect('inpatients:surgery_detail', pk=surgery.pk)
if request.method == 'POST':
reason = request.POST.get('reason')
surgery.status = 'CANCELLED'
if reason:
surgery.notes = (surgery.notes or "") + f"\n\nCancellation Reason ({timezone.now().strftime('%Y-%m-%d %H:%M')}):\n{reason}"
surgery.save()
messages.success(request, _('Surgery cancelled successfully'))
return redirect('inpatients:surgery_list')
return render(request, 'inpatients/surgeries/surgery_schedule.html', {
'surgery': surgery
})
#
#
# @login_required
# def get_available_beds(request):
# """
# AJAX view to get available beds for a ward.
# """
# ward_id = request.GET.get('ward_id')
#
# if not ward_id:
# return JsonResponse({'error': 'Ward ID is required'}, status=400)
#
# try:
# ward = Ward.objects.get(
# pk=ward_id,
# tenant=request.user.tenant,
# is_active=True
# )
# except Ward.DoesNotExist:
# return JsonResponse({'error': 'Ward not found'}, status=404)
#
# # Get available beds for this ward
# beds = Bed.objects.filter(
# ward=ward,
# status='AVAILABLE'
# ).order_by('room_number', 'bed_number')
#
# beds_data = [
# {
# 'id': bed.id,
# 'name': f"Room {bed.room_number}, Bed {bed.bed_number}",
# 'bed_type': bed.get_bed_type_display(),
# 'room_type': bed.get_room_type_display()
# }
# for bed in beds
# ]
#
# return JsonResponse({'beds': beds_data})
#
#
# @login_required
# def bed_status_board(request):
# """
# View for the bed status board.
# """
# tenant = request.user.tenant
#
# # Get all wards for this tenant
# wards = Ward.objects.filter(
# tenant=tenant,
# is_active=True
# ).order_by('name')
#
# # Build ward and bed data
# ward_data = []
#
# for ward in wards:
# beds = Bed.objects.filter(ward=ward).order_by('room_number', 'bed_number')
#
# # Group beds by room
# rooms = {}
# for bed in beds:
# room_num = bed.room_number
# if room_num not in rooms:
# rooms[room_num] = []
# rooms[room_num].append(bed)
#
# # Sort rooms by room number
# sorted_rooms = sorted(rooms.items())
#
# ward_data.append({
# 'ward': ward,
# 'rooms': sorted_rooms,
# 'total_beds': beds.count(),
# 'available_beds': beds.filter(status='AVAILABLE').count(),
# 'occupied_beds': beds.filter(status='OCCUPIED').count()
# })
#
# return render(request, 'inpatients/bed_status_board.html', {
# 'ward_data': ward_data
# })
#
#
# @login_required
# def clean_bed(request, pk):
# """
# Mark a bed as cleaned.
# """
# bed = get_object_or_404(
# Bed,
# pk=pk,
# ward__tenant=request.user.tenant
# )
#
# # Only beds with status CLEANING can be marked as cleaned
# if bed.status != 'CLEANING':
# messages.error(request, _('Only beds with status "Being Cleaned" can be marked as cleaned'))
# return redirect('inpatients:bed_detail', pk=bed.pk)
#
# if request.method == 'POST':
# cleaning_level = request.POST.get('cleaning_level', 'ROUTINE')
#
# bed.mark_cleaned(request.user, cleaning_level)
# messages.success(request, _('Bed marked as cleaned successfully'))
#
# # Redirect to referring page if available
# return redirect(request.POST.get('next', 'inpatients:bed_detail'), pk=bed.pk)
#
# return render(request, 'inpatients/clean_bed.html', {
# 'bed': bed,
# 'cleaning_levels': Bed.CLEANING_LEVEL_CHOICES,
# 'next': request.GET.get('next', reverse('inpatients:bed_detail', kwargs={'pk': bed.pk}))
# })
#
#
# @login_required
# @permission_required('inpatients.change_bed')
# def block_bed(request, pk):
# """
# Block a bed from being used.
# """
# bed = get_object_or_404(
# Bed,
# pk=pk,
# ward__tenant=request.user.tenant
# )
#
# # Only available beds can be blocked
# if bed.status != 'AVAILABLE':
# messages.error(request, _('Only available beds can be blocked'))
# return redirect('inpatients:bed_detail', pk=bed.pk)
#
# if request.method == 'POST':
# reason = request.POST.get('reason')
# blocked_until = request.POST.get('blocked_until')
#
# if not reason:
# messages.error(request, _('Reason is required'))
# return redirect('inpatients:block_bed', pk=bed.pk)
#
# bed.status = 'BLOCKED'
# bed.blocked_reason = reason
# bed.blocked_by = request.user
#
# if blocked_until:
# try:
# bed.blocked_until = timezone.datetime.fromisoformat(blocked_until)
# except ValueError:
# messages.error(request, _('Invalid date format'))
# return redirect('inpatients:block_bed', pk=bed.pk)
#
# bed.save()
#
# messages.success(request, _('Bed blocked successfully'))
# return redirect('inpatients:bed_detail', pk=bed.pk)
#
# return render(request, 'inpatients/block_bed.html', {
# 'bed': bed
# })
#
#
# @login_required
# @permission_required('inpatients.change_bed')
# def unblock_bed(request, pk):
# """
# Unblock a bed.
# """
# bed = get_object_or_404(
# Bed,
# pk=pk,
# ward__tenant=request.user.tenant
# )
#
# # Only blocked beds can be unblocked
# if bed.status != 'BLOCKED':
# messages.error(request, _('Only blocked beds can be unblocked'))
# return redirect('inpatients:bed_detail', pk=bed.pk)
#
# bed.status = 'AVAILABLE'
# bed.blocked_reason = None
# bed.blocked_until = None
# bed.save()
#
# messages.success(request, _('Bed unblocked successfully'))
# return redirect('inpatients:bed_detail', pk=bed.pk)
#
#
# @login_required
# @permission_required('inpatients.change_bed')
# def maintenance_bed(request, pk):
# """
# Mark a bed for maintenance.
# """
# bed = get_object_or_404(
# Bed,
# pk=pk,
# ward__tenant=request.user.tenant
# )
#
# # Only available beds can be marked for maintenance
# if bed.status != 'AVAILABLE':
# messages.error(request, _('Only available beds can be marked for maintenance'))
# return redirect('inpatients:bed_detail', pk=bed.pk)
#
# if request.method == 'POST':
# notes = request.POST.get('notes')
#
# # bed.mark_maintenance(notes)
# bed.status = 'MAINTENANCE'
# bed.notes = notes
# bed.save()
# messages.success(request, _('Bed marked for maintenance successfully'))
# return redirect('inpatients:bed_detail', pk=bed.pk)
#
# return render(request, 'inpatients/maintenance_bed.html', {
# 'bed': bed
# })
#
#
@login_required
def inpatient_stats(request):
"""
View for inpatient statistics.
"""
tenant = request.user.tenant
# Get date range for stats
date_from = request.GET.get('date_from')
date_to = request.GET.get('date_to')
# Default to last 30 days if not specified
if not date_from:
date_from = (timezone.now() - timezone.timedelta(days=30)).date().isoformat()
if not date_to:
date_to = timezone.now().date().isoformat()
# Query parameters
date_range_filter = Q(
admitted_at__date__gte=date_from,
admitted_at__date__lte=date_to
)
# Basic stats
total_admissions = Admission.objects.filter(
tenant=tenant,
**date_range_filter.children
).count()
avg_length_of_stay = Admission.objects.filter(
tenant=tenant,
status='DISCHARGED',
admitted_at__isnull=False,
discharged_at__isnull=False,
**date_range_filter.children
).aggregate(
avg_los=models.Avg(
ExpressionWrapper(
F('discharged_at') - F('admitted_at'),
output_field=fields.DurationField()
)
)
)['avg_los']
if avg_length_of_stay:
avg_length_of_stay = avg_length_of_stay.total_seconds() / (3600 * 24) # Convert to days
else:
avg_length_of_stay = 0
# Admissions by type
admissions_by_type = Admission.objects.filter(
tenant=tenant,
**date_range_filter.children
).values('admission_type').annotate(
count=Count('id')
).order_by('-count')
# Admissions by ward
admissions_by_ward = Admission.objects.filter(
tenant=tenant,
**date_range_filter.children
).values('initial_ward__name').annotate(
count=Count('id')
).order_by('-count')
# Bed occupancy over time
beds_timeline = []
# Occupancy by ward
occupancy_by_ward = []
wards = Ward.objects.filter(tenant=tenant, is_active=True)
for ward in wards:
total_beds = ward.beds.count()
occupied_beds = ward.beds.filter(status='OCCUPIED').count()
if total_beds > 0:
occupancy_rate = (occupied_beds / total_beds) * 100
else:
occupancy_rate = 0
occupancy_by_ward.append({
'ward': ward,
'total_beds': total_beds,
'occupied_beds': occupied_beds,
'occupancy_rate': occupancy_rate
})
return render(request, 'inpatients/inpatient_stats.html', {
'total_admissions': total_admissions,
'avg_length_of_stay': avg_length_of_stay,
'admissions_by_type': admissions_by_type,
'admissions_by_ward': admissions_by_ward,
'beds_timeline': beds_timeline,
'occupancy_by_ward': occupancy_by_ward,
'date_from': date_from,
'date_to': date_to
})