""" 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 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(), 'todays_surgeries': SurgerySchedule.objects.filter( tenant=tenant, scheduled_date=timezone.now().date(), status__in=['SCHEDULED', 'CONFIRMED', 'PREP', 'IN_PROGRESS'] ), 'pending_discharges': Admission.objects.filter( tenant=tenant, status='ADMITTED', anticipated_discharge_date=timezone.now().date() ), }) # Calculate occupancy rate if context['total_beds'] > 0: context['occupancy_rate'] = (context['occupied_beds'] / context['total_beds']) * 100 else: context['occupancy_rate'] = 0 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(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, 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): model = Admission form_class = AdmissionForm template_name = 'inpatients/admissions/admission_form.html' 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): queryset = Transfer.objects.filter(admission__tenant=self.request.user.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 # HTMX Views for real-time updates @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. """ # 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=request.user.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/surgery_schedule.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 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' # ).count(), # }) # # # Recent admissions # context['recent_admissions'] = Admission.objects.filter( # tenant=tenant # ).select_related( # 'patient', 'admitting_physician', 'initial_ward', 'initial_bed' # ).order_by('-admitted_at')[: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('scheduled_time')[: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', 'surgeon', 'operating_room' # ).order_by('scheduled_date', 'scheduled_time')[:5] # # return context # # # 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 WardDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView): # """ # Delete view for a ward. # """ # model = Ward # template_name = 'inpatients/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/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 # # # class AdmissionCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """ # Create view for an admission. # """ # model = Admission # form_class = AdmissionForm # template_name = 'inpatients/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}) @login_required # @permission_required('inpatients.change_admission') def discharge_patient(request, pk): print("function is right") print(pk) """ View to discharge a patient. """ admission = get_object_or_404( Admission, pk=pk, 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.get_full_name() if request.user.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, PermissionRequiredMixin, 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, PermissionRequiredMixin, 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/cancel_transfer.html', { # 'transfer': transfer # }) # # # class SurgeryScheduleListView(LoginRequiredMixin, ListView): # """ # List view for surgery schedules. # """ # model = SurgerySchedule # template_name = 'inpatients/surgery_list.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', 'surgeon', # 'anesthesiologist', 'operating_room' # ) # # # 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_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, # role__in=['DOCTOR', 'SPECIALIST'] # ).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('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/surgery_detail.html' # context_object_name = 'surgery' # # def get_queryset(self): # """Filter surgeries by tenant.""" # return SurgerySchedule.objects.filter( # admission__tenant=self.request.user.tenant # ).select_related( # 'patient', 'admission', 'surgeon', # 'anesthesiologist', 'operating_room' # ) # # # class SurgeryScheduleCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # """ # Create view for a surgery schedule. # """ # model = SurgerySchedule # form_class = SurgeryScheduleForm # template_name = 'inpatients/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, PermissionRequiredMixin, UpdateView): # """ # Update view for a surgery schedule. # """ # model = SurgerySchedule # form_class = SurgeryScheduleForm # template_name = 'inpatients/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 # @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_detail', pk=surgery.pk) # # return render(request, 'inpatients/cancel_surgery.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 # }) #