This commit is contained in:
Marwan Alwali 2025-08-20 16:06:10 +03:00
parent f941a8e608
commit 99858b4075
29 changed files with 8212 additions and 738 deletions

View File

@ -33,6 +33,7 @@ APPOINTMENT_TYPES = [
'THERAPY', 'VACCINATION', 'SCREENING', 'EMERGENCY', 'TELEMEDICINE' 'THERAPY', 'VACCINATION', 'SCREENING', 'EMERGENCY', 'TELEMEDICINE'
] ]
SAUDI_CHIEF_COMPLAINTS = [ SAUDI_CHIEF_COMPLAINTS = [
'Chest pain and shortness of breath', 'Chest pain and shortness of breath',
'Abdominal pain and nausea', 'Abdominal pain and nausea',

View File

@ -64,12 +64,12 @@ ARABIC_FAMILY_NAMES = [
] ]
def create_saudi_tenants(count=20): def create_saudi_tenants(count=1):
"""Create Saudi healthcare tenants""" """Create Saudi healthcare tenants"""
tenants = [] tenants = []
for i in range(count): for i in range(count):
hospital_name = random.choice(SAUDI_HOSPITALS) hospital_name = 'King Faisal Specialist Hospital'
city = random.choice(SAUDI_CITIES) city = random.choice(SAUDI_CITIES)
province = random.choice(SAUDI_PROVINCES) province = random.choice(SAUDI_PROVINCES)
@ -112,6 +112,8 @@ def create_super_user():
tenant1 = Tenant.objects.get(pk=1) tenant1 = Tenant.objects.get(pk=1)
User.objects.create_superuser( User.objects.create_superuser(
username="admin", username="admin",
first_name="Marwan",
last_name="Alwali",
email="marwan@tenhal.sa", email="marwan@tenhal.sa",
password="Kfsh&rc9788", password="Kfsh&rc9788",
tenant=tenant1 # assumes your User model has a ForeignKey to Tenant named `tenant` tenant=tenant1 # assumes your User model has a ForeignKey to Tenant named `tenant`
@ -266,32 +268,32 @@ def create_saudi_notifications(tenants):
{ {
'title': 'Ramadan Schedule Update', 'title': 'Ramadan Schedule Update',
'message': 'Hospital operating hours have been adjusted for the holy month of Ramadan. Please check the updated schedule.', 'message': 'Hospital operating hours have been adjusted for the holy month of Ramadan. Please check the updated schedule.',
'type': 'schedule', 'type': 'INFO',
'priority': 'high' 'priority': 'HIGH'
}, },
{ {
'title': 'CBAHI Accreditation Renewal', 'title': 'CBAHI Accreditation Renewal',
'message': 'Annual CBAHI accreditation renewal is due. Please ensure all documentation is up to date.', 'message': 'Annual CBAHI accreditation renewal is due. Please ensure all documentation is up to date.',
'type': 'compliance', 'type': 'INFO',
'priority': 'critical' 'priority': 'URGENT'
}, },
{ {
'title': 'Ministry of Health Guidelines', 'title': 'Ministry of Health Guidelines',
'message': 'New MOH guidelines for patient data privacy have been published. Review required.', 'message': 'New MOH guidelines for patient data privacy have been published. Review required.',
'type': 'policy', 'type': 'UPDATE',
'priority': 'high' 'priority': 'HIGH'
}, },
{ {
'title': 'Hajj Season Preparation', 'title': 'Hajj Season Preparation',
'message': 'Special preparations for Hajj season medical services are now in effect.', 'message': 'Special preparations for Hajj season medical services are now in effect.',
'type': 'seasonal', 'type': 'UPDATE',
'priority': 'medium' 'priority': 'MEDIUM'
}, },
{ {
'title': 'Arabic Language Interface', 'title': 'Arabic Language Interface',
'message': 'The system now supports full Arabic language interface. Switch in your profile settings.', 'message': 'The system now supports full Arabic language interface. Switch in your profile settings.',
'type': 'feature', 'type': 'FEATURE',
'priority': 'low' 'priority': 'LOW'
} }
] ]
@ -333,7 +335,7 @@ def create_saudi_audit_logs(tenants, count_per_tenant=100):
'vital_signs_recorded', 'medication_administered' 'vital_signs_recorded', 'medication_administered'
] ]
risk_levels = ['low', 'medium', 'high', 'critical'] risk_levels = AuditLogEntry.RISK_LEVEL_CHOICES
for tenant in tenants: for tenant in tenants:
for _ in range(count_per_tenant): for _ in range(count_per_tenant):
@ -343,8 +345,8 @@ def create_saudi_audit_logs(tenants, count_per_tenant=100):
tenant=tenant, tenant=tenant,
log_id=uuid.uuid4(), log_id=uuid.uuid4(),
event_type=event_type, event_type=event_type,
event_category='clinical' if event_type in ['medical_record_access', event_category='CLINICAL_DATA' if event_type in ['medical_record_access',
'prescription_issued'] else 'administrative', 'prescription_issued'] else 'SYSTEM_ADMINISTRATION',
user_email=f"{random.choice(ARABIC_FIRST_NAMES_MALE + ARABIC_FIRST_NAMES_FEMALE).lower()}@{tenant.name.lower().replace(' ', '')}.sa", user_email=f"{random.choice(ARABIC_FIRST_NAMES_MALE + ARABIC_FIRST_NAMES_FEMALE).lower()}@{tenant.name.lower().replace(' ', '')}.sa",
user_role=random.choice(['doctor', 'nurse', 'pharmacist', 'admin', 'receptionist']), user_role=random.choice(['doctor', 'nurse', 'pharmacist', 'admin', 'receptionist']),
session_key=uuid.uuid4(), session_key=uuid.uuid4(),
@ -379,10 +381,7 @@ def create_saudi_integration_logs(tenants, count_per_tenant=50):
"""Create Saudi-specific integration logs""" """Create Saudi-specific integration logs"""
integration_logs = [] integration_logs = []
saudi_integrations = [ saudi_integrations = IntegrationLog.INTEGRATION_TYPE_CHOICES
'MOH_HESN', 'NPHIES', 'Absher', 'SAUDI_POST', 'SAMA_PAYMENTS',
'CBAHI_PORTAL', 'SFDA_PORTAL', 'ZATCA_INTEGRATION'
]
message_types = [ message_types = [
'patient_eligibility', 'claim_submission', 'payment_notification', 'patient_eligibility', 'claim_submission', 'payment_notification',
@ -400,7 +399,7 @@ def create_saudi_integration_logs(tenants, count_per_tenant=50):
integration_type=integration_type, integration_type=integration_type,
direction=random.choice(['inbound', 'outbound']), direction=random.choice(['inbound', 'outbound']),
external_system=integration_type, external_system=integration_type,
endpoint=f"https://api.{integration_type.lower()}.gov.sa/v1/{message_type}", endpoint=f"https://api.{integration_type}.gov.sa/v1/{message_type}",
message_type=message_type, message_type=message_type,
message_id=f"MSG-{uuid.uuid4().hex[:12].upper()}", message_id=f"MSG-{uuid.uuid4().hex[:12].upper()}",
correlation_id=uuid.uuid4(), correlation_id=uuid.uuid4(),
@ -414,7 +413,7 @@ def create_saudi_integration_logs(tenants, count_per_tenant=50):
'response_code': '200', 'response_code': '200',
'data': {'verified': True, 'eligibility': 'active'} 'data': {'verified': True, 'eligibility': 'active'}
}, },
status=random.choice(['success', 'success', 'success', 'failed']), # Mostly successful status=random.choice(['SUCCESS', 'SUCCESS', 'SUCCESS', 'FAILED']), # Mostly successful
error_code=None if random.choice([True, True, True, False]) else 'ERR_401', error_code=None if random.choice([True, True, True, False]) else 'ERR_401',
error_message=None if random.choice([True, True, True, False]) else 'Authentication failed', error_message=None if random.choice([True, True, True, False]) else 'Authentication failed',
processing_time_ms=random.randint(100, 5000), processing_time_ms=random.randint(100, 5000),
@ -433,7 +432,7 @@ def main():
# Create tenants # Create tenants
print("\n1. Creating Saudi Healthcare Tenants...") print("\n1. Creating Saudi Healthcare Tenants...")
tenants = create_saudi_tenants(15) tenants = create_saudi_tenants(1)
# Create Super user # Create Super user
print("\n2. Creating Super User...") print("\n2. Creating Super User...")

Binary file not shown.

View File

@ -17,7 +17,38 @@ class Employee(models.Model):
""" """
Employee model for hospital staff management. Employee model for hospital staff management.
""" """
GENDER_CHOICES = [
('MALE', 'Male'),
('FEMALE', 'Female'),
('OTHER', 'Other'),
('UNKNOWN', 'Unknown'),
]
MARITAL_STATUS_CHOICES = [
('SINGLE', 'Single'),
('MARRIED', 'Married'),
('DIVORCED', 'Divorced'),
('WIDOWED', 'Widowed'),
('SEPARATED', 'Separated'),
('OTHER', 'Other'),
]
EMPLOYMENT_TYPE_CHOICES = [
('FULL_TIME', 'Full Time'),
('PART_TIME', 'Part Time'),
('CONTRACT', 'Contract'),
('TEMPORARY', 'Temporary'),
('INTERN', 'Intern'),
('VOLUNTEER', 'Volunteer'),
('PER_DIEM', 'Per Diem'),
('CONSULTANT', 'Consultant'),
]
EMPLOYMENT_STATUS_CHOICES = [
('ACTIVE', 'Active'),
('INACTIVE', 'Inactive'),
('TERMINATED', 'Terminated'),
('SUSPENDED', 'Suspended'),
('LEAVE', 'On Leave'),
('RETIRED', 'Retired'),
]
# Tenant relationship # Tenant relationship
tenant = models.ForeignKey( tenant = models.ForeignKey(
'core.Tenant', 'core.Tenant',
@ -135,26 +166,14 @@ class Employee(models.Model):
) )
gender = models.CharField( gender = models.CharField(
max_length=10, max_length=10,
choices=[ choices=GENDER_CHOICES,
('MALE', 'Male'),
('FEMALE', 'Female'),
('OTHER', 'Other'),
('UNKNOWN', 'Unknown'),
],
blank=True, blank=True,
null=True, null=True,
help_text='Gender' help_text='Gender'
) )
marital_status = models.CharField( marital_status = models.CharField(
max_length=20, max_length=20,
choices=[ choices=MARITAL_STATUS_CHOICES,
('SINGLE', 'Single'),
('MARRIED', 'Married'),
('DIVORCED', 'Divorced'),
('WIDOWED', 'Widowed'),
('SEPARATED', 'Separated'),
('OTHER', 'Other'),
],
blank=True, blank=True,
null=True, null=True,
help_text='Marital status' help_text='Marital status'
@ -175,28 +194,12 @@ class Employee(models.Model):
) )
employment_type = models.CharField( employment_type = models.CharField(
max_length=20, max_length=20,
choices=[ choices=EMPLOYMENT_TYPE_CHOICES,
('FULL_TIME', 'Full Time'),
('PART_TIME', 'Part Time'),
('CONTRACT', 'Contract'),
('TEMPORARY', 'Temporary'),
('INTERN', 'Intern'),
('VOLUNTEER', 'Volunteer'),
('PER_DIEM', 'Per Diem'),
('CONSULTANT', 'Consultant'),
],
help_text='Employment type' help_text='Employment type'
) )
employment_status = models.CharField( employment_status = models.CharField(
max_length=20, max_length=20,
choices=[ choices=EMPLOYMENT_STATUS_CHOICES,
('ACTIVE', 'Active'),
('INACTIVE', 'Inactive'),
('TERMINATED', 'Terminated'),
('SUSPENDED', 'Suspended'),
('LEAVE', 'On Leave'),
('RETIRED', 'Retired'),
],
default='ACTIVE', default='ACTIVE',
help_text='Employment status' help_text='Employment status'
) )

View File

@ -199,7 +199,7 @@ def create_saudi_employees(tenants, departments, employees_per_tenant=150):
# Determine employment type and status # Determine employment type and status
employment_type = random.choices( employment_type = random.choices(
['FULL_TIME', 'PART_TIME', 'CONTRACT', 'PER_DIEM'], ['FULL_TIME', 'PART_TIME', 'CONTRACT', 'CONTRACT'],
weights=[70, 15, 10, 5] weights=[70, 15, 10, 5]
)[0] )[0]
@ -355,40 +355,42 @@ def create_saudi_schedules(employees, schedules_per_employee=2):
schedule_patterns = { schedule_patterns = {
'DAY_SHIFT': { 'DAY_SHIFT': {
'monday': {'start': '07:00', 'end': '15:00'}, 'sunday': {'start': '07:00', 'end': '19:00'},
'tuesday': {'start': '07:00', 'end': '15:00'}, 'monday': {'start': '07:00', 'end': '19:00'},
'wednesday': {'start': '07:00', 'end': '15:00'}, 'tuesday': {'start': '07:00', 'end': '19:00'},
'thursday': {'start': '07:00', 'end': '15:00'}, 'wednesday': {'start': '07:00', 'end': '19:00'},
'friday': {'start': '07:00', 'end': '12:00'}, # Half day Friday 'thursday': {'start': '07:00', 'end': '19:00'},
'saturday': {'start': '07:00', 'end': '15:00'}, 'friday': {'start': '07:00', 'end': '19:00'},
'sunday': 'off' 'saturday': {'start': '07:00', 'end': '19:00'},
},
'EVENING_SHIFT': {
'monday': {'start': '15:00', 'end': '23:00'},
'tuesday': {'start': '15:00', 'end': '23:00'},
'wednesday': {'start': '15:00', 'end': '23:00'},
'thursday': {'start': '15:00', 'end': '23:00'},
'friday': {'start': '13:00', 'end': '21:00'},
'saturday': {'start': '15:00', 'end': '23:00'},
'sunday': 'off'
}, },
# 'EVENING_SHIFT': {
# 'monday': {'start': '15:00', 'end': '23:00'},
# 'tuesday': {'start': '15:00', 'end': '23:00'},
# 'wednesday': {'start': '15:00', 'end': '23:00'},
# 'thursday': {'start': '15:00', 'end': '23:00'},
# 'friday': {'start': '13:00', 'end': '21:00'},
# 'saturday': {'start': '15:00', 'end': '23:00'},
# 'sunday': 'off'
# },
'NIGHT_SHIFT': { 'NIGHT_SHIFT': {
'monday': {'start': '23:00', 'end': '07:00'}, 'sunday': {'start': '19:00', 'end': '07:00'},
'tuesday': {'start': '23:00', 'end': '07:00'}, 'monday': {'start': '19:00', 'end': '07:00'},
'wednesday': {'start': '23:00', 'end': '07:00'}, 'tuesday': {'start': '19:00', 'end': '07:00'},
'thursday': {'start': '23:00', 'end': '07:00'}, 'wednesday': {'start': '19:00', 'end': '07:00'},
'friday': 'off', 'thursday': {'start': '19:00', 'end': '07:00'},
'saturday': {'start': '23:00', 'end': '07:00'}, 'friday': {'start': '19:00', 'end': '07:00'},
'sunday': {'start': '23:00', 'end': '07:00'} 'saturday': {'start': '19:00', 'end': '07:00'},
}, },
'ADMIN_HOURS': { 'ADMIN_HOURS': {
'sunday': {'start': '08:00', 'end': '17:00'},
'monday': {'start': '08:00', 'end': '17:00'}, 'monday': {'start': '08:00', 'end': '17:00'},
'tuesday': {'start': '08:00', 'end': '17:00'}, 'tuesday': {'start': '08:00', 'end': '17:00'},
'wednesday': {'start': '08:00', 'end': '17:00'}, 'wednesday': {'start': '08:00', 'end': '17:00'},
'thursday': {'start': '08:00', 'end': '17:00'}, 'thursday': {'start': '08:00', 'end': '16:00'},
'friday': {'start': '08:00', 'end': '12:00'}, 'friday': 'off',
'saturday': 'off', 'saturday': 'off',
'sunday': 'off'
} }
} }
@ -463,7 +465,7 @@ def create_schedule_assignments(schedules, days_back=30):
shift_types = { shift_types = {
'DAY_SHIFT': 'DAY', 'DAY_SHIFT': 'DAY',
'EVENING_SHIFT': 'EVENING', # 'EVENING_SHIFT': 'EVENING',
'NIGHT_SHIFT': 'NIGHT', 'NIGHT_SHIFT': 'NIGHT',
'ADMIN_HOURS': 'DAY' 'ADMIN_HOURS': 'DAY'
} }
@ -491,7 +493,7 @@ def create_schedule_assignments(schedules, days_back=30):
pattern_key = None pattern_key = None
for pattern_name, pattern_data in [ for pattern_name, pattern_data in [
('DAY_SHIFT', schedule.schedule_pattern), ('DAY_SHIFT', schedule.schedule_pattern),
('EVENING_SHIFT', schedule.schedule_pattern), # ('EVENING_SHIFT', schedule.schedule_pattern),
('NIGHT_SHIFT', schedule.schedule_pattern), ('NIGHT_SHIFT', schedule.schedule_pattern),
('ADMIN_HOURS', schedule.schedule_pattern) ('ADMIN_HOURS', schedule.schedule_pattern)
]: ]:

View File

@ -15,7 +15,7 @@ class WardAdmin(admin.ModelAdmin):
Admin interface for Ward model. Admin interface for Ward model.
""" """
list_display = [ list_display = [
'name', 'ward_id', 'ward_type', 'specialty', 'total_beds', 'tenant','name', 'ward_id', 'ward_type', 'specialty', 'total_beds',
'occupancy_display', 'building', 'floor', 'is_active' 'occupancy_display', 'building', 'floor', 'is_active'
] ]
list_filter = [ list_filter = [
@ -70,9 +70,10 @@ class WardAdmin(admin.ModelAdmin):
color = 'orange' color = 'orange'
else: else:
color = 'green' color = 'green'
filled_beds = obj.total_beds - obj.available_beds
return format_html( return format_html(
'<span style="color: {};">{:.1f}% ({}/{})</span>', '<span style="color: {};">{}% ({}/{})</span>',
color, rate, obj.total_beds - obj.available_beds, obj.total_beds color, rate, filled_beds, obj.total_beds
) )
occupancy_display.short_description = 'Occupancy' occupancy_display.short_description = 'Occupancy'

View File

@ -0,0 +1,18 @@
# Generated by Django 5.2.4 on 2025-08-19 15:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("inpatients", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="ward",
name="ward_id",
field=models.CharField(help_text="Unique ward identifier", max_length=50),
),
]

View File

@ -0,0 +1,31 @@
# Generated by Django 5.2.4 on 2025-08-19 19:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("inpatients", "0002_alter_ward_ward_id"),
]
operations = [
migrations.AlterField(
model_name="bed",
name="bed_position",
field=models.CharField(
blank=True,
choices=[
("A", "A"),
("B", "B"),
("C", "C"),
("D", "D"),
("E", "E"),
("F", "F"),
],
help_text="Position within room",
max_length=20,
null=True,
),
),
]

View File

@ -11,12 +11,54 @@ from django.conf import settings
from datetime import timedelta, datetime, time from datetime import timedelta, datetime, time
import json import json
from kombu.transport.redis import PRIORITY_STEPS
class Ward(models.Model): class Ward(models.Model):
""" """
Hospital ward model for organizing patient care areas. Hospital ward model for organizing patient care areas.
""" """
WARD_TYPE_CHOICES = [
('GENERAL', 'General Medical'),
('SURGICAL', 'Surgical'),
('ICU', 'Intensive Care Unit'),
('CCU', 'Cardiac Care Unit'),
('NICU', 'Neonatal ICU'),
('PICU', 'Pediatric ICU'),
('EMERGENCY', 'Emergency'),
('MATERNITY', 'Maternity'),
('PEDIATRIC', 'Pediatric'),
('ONCOLOGY', 'Oncology'),
('CARDIAC', 'Cardiac'),
('ORTHOPEDIC', 'Orthopedic'),
('NEUROLOGY', 'Neurology'),
('PSYCHIATRY', 'Psychiatry'),
('REHABILITATION', 'Rehabilitation'),
('ISOLATION', 'Isolation'),
('STEP_DOWN', 'Step Down'),
('OTHER', 'Other'),
]
SPECIALITY_CHOICES = [
('GENERAL_MEDICINE', 'General Medicine'),
('SURGERY', 'Surgery'),
('CARDIOLOGY', 'Cardiology'),
('NEUROLOGY', 'Neurology'),
('ONCOLOGY', 'Oncology'),
('PEDIATRICS', 'Pediatrics'),
('OBSTETRICS', 'Obstetrics'),
('GYNECOLOGY', 'Gynecology'),
('ORTHOPEDICS', 'Orthopedics'),
('PSYCHIATRY', 'Psychiatry'),
('EMERGENCY', 'Emergency Medicine'),
('CRITICAL_CARE', 'Critical Care'),
('REHABILITATION', 'Rehabilitation'),
('OTHER', 'Other'),
]
GENDER_RESTRICTIONS_CHOICES = [
('NONE', 'No Restrictions'),
('MALE_ONLY', 'Male Only'),
('FEMALE_ONLY', 'Female Only'),
]
# Tenant relationship # Tenant relationship
tenant = models.ForeignKey( tenant = models.ForeignKey(
'core.Tenant', 'core.Tenant',
@ -27,7 +69,7 @@ class Ward(models.Model):
# Ward Information # Ward Information
ward_id = models.CharField( ward_id = models.CharField(
max_length=20, max_length=50,
help_text='Unique ward identifier' help_text='Unique ward identifier'
) )
name = models.CharField( name = models.CharField(
@ -43,47 +85,13 @@ class Ward(models.Model):
# Ward Type and Specialty # Ward Type and Specialty
ward_type = models.CharField( ward_type = models.CharField(
max_length=50, max_length=50,
choices=[ choices=WARD_TYPE_CHOICES,
('GENERAL', 'General Medical'),
('SURGICAL', 'Surgical'),
('ICU', 'Intensive Care Unit'),
('CCU', 'Cardiac Care Unit'),
('NICU', 'Neonatal ICU'),
('PICU', 'Pediatric ICU'),
('EMERGENCY', 'Emergency'),
('MATERNITY', 'Maternity'),
('PEDIATRIC', 'Pediatric'),
('ONCOLOGY', 'Oncology'),
('CARDIAC', 'Cardiac'),
('ORTHOPEDIC', 'Orthopedic'),
('NEUROLOGY', 'Neurology'),
('PSYCHIATRY', 'Psychiatry'),
('REHABILITATION', 'Rehabilitation'),
('ISOLATION', 'Isolation'),
('STEP_DOWN', 'Step Down'),
('OTHER', 'Other'),
],
help_text='Type of ward' help_text='Type of ward'
) )
specialty = models.CharField( specialty = models.CharField(
max_length=100, max_length=100,
choices=[ choices=SPECIALITY_CHOICES,
('GENERAL_MEDICINE', 'General Medicine'),
('SURGERY', 'Surgery'),
('CARDIOLOGY', 'Cardiology'),
('NEUROLOGY', 'Neurology'),
('ONCOLOGY', 'Oncology'),
('PEDIATRICS', 'Pediatrics'),
('OBSTETRICS', 'Obstetrics'),
('GYNECOLOGY', 'Gynecology'),
('ORTHOPEDICS', 'Orthopedics'),
('PSYCHIATRY', 'Psychiatry'),
('EMERGENCY', 'Emergency Medicine'),
('CRITICAL_CARE', 'Critical Care'),
('REHABILITATION', 'Rehabilitation'),
('OTHER', 'Other'),
],
help_text='Medical specialty' help_text='Medical specialty'
) )
@ -175,11 +183,7 @@ class Ward(models.Model):
) )
gender_restrictions = models.CharField( gender_restrictions = models.CharField(
max_length=20, max_length=20,
choices=[ choices=GENDER_RESTRICTIONS_CHOICES,
('NONE', 'No Restrictions'),
('MALE_ONLY', 'Male Only'),
('FEMALE_ONLY', 'Female Only'),
],
default='NONE', default='NONE',
help_text='Gender restrictions' help_text='Gender restrictions'
) )
@ -238,7 +242,7 @@ class Ward(models.Model):
unique_together = ['tenant', 'ward_id'] unique_together = ['tenant', 'ward_id']
def __str__(self): def __str__(self):
return f"{self.name} ({self.ward_id})" return self.name
@property @property
def occupancy_rate(self): def occupancy_rate(self):
@ -246,9 +250,10 @@ class Ward(models.Model):
Calculate current occupancy rate. Calculate current occupancy rate.
""" """
occupied_beds = self.beds.filter(status='OCCUPIED').count() occupied_beds = self.beds.filter(status='OCCUPIED').count()
if self.total_beds > 0: if self.total_beds == 0:
return (occupied_beds / self.total_beds) * 100 return 0.0
return 0 return 100 * (occupied_beds / self.total_beds)
@property @property
def available_beds(self): def available_beds(self):
@ -257,6 +262,10 @@ class Ward(models.Model):
""" """
return self.beds.filter(status='AVAILABLE').count() return self.beds.filter(status='AVAILABLE').count()
@property
def occupied_beds(self):
return self.beds.filter(status='OCCUPIED').count()
class Bed(models.Model): class Bed(models.Model):
""" """
@ -299,10 +308,12 @@ class Bed(models.Model):
('TERMINAL', 'Terminal Cleaning'), ('TERMINAL', 'Terminal Cleaning'),
] ]
BED_POSITION_CHOICES = [ BED_POSITION_CHOICES = [
('WINDOW', 'Window Side'), ('A', 'A'),
('DOOR', 'Door Side'), ('B', 'B'),
('CENTER', 'Center'), ('C', 'C'),
('CORNER', 'Corner'), ('D', 'D'),
('E', 'E'),
('F', 'F'),
] ]
# Ward relationship # Ward relationship
ward = models.ForeignKey( ward = models.ForeignKey(
@ -503,7 +514,69 @@ class Admission(models.Model):
""" """
Patient admission model for tracking inpatient stays. Patient admission model for tracking inpatient stays.
""" """
ADMISSION_TYPE_CHOICES = [
('EMERGENCY', 'Emergency Admission'),
('ELECTIVE', 'Elective Admission'),
('URGENT', 'Urgent Admission'),
('OBSERVATION', 'Observation'),
('DAY_SURGERY', 'Day Surgery'),
('TRANSFER', 'Transfer from Another Facility'),
('READMISSION', 'Readmission'),
('DIRECT', 'Direct Admission'),
('OTHER', 'Other'),
]
ADMISSION_SOURCE_CHOICES = [
('EMERGENCY', 'Emergency Department'),
('OUTPATIENT', 'Outpatient Clinic'),
('PHYSICIAN_OFFICE', 'Physician Office'),
('TRANSFER', 'Transfer from Another Hospital'),
('NURSING_HOME', 'Nursing Home'),
('HOME', 'Home'),
('AMBULATORY_SURGERY', 'Ambulatory Surgery'),
('OTHER_FACILITY', 'Other Healthcare Facility'),
('OTHER', 'Other'),
]
STATUS_CHOICES = [
('PENDING', 'Pending Admission'),
('ADMITTED', 'Admitted'),
('TRANSFERRED', 'Transferred'),
('DISCHARGED', 'Discharged'),
('DECEASED', 'Deceased'),
('LEFT_AMA', 'Left Against Medical Advice'),
('CANCELLED', 'Cancelled'),
]
PRIORITY_CHOICES = [
('ROUTINE', 'Routine'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('CRITICAL', 'Critical'),
]
DISCHARGE_DISPOSITION_CHOICES = [
('HOME', 'Home'),
('HOME_HEALTH', 'Home with Health Services'),
('NURSING_HOME', 'Nursing Home'),
('REHAB_FACILITY', 'Rehabilitation Facility'),
('HOSPICE', 'Hospice'),
('TRANSFER', 'Transfer to Another Hospital'),
('DECEASED', 'Deceased'),
('LEFT_AMA', 'Left Against Medical Advice'),
('OTHER', 'Other'),
]
ISOLATION_TYPE_CHOICES = [
('CONTACT', 'Contact Precautions'),
('DROPLET', 'Droplet Precautions'),
('AIRBORNE', 'Airborne Precautions'),
('PROTECTIVE', 'Protective Isolation'),
('STRICT', 'Strict Isolation'),
]
CODE_STATUS_CHOICES = [
('FULL_CODE', 'Full Code'),
('DNR', 'Do Not Resuscitate'),
('DNI', 'Do Not Intubate'),
('DNR_DNI', 'DNR/DNI'),
('COMFORT_CARE', 'Comfort Care Only'),
('LIMITED', 'Limited Code'),
]
# Tenant relationship # Tenant relationship
tenant = models.ForeignKey( tenant = models.ForeignKey(
'core.Tenant', 'core.Tenant',
@ -534,33 +607,13 @@ class Admission(models.Model):
) )
admission_type = models.CharField( admission_type = models.CharField(
max_length=30, max_length=30,
choices=[ choices=ADMISSION_TYPE_CHOICES,
('EMERGENCY', 'Emergency Admission'),
('ELECTIVE', 'Elective Admission'),
('URGENT', 'Urgent Admission'),
('OBSERVATION', 'Observation'),
('DAY_SURGERY', 'Day Surgery'),
('TRANSFER', 'Transfer from Another Facility'),
('READMISSION', 'Readmission'),
('DIRECT', 'Direct Admission'),
('OTHER', 'Other'),
],
help_text='Type of admission' help_text='Type of admission'
) )
admission_source = models.CharField( admission_source = models.CharField(
max_length=30, max_length=30,
choices=[ choices=ADMISSION_SOURCE_CHOICES,
('EMERGENCY', 'Emergency Department'),
('OUTPATIENT', 'Outpatient Clinic'),
('PHYSICIAN_OFFICE', 'Physician Office'),
('TRANSFER', 'Transfer from Another Hospital'),
('NURSING_HOME', 'Nursing Home'),
('HOME', 'Home'),
('AMBULATORY_SURGERY', 'Ambulatory Surgery'),
('OTHER_FACILITY', 'Other Healthcare Facility'),
('OTHER', 'Other'),
],
help_text='Source of admission' help_text='Source of admission'
) )
@ -616,15 +669,7 @@ class Admission(models.Model):
# Admission Status # Admission Status
status = models.CharField( status = models.CharField(
max_length=20, max_length=20,
choices=[ choices=STATUS_CHOICES,
('PENDING', 'Pending Admission'),
('ADMITTED', 'Admitted'),
('TRANSFERRED', 'Transferred'),
('DISCHARGED', 'Discharged'),
('DECEASED', 'Deceased'),
('LEFT_AMA', 'Left Against Medical Advice'),
('CANCELLED', 'Cancelled'),
],
default='PENDING', default='PENDING',
help_text='Current admission status' help_text='Current admission status'
) )
@ -632,12 +677,7 @@ class Admission(models.Model):
# Priority and Acuity # Priority and Acuity
priority = models.CharField( priority = models.CharField(
max_length=20, max_length=20,
choices=[ choices=PRIORITY_CHOICES,
('ROUTINE', 'Routine'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('CRITICAL', 'Critical'),
],
default='ROUTINE', default='ROUTINE',
help_text='Admission priority' help_text='Admission priority'
) )
@ -691,17 +731,7 @@ class Admission(models.Model):
) )
discharge_disposition = models.CharField( discharge_disposition = models.CharField(
max_length=30, max_length=30,
choices=[ choices=DISCHARGE_DISPOSITION_CHOICES,
('HOME', 'Home'),
('HOME_HEALTH', 'Home with Health Services'),
('NURSING_HOME', 'Nursing Home'),
('REHAB_FACILITY', 'Rehabilitation Facility'),
('HOSPICE', 'Hospice'),
('TRANSFER', 'Transfer to Another Hospital'),
('DECEASED', 'Deceased'),
('LEFT_AMA', 'Left Against Medical Advice'),
('OTHER', 'Other'),
],
blank=True, blank=True,
null=True, null=True,
help_text='Discharge disposition' help_text='Discharge disposition'
@ -714,13 +744,7 @@ class Admission(models.Model):
) )
isolation_type = models.CharField( isolation_type = models.CharField(
max_length=30, max_length=30,
choices=[ choices=ISOLATION_TYPE_CHOICES,
('CONTACT', 'Contact Precautions'),
('DROPLET', 'Droplet Precautions'),
('AIRBORNE', 'Airborne Precautions'),
('PROTECTIVE', 'Protective Isolation'),
('STRICT', 'Strict Isolation'),
],
blank=True, blank=True,
null=True, null=True,
help_text='Type of isolation required' help_text='Type of isolation required'
@ -747,14 +771,7 @@ class Admission(models.Model):
# Code Status # Code Status
code_status = models.CharField( code_status = models.CharField(
max_length=20, max_length=20,
choices=[ choices=CODE_STATUS_CHOICES,
('FULL_CODE', 'Full Code'),
('DNR', 'Do Not Resuscitate'),
('DNI', 'Do Not Intubate'),
('DNR_DNI', 'DNR/DNI'),
('COMFORT_CARE', 'Comfort Care Only'),
('LIMITED', 'Limited Code'),
],
default='FULL_CODE', default='FULL_CODE',
help_text='Code status' help_text='Code status'
) )
@ -822,11 +839,50 @@ class Admission(models.Model):
return self.status in ['PENDING', 'ADMITTED', 'TRANSFERRED'] return self.status in ['PENDING', 'ADMITTED', 'TRANSFERRED']
class Transfer(models.Model): class Transfer(models.Model):
""" """
Patient transfer model for tracking ward/bed changes. Patient transfer model for tracking ward/bed changes.
""" """
TRANSFER_TYPE_CHOICES = [
('WARD', 'Ward Transfer'),
('BED', 'Bed Transfer'),
('ROOM', 'Room Transfer'),
('UNIT', 'Unit Transfer'),
('FACILITY', 'Facility Transfer'),
]
STATUS_CHOICES = [
('REQUESTED', 'Requested'),
('APPROVED', 'Approved'),
('SCHEDULED', 'Scheduled'),
('IN_PROGRESS', 'In Progress'),
('COMPLETED', 'Completed'),
('CANCELLED', 'Cancelled'),
('DELAYED', 'Delayed'),
]
PRIORITY_CHOICES = [
('ROUTINE', 'Routine'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('STAT', 'STAT'),
]
TRANSPORT_METHOD_CHOICES = [
('WHEELCHAIR', 'Wheelchair'),
('STRETCHER', 'Stretcher'),
('BED', 'Hospital Bed'),
('AMBULATORY', 'Walking'),
('AMBULANCE', 'Ambulance'),
('OTHER', 'Other'),
]
PATIENT_CONDITION_CHOICES = [
('STABLE', 'Stable'),
('UNSTABLE', 'Unstable'),
('CRITICAL', 'Critical'),
('IMPROVING', 'Improving'),
('DETERIORATING', 'Deteriorating'),
]
# Transfer Information # Transfer Information
transfer_id = models.UUIDField( transfer_id = models.UUIDField(
default=uuid.uuid4, default=uuid.uuid4,
@ -852,13 +908,7 @@ class Transfer(models.Model):
# Transfer Details # Transfer Details
transfer_type = models.CharField( transfer_type = models.CharField(
max_length=20, max_length=20,
choices=[ choices=TRANSFER_TYPE_CHOICES,
('WARD', 'Ward Transfer'),
('BED', 'Bed Transfer'),
('ROOM', 'Room Transfer'),
('UNIT', 'Unit Transfer'),
('FACILITY', 'Facility Transfer'),
],
help_text='Type of transfer' help_text='Type of transfer'
) )
@ -913,15 +963,7 @@ class Transfer(models.Model):
# Transfer Status # Transfer Status
status = models.CharField( status = models.CharField(
max_length=20, max_length=20,
choices=[ choices=STATUS_CHOICES,
('REQUESTED', 'Requested'),
('APPROVED', 'Approved'),
('SCHEDULED', 'Scheduled'),
('IN_PROGRESS', 'In Progress'),
('COMPLETED', 'Completed'),
('CANCELLED', 'Cancelled'),
('DELAYED', 'Delayed'),
],
default='REQUESTED', default='REQUESTED',
help_text='Transfer status' help_text='Transfer status'
) )
@ -932,12 +974,7 @@ class Transfer(models.Model):
) )
priority = models.CharField( priority = models.CharField(
max_length=20, max_length=20,
choices=[ choices=PRIORITY_CHOICES,
('ROUTINE', 'Routine'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('STAT', 'STAT'),
],
default='ROUTINE', default='ROUTINE',
help_text='Transfer priority' help_text='Transfer priority'
) )
@ -969,14 +1006,7 @@ class Transfer(models.Model):
# Transport Information # Transport Information
transport_method = models.CharField( transport_method = models.CharField(
max_length=20, max_length=20,
choices=[ choices=TRANSPORT_METHOD_CHOICES,
('WHEELCHAIR', 'Wheelchair'),
('STRETCHER', 'Stretcher'),
('BED', 'Hospital Bed'),
('AMBULATORY', 'Walking'),
('AMBULANCE', 'Ambulance'),
('OTHER', 'Other'),
],
blank=True, blank=True,
null=True, null=True,
help_text='Method of transport' help_text='Method of transport'
@ -1003,13 +1033,7 @@ class Transfer(models.Model):
# Clinical Information # Clinical Information
patient_condition = models.CharField( patient_condition = models.CharField(
max_length=20, max_length=20,
choices=[ choices=PATIENT_CONDITION_CHOICES,
('STABLE', 'Stable'),
('UNSTABLE', 'Unstable'),
('CRITICAL', 'Critical'),
('IMPROVING', 'Improving'),
('DETERIORATING', 'Deteriorating'),
],
help_text='Patient condition at time of transfer' help_text='Patient condition at time of transfer'
) )
vital_signs = models.JSONField( vital_signs = models.JSONField(
@ -1083,7 +1107,37 @@ class DischargeSummary(models.Model):
""" """
Discharge summary model for documenting patient discharge. Discharge summary model for documenting patient discharge.
""" """
DISCHARGE_DISPOSITION_CHOICES = [
('HOME', 'Home'),
('HOME_HEALTH', 'Home with Health Services'),
('NURSING_HOME', 'Nursing Home'),
('REHAB_FACILITY', 'Rehabilitation Facility'),
('HOSPICE', 'Hospice'),
('TRANSFER', 'Transfer to Another Hospital'),
('OTHER', 'Other'),
]
TRANSPORTATION_METHOD_CHOICES = [
('PRIVATE', 'Private Vehicle'),
('TAXI', 'Taxi'),
('AMBULANCE', 'Ambulance'),
('MEDICAL_TRANSPORT', 'Medical Transport'),
('PUBLIC_TRANSPORT', 'Public Transportation'),
('WALKING', 'Walking'),
('OTHER', 'Other'),
]
PATIENT_UNDERSTANDING_CHOICES = [
('EXCELLENT', 'Excellent'),
('GOOD', 'Good'),
('FAIR', 'Fair'),
('POOR', 'Poor'),
]
READMISSION_RISK_CHOICES=[
('LOW', 'Low Risk'),
('MODERATE', 'Moderate Risk'),
('HIGH', 'High Risk'),
('VERY_HIGH', 'Very High Risk'),
]
# Admission relationship # Admission relationship
admission = models.OneToOneField( admission = models.OneToOneField(
Admission, Admission,
@ -1200,15 +1254,7 @@ class DischargeSummary(models.Model):
# Discharge Disposition # Discharge Disposition
discharge_disposition = models.CharField( discharge_disposition = models.CharField(
max_length=30, max_length=30,
choices=[ choices=DISCHARGE_DISPOSITION_CHOICES,
('HOME', 'Home'),
('HOME_HEALTH', 'Home with Health Services'),
('NURSING_HOME', 'Nursing Home'),
('REHAB_FACILITY', 'Rehabilitation Facility'),
('HOSPICE', 'Hospice'),
('TRANSFER', 'Transfer to Another Hospital'),
('OTHER', 'Other'),
],
help_text='Discharge disposition' help_text='Discharge disposition'
) )
discharge_location = models.CharField( discharge_location = models.CharField(
@ -1225,15 +1271,7 @@ class DischargeSummary(models.Model):
) )
transportation_method = models.CharField( transportation_method = models.CharField(
max_length=30, max_length=30,
choices=[ choices=TRANSPORTATION_METHOD_CHOICES,
('PRIVATE', 'Private Vehicle'),
('TAXI', 'Taxi'),
('AMBULANCE', 'Ambulance'),
('MEDICAL_TRANSPORT', 'Medical Transport'),
('PUBLIC_TRANSPORT', 'Public Transportation'),
('WALKING', 'Walking'),
('OTHER', 'Other'),
],
blank=True, blank=True,
null=True, null=True,
help_text='Method of transportation' help_text='Method of transportation'
@ -1264,12 +1302,7 @@ class DischargeSummary(models.Model):
) )
patient_understanding = models.CharField( patient_understanding = models.CharField(
max_length=20, max_length=20,
choices=[ choices=PATIENT_UNDERSTANDING_CHOICES,
('EXCELLENT', 'Excellent'),
('GOOD', 'Good'),
('FAIR', 'Fair'),
('POOR', 'Poor'),
],
blank=True, blank=True,
null=True, null=True,
help_text='Patient understanding of instructions' help_text='Patient understanding of instructions'
@ -1296,12 +1329,7 @@ class DischargeSummary(models.Model):
# Quality Measures # Quality Measures
readmission_risk = models.CharField( readmission_risk = models.CharField(
max_length=20, max_length=20,
choices=[ choices=READMISSION_RISK_CHOICES,
('LOW', 'Low Risk'),
('MODERATE', 'Moderate Risk'),
('HIGH', 'High Risk'),
('VERY_HIGH', 'Very High Risk'),
],
blank=True, blank=True,
null=True, null=True,
help_text='Risk of readmission' help_text='Risk of readmission'
@ -1374,7 +1402,52 @@ class SurgerySchedule(models.Model):
""" """
Surgery schedule model for tracking surgical procedures. Surgery schedule model for tracking surgical procedures.
""" """
SURGERY_TYPE_CHOICES = [
('ELECTIVE', 'Elective'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('TRAUMA', 'Trauma'),
('TRANSPLANT', 'Transplant'),
('CARDIAC', 'Cardiac'),
('NEUROSURGERY', 'Neurosurgery'),
('ORTHOPEDIC', 'Orthopedic'),
('GENERAL', 'General Surgery'),
('OTHER', 'Other'),
]
ANESTHESIA_TYPE_CHOICES = [
('GENERAL', 'General Anesthesia'),
('REGIONAL', 'Regional Anesthesia'),
('LOCAL', 'Local Anesthesia'),
('SPINAL', 'Spinal Anesthesia'),
('EPIDURAL', 'Epidural Anesthesia'),
('MAC', 'Monitored Anesthesia Care'),
('SEDATION', 'Conscious Sedation'),
('OTHER', 'Other'),
]
STATUS_CHOICES = [
('SCHEDULED', 'Scheduled'),
('CONFIRMED', 'Confirmed'),
('PREP', 'Pre-operative Prep'),
('IN_PROGRESS', 'In Progress'),
('COMPLETED', 'Completed'),
('CANCELLED', 'Cancelled'),
('POSTPONED', 'Postponed'),
('DELAYED', 'Delayed'),
]
RECOVERY_LOCATION_CHOICES = [
('PACU', 'Post-Anesthesia Care Unit'),
('ICU', 'Intensive Care Unit'),
('WARD', 'Regular Ward'),
('SAME_DAY', 'Same Day Surgery'),
('HOME', 'Home'),
('OTHER', 'Other'),
]
PRIORITY_CHOICES = [
('ROUTINE', 'Routine'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('STAT', 'STAT'),
]
# Tenant relationship # Tenant relationship
tenant = models.ForeignKey( tenant = models.ForeignKey(
'core.Tenant', 'core.Tenant',
@ -1418,18 +1491,7 @@ class SurgerySchedule(models.Model):
) )
surgery_type = models.CharField( surgery_type = models.CharField(
max_length=30, max_length=30,
choices=[ choices=SURGERY_TYPE_CHOICES,
('ELECTIVE', 'Elective'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('TRAUMA', 'Trauma'),
('TRANSPLANT', 'Transplant'),
('CARDIAC', 'Cardiac'),
('NEUROSURGERY', 'Neurosurgery'),
('ORTHOPEDIC', 'Orthopedic'),
('GENERAL', 'General Surgery'),
('OTHER', 'Other'),
],
help_text='Type of surgery' help_text='Type of surgery'
) )
@ -1497,16 +1559,7 @@ class SurgerySchedule(models.Model):
# Anesthesia Information # Anesthesia Information
anesthesia_type = models.CharField( anesthesia_type = models.CharField(
max_length=30, max_length=30,
choices=[ choices=ANESTHESIA_TYPE_CHOICES,
('GENERAL', 'General Anesthesia'),
('REGIONAL', 'Regional Anesthesia'),
('LOCAL', 'Local Anesthesia'),
('SPINAL', 'Spinal Anesthesia'),
('EPIDURAL', 'Epidural Anesthesia'),
('MAC', 'Monitored Anesthesia Care'),
('SEDATION', 'Conscious Sedation'),
('OTHER', 'Other'),
],
blank=True, blank=True,
null=True, null=True,
help_text='Type of anesthesia' help_text='Type of anesthesia'
@ -1551,16 +1604,7 @@ class SurgerySchedule(models.Model):
# Surgery Status # Surgery Status
status = models.CharField( status = models.CharField(
max_length=20, max_length=20,
choices=[ choices=STATUS_CHOICES,
('SCHEDULED', 'Scheduled'),
('CONFIRMED', 'Confirmed'),
('PREP', 'Pre-operative Prep'),
('IN_PROGRESS', 'In Progress'),
('COMPLETED', 'Completed'),
('CANCELLED', 'Cancelled'),
('POSTPONED', 'Postponed'),
('DELAYED', 'Delayed'),
],
default='SCHEDULED', default='SCHEDULED',
help_text='Surgery status' help_text='Surgery status'
) )
@ -1602,14 +1646,7 @@ class SurgerySchedule(models.Model):
# Recovery Information # Recovery Information
recovery_location = models.CharField( recovery_location = models.CharField(
max_length=50, max_length=50,
choices=[ choices=RECOVERY_LOCATION_CHOICES,
('PACU', 'Post-Anesthesia Care Unit'),
('ICU', 'Intensive Care Unit'),
('WARD', 'Regular Ward'),
('SAME_DAY', 'Same Day Surgery'),
('HOME', 'Home'),
('OTHER', 'Other'),
],
blank=True, blank=True,
null=True, null=True,
help_text='Post-operative recovery location' help_text='Post-operative recovery location'
@ -1618,12 +1655,7 @@ class SurgerySchedule(models.Model):
# Priority and Urgency # Priority and Urgency
priority = models.CharField( priority = models.CharField(
max_length=20, max_length=20,
choices=[ choices=PRIORITY_CHOICES,
('ROUTINE', 'Routine'),
('URGENT', 'Urgent'),
('EMERGENT', 'Emergent'),
('STAT', 'STAT'),
],
default='ROUTINE', default='ROUTINE',
help_text='Surgery priority' help_text='Surgery priority'
) )

View File

@ -11,10 +11,12 @@ urlpatterns = [
# Main views # Main views
path('', views.InpatientDashboardView.as_view(), name='dashboard'), path('', views.InpatientDashboardView.as_view(), name='dashboard'),
path('wards/', views.WardListView.as_view(), name='ward_list'), path('wards/', views.WardListView.as_view(), name='ward_list'),
path('wards/<int:pk>/', views.WardDetailView.as_view(), name='ward_detail'),
path('beds/', views.BedManagementView.as_view(), name='bed_management'), path('beds/', views.BedManagementView.as_view(), name='bed_management'),
path('admissions/', views.AdmissionListView.as_view(), name='admission_list'), path('admissions/', views.AdmissionListView.as_view(), name='admission_list'),
path('admissions/<int:pk>/', views.AdmissionDetailView.as_view(), name='admission_detail'), path('admissions/<int:pk>/', views.AdmissionDetailView.as_view(), name='admission_detail'),
path('admissions/create/', views.AdmissionCreateView.as_view(), name='admission_create'), path('admissions/create/', views.AdmissionCreateView.as_view(), name='admission_create'),
path('admissions/<int:pk>/edit/', views.AdmissionUpdateView.as_view(), name='admission_update'),
path('transfers/', views.TransferManagementView.as_view(), name='transfer_management'), path('transfers/', views.TransferManagementView.as_view(), name='transfer_management'),
path('surgery/', views.SurgeryScheduleView.as_view(), name='surgery_schedule'), path('surgery/', views.SurgeryScheduleView.as_view(), name='surgery_schedule'),
@ -37,6 +39,7 @@ urlpatterns = [
# Actions # Actions
path('transfer/<int:admission_id>/', views.transfer_patient, name='transfer_patient'), path('transfer/<int:admission_id>/', views.transfer_patient, name='transfer_patient'),
path('discharge/<int:pk>/', views.discharge_patient, name='discharge_patient'),
path('transfer/<int:transfer_id>/approve/', views.approve_transfer, name='approve_transfer'), path('transfer/<int:transfer_id>/approve/', views.approve_transfer, name='approve_transfer'),
path('transfer/<int:transfer_id>/complete/', views.complete_transfer, name='complete_transfer'), path('transfer/<int:transfer_id>/complete/', views.complete_transfer, name='complete_transfer'),
path('bed/<int:bed_id>/status/', views.update_bed_status, name='update_bed_status'), path('bed/<int:bed_id>/status/', views.update_bed_status, name='update_bed_status'),

10
inpatients/utils.py Normal file
View File

@ -0,0 +1,10 @@
# from .models import Admission
#
#
#
# def discharge(request, admission_id):
# admission = Admission.objects.get(admission_id=admission_id)
#
# admission.status = 'DISCHARGED'
# admission.save()
# return None

View File

@ -1,7 +1,7 @@
""" """
Views for inpatients app. Views for inpatients app.
""" """
from django.conf.locale import te
from django.shortcuts import render, get_object_or_404, redirect from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
@ -423,10 +423,10 @@ class BedDetailView(LoginRequiredMixin, DetailView):
# Get bed history - admissions that used this bed # Get bed history - admissions that used this bed
context['admission_history'] = Admission.objects.filter( context['admission_history'] = Admission.objects.filter(
Q(initial_bed=bed) | Q(current_bed=bed) Q(current_bed=bed) | Q(current_bed=bed)
).select_related( ).select_related(
'patient', 'admitting_physician' 'patient', 'admitting_physician'
).order_by('-admitted_at')[:10] ).order_by('-admission_datetime')[:10]
# Get maintenance history if available # Get maintenance history if available
# This would require a model to track maintenance events # This would require a model to track maintenance events
@ -554,10 +554,29 @@ class BedManagementView(LoginRequiredMixin, ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**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({ context.update({
'wards': Ward.objects.filter(tenant=self.request.user.tenant, is_active=True), 'wards': Ward.objects.filter(tenant=tenant, is_active=True),
'bed_statuses': Bed._meta.get_field('status').choices, 'bed_statuses': Bed.STATUS_CHOICES,
'bed_types': Bed._meta.get_field('bed_type').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 return context
@ -607,8 +626,8 @@ class AdmissionListView(LoginRequiredMixin, ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context.update({ context.update({
'admission_statuses': Admission._meta.get_field('status').choices, 'admission_statuses': Admission.STATUS_CHOICES,
'admission_types': Admission._meta.get_field('admission_type').choices, 'admission_types': Admission.ADMISSION_TYPE_CHOICES,
'wards': Ward.objects.filter(tenant=self.request.user.tenant, is_active=True), 'wards': Ward.objects.filter(tenant=self.request.user.tenant, is_active=True),
}) })
return context return context
@ -1540,62 +1559,62 @@ def maintenance_bed(request, pk):
# return context # return context
# #
# #
# class WardDetailView(LoginRequiredMixin, DetailView): class WardDetailView(LoginRequiredMixin, DetailView):
# """ """
# Detail view for a ward. Detail view for a ward.
# """ """
# model = Ward model = Ward
# template_name = 'inpatients/ward_detail.html' template_name = 'inpatients/ward_detail.html'
# context_object_name = 'ward' context_object_name = 'ward'
#
# def get_queryset(self): def get_queryset(self):
# """Filter wards by tenant.""" """Filter wards by tenant."""
# return Ward.objects.filter( return Ward.objects.filter(
# tenant=self.request.user.tenant tenant=self.request.user.tenant
# ).select_related('nurse_manager') ).select_related('nurse_manager')
#
# def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
# ward = self.get_object() ward = self.get_object()
#
# # Get beds for this ward with patient information # Get beds for this ward with patient information
# context['beds'] = Bed.objects.filter( context['beds'] = Bed.objects.filter(
# ward=ward ward=ward
# ).select_related( ).select_related(
# 'current_patient', 'current_admission' 'current_patient', 'current_admission'
# ).order_by('room_number', 'bed_number') ).order_by('room_number', 'bed_number')
#
# # Group beds by room for display # Group beds by room for display
# rooms = {} rooms = {}
# for bed in context['beds']: for bed in context['beds']:
# room_num = bed.room_number room_num = bed.room_number
# if room_num not in rooms: if room_num not in rooms:
# rooms[room_num] = [] rooms[room_num] = []
# rooms[room_num].append(bed) rooms[room_num].append(bed)
# context['rooms'] = rooms context['rooms'] = rooms
#
# # Get ward statistics # Get ward statistics
# context['total_beds'] = context['beds'].count() context['total_beds'] = context['beds'].count()
# context['available_beds'] = context['beds'].filter(status='AVAILABLE').count() context['available_beds'] = context['beds'].filter(status='AVAILABLE').count()
# context['occupied_beds'] = context['beds'].filter(status='OCCUPIED').count() context['occupied_beds'] = context['beds'].filter(status='OCCUPIED').count()
# context['maintenance_beds'] = context['beds'].filter( context['maintenance_beds'] = context['beds'].filter(
# status__in=['MAINTENANCE', 'OUT_OF_ORDER', 'CLEANING'] status__in=['MAINTENANCE', 'OUT_OF_ORDER', 'CLEANING']
# ).count() ).count()
#
# if context['total_beds'] > 0: if context['total_beds'] > 0:
# context['occupancy_rate'] = (context['occupied_beds'] / context['total_beds']) * 100 context['occupancy_rate'] = (context['occupied_beds'] / context['total_beds']) * 100
# else: else:
# context['occupancy_rate'] = 0 context['occupancy_rate'] = 0
#
# # Get recent admissions to this ward # Get recent admissions to this ward
# context['recent_admissions'] = Admission.objects.filter( context['recent_admissions'] = Admission.objects.filter(
# Q(initial_ward=ward) | Q(current_bed__ward=ward), Q(initial_ward=ward) | Q(current_bed__ward=ward),
# status__in=['ADMITTED', 'READY_FOR_DISCHARGE'] status__in=['ADMITTED', 'READY_FOR_DISCHARGE']
# ).select_related( ).select_related(
# 'patient', 'admitting_physician' 'patient', 'admitting_physician'
# ).order_by('-admitted_at')[:10] ).order_by('-admitted_at')[:10]
#
# return context return context
# #
# #
# class WardCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): # class WardCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
@ -2069,96 +2088,99 @@ def maintenance_bed(request, pk):
# return reverse('inpatients:admission_detail', kwargs={'pk': self.object.pk}) # return reverse('inpatients:admission_detail', kwargs={'pk': self.object.pk})
# #
# #
# class AdmissionUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): class AdmissionUpdateView(LoginRequiredMixin, UpdateView):
# """ """
# Update view for an admission. Update view for an admission.
# """ """
# model = Admission model = Admission
# form_class = AdmissionForm form_class = AdmissionForm
# template_name = 'inpatients/admission_form.html' template_name = 'inpatients/admissions/admission_form.html'
# permission_required = 'inpatients.change_admission' permission_required = 'inpatients.change_admission'
#
# def get_queryset(self): def get_queryset(self):
# """Filter admissions by tenant.""" """Filter admissions by tenant."""
# return Admission.objects.filter(tenant=self.request.user.tenant) return Admission.objects.filter(tenant=self.request.user.tenant)
#
# def get_form_kwargs(self): def get_form_kwargs(self):
# kwargs = super().get_form_kwargs() kwargs = super().get_form_kwargs()
# kwargs['user'] = self.request.user kwargs['user'] = self.request.user
# return kwargs return kwargs
#
# def form_valid(self, form): def form_valid(self, form):
# # Check if status is changing to ADMITTED # Check if status is changing to ADMITTED
# old_status = self.get_object().status old_status = self.get_object().status
# new_status = form.instance.status new_status = form.instance.status
#
# response = super().form_valid(form) response = super().form_valid(form)
#
# # If changing to ADMITTED, assign the bed # If changing to ADMITTED, assign the bed
# if old_status != 'ADMITTED' and new_status == 'ADMITTED' and form.instance.initial_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) form.instance.initial_bed.assign_patient(form.instance.patient, form.instance)
# messages.success(self.request, _('Patient admitted and assigned to bed successfully')) messages.success(self.request, _('Patient admitted and assigned to bed successfully'))
# else: else:
# messages.success(self.request, _('Admission updated successfully')) messages.success(self.request, _('Admission updated successfully'))
#
# return response return response
#
# def get_success_url(self): def get_success_url(self):
# return reverse('inpatients:admission_detail', kwargs={'pk': self.object.pk}) return reverse('inpatients:admission_detail', kwargs={'pk': self.object.pk})
#
#
# @login_required @login_required
# @permission_required('inpatients.change_admission') # @permission_required('inpatients.change_admission')
# def discharge_patient(request, pk): def discharge_patient(request, pk):
# """ print("function is right")
# View to discharge a patient. print(pk)
# """ """
# admission = get_object_or_404( View to discharge a patient.
# Admission, """
# pk=pk, admission = get_object_or_404(
# tenant=request.user.tenant Admission,
# ) pk=pk,
# tenant=request.user.tenant
# # Only admitted patients can be discharged )
# if admission.status not in ['ADMITTED', 'READY_FOR_DISCHARGE']: print(admission)
# messages.error(request, _('Only admitted patients or patients ready for discharge can be discharged'))
# return redirect('inpatients:admission_detail', pk=admission.pk) # Only admitted patients can be discharged
# if admission.status not in ['ADMITTED', 'READY_FOR_DISCHARGE']:
# if request.method == 'POST': messages.error(request, _('Only admitted patients or patients ready for discharge can be discharged'))
# summary_form = DischargeSummaryForm( return redirect('inpatients:admission_detail', pk=admission.pk)
# request.POST,
# user=request.user, if request.method == 'POST':
# admission=admission summary_form = DischargeSummaryForm(
# ) request.POST,
# user=request.user,
# if summary_form.is_valid(): admission=admission
# summary = summary_form.save(commit=False) )
# summary.patient = admission.patient
# summary.created_by = request.user if summary_form.is_valid():
# summary.save() summary = summary_form.save(commit=False)
# summary.patient = admission.patient
# # Link summary to admission and discharge summary.created_by = request.user
# admission.discharge_summary = summary summary.save()
# admission.discharge(request.user)
# # Link summary to admission and discharge
# messages.success(request, _('Patient discharged successfully')) admission.discharge_summary = summary
# return redirect('inpatients:admission_detail', pk=admission.pk) # admission.status = 'DISCHARGED'
# else:
# initial = { messages.success(request, _('Patient discharged successfully'))
# 'patient': admission.patient, return redirect('inpatients:admission_detail', pk=admission.pk)
# 'discharge_diagnosis': admission.admitting_diagnosis, else:
# 'doctor_name': request.user.get_full_name() if request.user.role in ['DOCTOR', 'SPECIALIST'] else '' initial = {
# } 'patient': admission.patient,
# summary_form = DischargeSummaryForm( 'discharge_diagnosis': admission.admitting_diagnosis,
# initial=initial, 'doctor_name': request.user.get_full_name() if request.user.role in ['DOCTOR', 'SPECIALIST'] else ''
# user=request.user, }
# admission=admission summary_form = DischargeSummaryForm(
# ) initial=initial,
# user=request.user,
# return render(request, 'inpatients/discharge_form.html', { admission=admission
# 'admission': admission, )
# 'form': summary_form
# }) return render(request, 'inpatients/discharges/discharge_form.html', {
'admission': admission,
'form': summary_form
})
# #
# #
# @login_required # @login_required
@ -2955,33 +2977,36 @@ def maintenance_bed(request, pk):
# return redirect('inpatients:bed_detail', pk=bed.pk) # return redirect('inpatients:bed_detail', pk=bed.pk)
# #
# #
# @login_required @login_required
# @permission_required('inpatients.change_bed') # @permission_required('inpatients.change_bed')
# def maintenance_bed(request, pk): def maintenance_bed(request, pk):
# """ """
# Mark a bed for maintenance. Mark a bed for maintenance.
# """ """
# bed = get_object_or_404( bed = get_object_or_404(
# Bed, Bed,
# pk=pk, pk=pk,
# ward__tenant=request.user.tenant ward__tenant=request.user.tenant
# ) )
#
# # Only available beds can be marked for maintenance # Only available beds can be marked for maintenance
# if bed.status != 'AVAILABLE': if bed.status != 'AVAILABLE':
# messages.error(request, _('Only available beds can be marked for maintenance')) messages.error(request, _('Only available beds can be marked for maintenance'))
# return redirect('inpatients:bed_detail', pk=bed.pk) return redirect('inpatients:bed_detail', pk=bed.pk)
#
# if request.method == 'POST': if request.method == 'POST':
# notes = request.POST.get('notes') notes = request.POST.get('notes')
#
# bed.mark_maintenance(notes) # bed.mark_maintenance(notes)
# messages.success(request, _('Bed marked for maintenance successfully')) bed.status = 'MAINTENANCE'
# return redirect('inpatients:bed_detail', pk=bed.pk) bed.notes = notes
# bed.save()
# return render(request, 'inpatients/maintenance_bed.html', { messages.success(request, _('Bed marked for maintenance successfully'))
# 'bed': bed return redirect('inpatients:bed_detail', pk=bed.pk)
# })
return render(request, 'inpatients/maintenance_bed.html', {
'bed': bed
})
# #
# #
# @login_required # @login_required

View File

@ -24,13 +24,10 @@ SAUDI_WARD_NAMES = [
'Orthopedic Ward', 'Maternity Ward', 'Pediatric Ward', 'Oncology Ward' 'Orthopedic Ward', 'Maternity Ward', 'Pediatric Ward', 'Oncology Ward'
] ]
SAUDI_SPECIALTIES = [ SAUDI_SPECIALTIES = ['GENERAL_MEDICINE','SURGERY','CARDIOLOGY','NEUROLOGY','ONCOLOGY',
'Internal Medicine', 'Cardiology', 'Cardiac Surgery', 'Orthopedics', 'PEDIATRICS','OBSTETRICS','GYNECOLOGY','ORTHOPEDICS','PSYCHIATRY',
'General Surgery', 'Neurology', 'Neurosurgery', 'Oncology', 'EMERGENCY','CRITICAL_CARE','REHABILITATION',
'Pediatrics', 'Obstetrics and Gynecology', 'Emergency Medicine', ]
'Intensive Care', 'Anesthesiology', 'Psychiatry', 'Dermatology',
'Ophthalmology', 'ENT', 'Urology', 'Nephrology', 'Endocrinology'
]
SAUDI_BUILDINGS = [ SAUDI_BUILDINGS = [
'King Fahd Medical Tower', 'Prince Sultan Building', 'Al-Faisal Complex', 'King Fahd Medical Tower', 'Prince Sultan Building', 'Al-Faisal Complex',
@ -71,16 +68,10 @@ def create_saudi_wards(tenants, wards_per_tenant=12):
"""Create Saudi hospital wards""" """Create Saudi hospital wards"""
wards = [] wards = []
ward_types = [ ward_types =['GENERAL','SURGICAL','ICU','CCU','NICU','PICU','EMERGENCY','MATERNITY','PEDIATRIC',
('medical', 'Medical Ward'), 'ONCOLOGY','CARDIAC','ORTHOPEDIC','NEUROLOGY','PSYCHIATRY','REHABILITATION',
('surgical', 'Surgical Ward'), 'ISOLATION','STEP_DOWN',
('icu', 'Intensive Care Unit'), ]
('pediatric', 'Pediatric Ward'),
('maternity', 'Maternity Ward'),
('emergency', 'Emergency Ward'),
('psychiatric', 'Psychiatric Ward'),
('rehabilitation', 'Rehabilitation Ward')
]
for tenant in tenants: for tenant in tenants:
for i in range(wards_per_tenant): for i in range(wards_per_tenant):
@ -123,7 +114,7 @@ def create_saudi_wards(tenants, wards_per_tenant=12):
ward_id=uuid.uuid4(), ward_id=uuid.uuid4(),
name=ward_name, name=ward_name,
description=f"Specialized {specialty.lower()} ward providing comprehensive inpatient care", description=f"Specialized {specialty.lower()} ward providing comprehensive inpatient care",
ward_type=ward_type[0], ward_type=ward_type,
specialty=specialty, specialty=specialty,
total_beds=total_beds, total_beds=total_beds,
private_rooms=private_rooms, private_rooms=private_rooms,
@ -176,23 +167,15 @@ def create_saudi_beds(wards):
beds = [] beds = []
bed_types = [ bed_types = [
('standard', 'Standard Hospital Bed'), 'STANDARD','ICU','CARDIAC','ISOLATION','BARIATRIC','PEDIATRIC',
('electric', 'Electric Adjustable Bed'), 'NEONATAL','MATERNITY','PSYCHIATRIC','STRETCHER','RECLINER'
('icu', 'ICU Bed'),
('bariatric', 'Bariatric Bed'),
('pediatric', 'Pediatric Bed')
] ]
room_types = [ room_types = ['PRIVATE','SEMI_PRIVATE','SHARED','ICU','ISOLATION']
('private', 'Private Room'),
('semi_private', 'Semi-Private Room'),
('shared', 'Shared Room'),
('isolation', 'Isolation Room')
]
bed_statuses = ['available', 'occupied', 'maintenance', 'blocked'] bed_statuses = ['AVAILABLE','OCCUPIED','RESERVED','MAINTENANCE','CLEANING','OUT_OF_ORDER','BLOCKED']
cleaning_levels = ['standard', 'deep', 'isolation', 'terminal'] cleaning_levels = ['STANDARD','DEEP','ISOLATION','TERMINAL']
for ward in wards: for ward in wards:
for bed_num in range(1, ward.total_beds + 1): for bed_num in range(1, ward.total_beds + 1):
@ -213,13 +196,13 @@ def create_saudi_beds(wards):
ward=ward, ward=ward,
bed_number=bed_number, bed_number=bed_number,
room_number=room_number, room_number=room_number,
bed_type=bed_type[0], bed_type=bed_type,
room_type=room_type[0], room_type=room_type,
status=status, status=status,
occupied_since=django_timezone.now() - timedelta( occupied_since=django_timezone.now() - timedelta(
days=random.randint(1, 30)) if status == 'occupied' else None, days=random.randint(1, 30)) if status == 'OCCUPIED' else None,
reserved_until=django_timezone.now() + timedelta( reserved_until=django_timezone.now() + timedelta(
hours=random.randint(1, 24)) if status == 'available' and random.choice([True, False]) else None, hours=random.randint(1, 24)) if status == 'AVAILABLE' and random.choice([True, False]) else None,
equipment=[ equipment=[
'Cardiac Monitor', 'IV Pole', 'Oxygen Outlet', 'Suction Outlet', 'Cardiac Monitor', 'IV Pole', 'Oxygen Outlet', 'Suction Outlet',
'Nurse Call System', 'Bedside Table', 'Privacy Curtain' 'Nurse Call System', 'Bedside Table', 'Privacy Curtain'
@ -250,34 +233,21 @@ def create_saudi_admissions(tenants, beds, admissions_per_tenant=100):
"""Create Saudi hospital admissions""" """Create Saudi hospital admissions"""
admissions = [] admissions = []
admission_types = [ admission_types = ['EMERGENCY','ELECTIVE','URGENT','OBSERVATION','DAY_SURGERY',
('emergency', 'Emergency Admission'), 'TRANSFER','READMISSION','DIRECT',
('elective', 'Elective Admission'), ]
('urgent', 'Urgent Admission'),
('transfer', 'Transfer from Another Facility')
]
admission_sources = [ admission_sources = ['EMERGENCY','OUTPATIENT','PHYSICIAN_OFFICE','TRANSFER',
('emergency_department', 'Emergency Department'), 'NURSING_HOME','HOME','AMBULATORY_SURGERY'
('outpatient_clinic', 'Outpatient Clinic'), ]
('physician_office', 'Physician Office'),
('transfer_acute', 'Transfer from Acute Care'),
('transfer_ltc', 'Transfer from Long-term Care'),
('direct_admission', 'Direct Admission')
]
statuses = ['active', 'discharged', 'transferred'] statuses = ['PENDING','ADMITTED','TRANSFERRED','DISCHARGED','DECEASED','LEFT_AMA','CANCELLED',]
priorities = ['routine', 'urgent', 'emergent', 'critical'] priorities = ['ROUTINE','URGENT','EMERGENT','CRITICAL']
acuity_levels = ['1', '2', '3', '4', '5'] # 1 = highest acuity acuity_levels = ['1', '2', '3', '4', '5'] # 1 = highest acuity
isolation_types = [ isolation_types = ['CONTACT','DROPLET','AIRBORNE','PROTECTIVE','STRICT',]
'contact', 'droplet', 'airborne', 'protective', 'contact_plus',
'strict_isolation', 'reverse_isolation'
]
code_statuses = [ code_statuses = ['FULL_CODE','DNR','DNI','DNR_DNI','COMFORT_CARE','LIMITED',]
'full_code', 'dnr', 'dni', 'comfort_care', 'limited_intervention'
]
for tenant in tenants: for tenant in tenants:
# Get patients and staff for this tenant # Get patients and staff for this tenant
@ -309,8 +279,8 @@ def create_saudi_admissions(tenants, beds, admissions_per_tenant=100):
admission_id=uuid.uuid4(), admission_id=uuid.uuid4(),
patient=patient, patient=patient,
admission_datetime=admission_datetime, admission_datetime=admission_datetime,
admission_type=random.choice(admission_types)[0], admission_type=random.choice(admission_types),
admission_source=random.choice(admission_sources)[0], admission_source=random.choice(admission_sources),
chief_complaint=random.choice([ chief_complaint=random.choice([
'Chest pain', 'Shortness of breath', 'Abdominal pain', 'Chest pain', 'Shortness of breath', 'Abdominal pain',
'Fever and chills', 'Weakness and fatigue', 'Nausea and vomiting', 'Fever and chills', 'Weakness and fatigue', 'Nausea and vomiting',
@ -375,17 +345,11 @@ def create_saudi_transfers(admissions):
"""Create transfer records for Saudi patients""" """Create transfer records for Saudi patients"""
transfers = [] transfers = []
transfer_types = [ transfer_types = ['WARD','BED','ROOM','UNIT','FACILITY',]
('ward_to_ward', 'Ward to Ward'),
('ward_to_icu', 'Ward to ICU'),
('icu_to_ward', 'ICU to Ward'),
('er_to_ward', 'ER to Ward'),
('ward_to_or', 'Ward to OR')
]
transfer_statuses = ['requested', 'approved', 'in_progress', 'completed', 'cancelled'] transfer_statuses = ['REQUESTED','APPROVED','SCHEDULED','IN_PROGRESS','COMPLETED','CANCELLED','DELAYED',]
transport_methods = ['bed', 'wheelchair', 'stretcher', 'walking', 'ambulance'] transport_methods = ['WHEELCHAIR','STRETCHER','BED','AMBULATORY','AMBULANCE','OTHER',]
priorities = ['routine', 'urgent', 'emergent', 'stat'] priorities = ['ROUTINE','URGENT','EMERGENT','STAT',]
# Create transfers for 30% of admissions # Create transfers for 30% of admissions
for admission in random.sample(admissions, int(len(admissions) * 0.3)): for admission in random.sample(admissions, int(len(admissions) * 0.3)):
@ -394,7 +358,7 @@ def create_saudi_transfers(admissions):
# Get available beds in different wards # Get available beds in different wards
available_beds = Bed.objects.filter( available_beds = Bed.objects.filter(
ward__tenant=admission.tenant, ward__tenant=admission.tenant,
status='available' status='AVAILABLE'
).exclude(ward=admission.current_ward) ).exclude(ward=admission.current_ward)
if not available_beds: if not available_beds:
@ -425,10 +389,10 @@ def create_saudi_transfers(admissions):
scheduled_datetime = None scheduled_datetime = None
actual_datetime = None actual_datetime = None
if status in ['approved', 'in_progress', 'completed']: if status in ['APPROVED', 'IN_PROGRESS', 'COMPLETED']:
scheduled_datetime = requested_datetime + timedelta(hours=random.randint(1, 12)) scheduled_datetime = requested_datetime + timedelta(hours=random.randint(1, 12))
if status in ['in_progress', 'completed']: if status in ['IN_PROGRESS', 'COMPLETED']:
actual_datetime = scheduled_datetime + timedelta(minutes=random.randint(-30, 60)) actual_datetime = scheduled_datetime + timedelta(minutes=random.randint(-30, 60))
# Get transport team members # Get transport team members
@ -444,7 +408,7 @@ def create_saudi_transfers(admissions):
transfer_id=uuid.uuid4(), transfer_id=uuid.uuid4(),
admission=admission, admission=admission,
patient=admission.patient, patient=admission.patient,
transfer_type=transfer_type[0], transfer_type=transfer_type,
from_ward=admission.current_ward, from_ward=admission.current_ward,
from_bed=admission.current_bed, from_bed=admission.current_bed,
to_ward=to_ward, to_ward=to_ward,
@ -473,9 +437,9 @@ def create_saudi_transfers(admissions):
}, },
handoff_report=f"Patient transferred for {random.choice(['specialized care', 'higher acuity monitoring', 'bed availability'])}", handoff_report=f"Patient transferred for {random.choice(['specialized care', 'higher acuity monitoring', 'bed availability'])}",
medications_transferred=random.sample(SAUDI_MEDICATIONS, random.randint(2, 5)), medications_transferred=random.sample(SAUDI_MEDICATIONS, random.randint(2, 5)),
delay_reason="Bed not ready" if status == 'approved' and random.choice([True, False]) else None, delay_reason="Bed not ready" if status == 'APPROVED' and random.choice([True, False]) else None,
complications="None noted" if status == 'completed' else None, complications="None noted" if status == 'COMPLETED' else None,
notes=f"Transfer completed successfully" if status == 'completed' else None, notes=f"Transfer completed successfully" if status == 'COMPLETED' else None,
created_at=requested_datetime, created_at=requested_datetime,
updated_at=requested_datetime + timedelta(hours=random.randint(1, 24)) updated_at=requested_datetime + timedelta(hours=random.randint(1, 24))
) )
@ -485,19 +449,19 @@ def create_saudi_transfers(admissions):
transfer.transport_team.set(transport_team_members) transfer.transport_team.set(transport_team_members)
# Update admission current location if transfer completed # Update admission current location if transfer completed
if status == 'completed': if status == 'COMPLETED':
admission.current_ward = to_ward admission.current_ward = to_ward
admission.current_bed = to_bed admission.current_bed = to_bed
admission.save() admission.save()
# Update bed statuses # Update bed statuses
transfer.from_bed.status = 'available' transfer.from_bed.status = 'AVAILABLE'
transfer.from_bed.current_patient = None transfer.from_bed.current_patient = None
transfer.from_bed.current_admission = None transfer.from_bed.current_admission = None
transfer.from_bed.occupied_since = None transfer.from_bed.occupied_since = None
transfer.from_bed.save() transfer.from_bed.save()
to_bed.status = 'occupied' to_bed.status = 'OCCUPIED'
to_bed.current_patient = admission.patient to_bed.current_patient = admission.patient
to_bed.current_admission = admission to_bed.current_admission = admission
to_bed.occupied_since = actual_datetime to_bed.occupied_since = actual_datetime
@ -513,18 +477,9 @@ def create_saudi_discharge_summaries(admissions):
"""Create discharge summaries for discharged Saudi patients""" """Create discharge summaries for discharged Saudi patients"""
summaries = [] summaries = []
discharged_admissions = [adm for adm in admissions if adm.status == 'discharged' and adm.discharge_datetime] discharged_admissions = [adm for adm in admissions if adm.status == 'DISCHARGED' and adm.discharge_datetime]
discharge_dispositions = [ discharge_dispositions = ['HOME','HOME_HEALTH','NURSING_HOME','REHAB_FACILITY','HOSPICE','TRANSFER','DECEASED','LEFT_AMA','OTHER',]
('home', 'Home'),
('home_health', 'Home with Health Services'),
('skilled_nursing', 'Skilled Nursing Facility'),
('rehabilitation', 'Rehabilitation Facility'),
('ltac', 'Long-term Acute Care'),
('hospice', 'Hospice Care'),
('transfer_acute', 'Transfer to Acute Care'),
('ama', 'Against Medical Advice')
]
for admission in discharged_admissions: for admission in discharged_admissions:
length_of_stay = (admission.discharge_datetime.date() - admission.admission_datetime.date()).days length_of_stay = (admission.discharge_datetime.date() - admission.admission_datetime.date()).days
@ -582,7 +537,7 @@ def create_saudi_discharge_summaries(admissions):
"Chest pain", "Swelling in legs", "Unusual bleeding" "Chest pain", "Swelling in legs", "Unusual bleeding"
], ],
when_to_call="Call physician or return to emergency department if experiencing any warning signs", when_to_call="Call physician or return to emergency department if experiencing any warning signs",
discharge_disposition=random.choice(discharge_dispositions)[0], discharge_disposition=random.choice(discharge_dispositions),
discharge_location="Home" if random.choice([True, True, False]) else "Rehabilitation facility", discharge_location="Home" if random.choice([True, True, False]) else "Rehabilitation facility",
transportation_arranged=True, transportation_arranged=True,
transportation_method=random.choice(['private_vehicle', 'taxi', 'ambulance', 'hospital_transport']), transportation_method=random.choice(['private_vehicle', 'taxi', 'ambulance', 'hospital_transport']),
@ -617,32 +572,14 @@ def create_saudi_surgery_schedules(tenants, surgeries_per_tenant=30):
"""Create surgery schedules for Saudi patients""" """Create surgery schedules for Saudi patients"""
surgeries = [] surgeries = []
surgery_types = [ surgery_types = ['ELECTIVE','URGENT','EMERGENT','TRAUMA','TRANSPLANT',
('elective', 'Elective Surgery'), 'CARDIAC','NEUROSURGERY','ORTHOPEDIC','GENERAL','OTHER',]
('emergency', 'Emergency Surgery'),
('urgent', 'Urgent Surgery'),
('add_on', 'Add-on Surgery')
]
anesthesia_types = [ anesthesia_types = ['GENERAL','REGIONAL','LOCAL','SPINAL','EPIDURAL','MAC','SEDATION','OTHER',]
('general', 'General Anesthesia'),
('regional', 'Regional Anesthesia'),
('local', 'Local Anesthesia'),
('mac', 'Monitored Anesthesia Care'),
('spinal', 'Spinal Anesthesia'),
('epidural', 'Epidural Anesthesia')
]
surgery_statuses = [ surgery_statuses = ['SCHEDULED','CONFIRMED','PREP','IN_PROGRESS','COMPLETED','CANCELLED','POSTPONED','DELAYED',]
('scheduled', 'Scheduled'),
('confirmed', 'Confirmed'),
('in_progress', 'In Progress'),
('completed', 'Completed'),
('cancelled', 'Cancelled'),
('delayed', 'Delayed')
]
priorities = ['routine', 'urgent', 'emergent', 'elective'] priorities = ['ROUTINE','URGENT','EMERGENT','STAT',]
for tenant in tenants: for tenant in tenants:
# Get required staff and patients # Get required staff and patients
@ -674,9 +611,9 @@ def create_saudi_surgery_schedules(tenants, surgeries_per_tenant=30):
actual_end_time = None actual_end_time = None
actual_duration = None actual_duration = None
if status in ['in_progress', 'completed']: if status in ['IN_PROGRESS', 'COMPLETED']:
actual_start_time = datetime.combine(surgery_date, start_time) actual_start_time = datetime.combine(surgery_date, start_time)
if status == 'completed': if status == 'COMPLETED':
actual_duration = estimated_duration + random.randint(-30, 60) actual_duration = estimated_duration + random.randint(-30, 60)
actual_end_time = actual_start_time + timedelta(minutes=actual_duration) actual_end_time = actual_start_time + timedelta(minutes=actual_duration)
@ -687,7 +624,7 @@ def create_saudi_surgery_schedules(tenants, surgeries_per_tenant=30):
admission=admission, # Always provide admission - required field admission=admission, # Always provide admission - required field
procedure_name=procedure, procedure_name=procedure,
procedure_code=f"CPT-{random.randint(10000, 99999)}", procedure_code=f"CPT-{random.randint(10000, 99999)}",
surgery_type=random.choice(surgery_types)[0], surgery_type=random.choice(surgery_types),
scheduled_date=surgery_date, scheduled_date=surgery_date,
scheduled_start_time=start_time, scheduled_start_time=start_time,
estimated_duration_minutes=estimated_duration, estimated_duration_minutes=estimated_duration,
@ -697,7 +634,7 @@ def create_saudi_surgery_schedules(tenants, surgeries_per_tenant=30):
anesthesiologist=random.choice(anesthesiologists), anesthesiologist=random.choice(anesthesiologists),
scrub_nurse=random.choice(nurses), scrub_nurse=random.choice(nurses),
circulating_nurse=random.choice(nurses), circulating_nurse=random.choice(nurses),
anesthesia_type=random.choice(anesthesia_types)[0], anesthesia_type=random.choice(anesthesia_types),
preop_diagnosis=random.choice(SAUDI_COMMON_DIAGNOSES), preop_diagnosis=random.choice(SAUDI_COMMON_DIAGNOSES),
preop_orders=[ preop_orders=[
'NPO after midnight', 'Pre-op antibiotics', 'Type and crossmatch', 'NPO after midnight', 'Pre-op antibiotics', 'Type and crossmatch',
@ -713,13 +650,13 @@ def create_saudi_surgery_schedules(tenants, surgeries_per_tenant=30):
actual_start_time=actual_start_time, actual_start_time=actual_start_time,
actual_end_time=actual_end_time, actual_end_time=actual_end_time,
actual_duration_minutes=actual_duration, actual_duration_minutes=actual_duration,
postop_diagnosis=random.choice(SAUDI_COMMON_DIAGNOSES) if status == 'completed' else None, postop_diagnosis=random.choice(SAUDI_COMMON_DIAGNOSES) if status == 'COMPLETED' else None,
procedure_performed=procedure if status == 'completed' else None, procedure_performed=procedure if status == 'COMPLETED' else None,
complications="None" if status == 'completed' and random.choice([True, True, False]) else None, complications="None" if status == 'COMPLETED' and random.choice([True, True, False]) else None,
recovery_location=random.choice( recovery_location=random.choice(
['PACU', 'ICU', 'Ward', 'Same Day Surgery']) if status == 'completed' else None, ['PACU', 'ICU', 'WARD', 'SAME_DAY']) if status == 'COMPLETED' else None,
priority=random.choice(priorities), priority=random.choice(priorities),
surgery_notes=f"Successful {procedure.lower()} performed without complications" if status == 'completed' else None, surgery_notes=f"Successful {procedure.lower()} performed without complications" if status == 'COMPLETED' else None,
created_at=django_timezone.now() - timedelta(days=random.randint(1, 30)), created_at=django_timezone.now() - timedelta(days=random.randint(1, 30)),
updated_at=django_timezone.now() - timedelta(hours=random.randint(1, 24)) updated_at=django_timezone.now() - timedelta(hours=random.randint(1, 24))
) )

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,7 @@ SAUDI_STATES = [
'Jazan Province', 'Madinah Province', 'Qassim Province', 'Tabuk Province' 'Jazan Province', 'Madinah Province', 'Qassim Province', 'Tabuk Province'
] ]
SAUDI_LANGUAGES = ['Arabic', 'English', 'Urdu', 'Hindi', 'Bengali', 'Tagalog'] SAUDI_LANGUAGES = ['Arabic', 'English']
SAUDI_ALLERGIES = [ SAUDI_ALLERGIES = [
'Penicillin', 'Aspirin', 'Ibuprofen', 'Sulfa drugs', 'Contrast dye', 'Penicillin', 'Aspirin', 'Ibuprofen', 'Sulfa drugs', 'Contrast dye',
@ -110,7 +110,7 @@ def create_saudi_patient_profiles():
date_of_birth = date(birth_year, birth_month, birth_day) date_of_birth = date(birth_year, birth_month, birth_day)
# Generate unique MRN # Generate unique MRN
mrn = f"MRN{tenant.id}{random.randint(100000, 999999)}" mrn = random.randint(100000, 999999)
# Check if MRN already exists # Check if MRN already exists
while PatientProfile.objects.filter(mrn=mrn).exists(): while PatientProfile.objects.filter(mrn=mrn).exists():

BIN
templates/.DS_Store vendored

Binary file not shown.

View File

@ -18,10 +18,18 @@
<!-- ================== BEGIN core-css ================== --> <!-- ================== BEGIN core-css ================== -->
<link href="{% static 'css/vendor.min.css' %}" rel="stylesheet" /> <link href="{% static 'css/vendor.min.css' %}" rel="stylesheet" />
{# <link href="{% static 'css/default/app.min.css' %}" rel="stylesheet" />#} <link href="{% static 'css/default/app.min.css' %}" rel="stylesheet" />
<link href="{% static 'css/apple/app.min.css' %}" rel="stylesheet" /> {# <link href="{% static 'css/apple/app.min.css' %}" rel="stylesheet" />#}
{# <link href="{% static 'css/transparent/app.min.css' %}" rel="stylesheet" />#} {# <link href="{% static 'css/transparent/app.min.css' %}" rel="stylesheet" />#}
<link href="{% static 'css/custom.css' %}" rel="stylesheet" /> <link href="{% static 'css/custom.css' %}" rel="stylesheet" />
<head>
<link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" rel="stylesheet"/>
</head>
<body>
<!-- Your page content -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
</body>
<script src="{% static 'plugins/apexcharts/dist/apexcharts.min.js' %}"></script> <script src="{% static 'plugins/apexcharts/dist/apexcharts.min.js' %}"></script>
<!-- HTMX --> <!-- HTMX -->
<script src="{% static 'js/htmx.min.js' %}"></script> <script src="{% static 'js/htmx.min.js' %}"></script>

View File

@ -261,22 +261,31 @@
</div> </div>
<div> <div>
<div class="fw-semibold">{{ admission.patient.get_full_name }}</div> <div class="fw-semibold">{{ admission.patient.get_full_name }}</div>
<small class="text-muted">{{ admission.patient.patient_id }}</small> <small class="text-muted">{{ admission.patient.mrn }}</small>
</div> </div>
</div> </div>
</td> </td>
<td> <td>
<div>{{ admission.ward.name }}</div> <div>{{ admission.current_ward.name }}</div>
<small class="text-muted">Bed {{ admission.bed.bed_number }}</small> <small class="text-muted">Bed {{ admission.current_bed.bed_number }}</small>
</td> </td>
<td> <td>
<div>{{ admission.admission_date|date:"M d, Y" }}</div> <div>{{ admission.admission_datetime|date:"M d, Y" }}</div>
<small class="text-muted">{{ admission.admission_time|time:"H:i" }}</small> <small class="text-muted ">{{ admission.admission_datetime|time:"H:i" }}</small>
</td> </td>
<td> <td>
<span class="badge bg-{% if admission.admission_type == 'EMERGENCY' %}danger{% elif admission.admission_type == 'ELECTIVE' %}primary{% elif admission.admission_type == 'TRANSFER' %}info{% else %}secondary{% endif %}"> <div class="badge
{{ admission.get_admission_type_display }} bg-{% if admission.admission_type == 'EMERGENCY' %}danger
</span> {% elif admission.admission_type == 'ELECTIVE' %}primary
{% elif admission.admission_type == 'TRANSFER' %}info
{% elif admission.admission_type == 'URGENT' %}yellow
{% elif admission.admission_type == 'OBSERVATION' %}indego
{% elif admission.admission_type == 'DAY_SURGERY' %}purple
{% elif admission.admission_type == 'DIRECT' %}red
{% else %}secondary
{% endif %}">
{{ admission.admission_type }}
</div>
</td> </td>
<td> <td>
{% if admission.attending_physician %} {% if admission.attending_physician %}
@ -315,9 +324,10 @@
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle dropdown-toggle-split" <button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle dropdown-toggle-split"
data-bs-toggle="dropdown"> data-bs-toggle="dropdown">
<span class="visually-hidden">Toggle Dropdown</span> <span class="visually-hidden">Toggle Dropdown</span>
<i class="fas fa-ellipsis-v"></i>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a class="dropdown-item" href="{% url 'inpatients:admission_edit' admission.pk %}"> <li><a class="dropdown-item" href="{% url 'inpatients:admission_update' admission.pk %}">
<i class="fas fa-edit me-2"></i>Edit <i class="fas fa-edit me-2"></i>Edit
</a></li> </a></li>
{% if admission.status == 'ADMITTED' %} {% if admission.status == 'ADMITTED' %}
@ -365,48 +375,7 @@
<!-- Pagination --> <!-- Pagination -->
{% if is_paginated %} {% if is_paginated %}
<div class="card-footer"> {% include 'partial/pagination.html'%}
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted">
Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} admissions
</span>
</div>
<nav aria-label="Admissions pagination">
<ul class="pagination pagination-sm mb-0">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page=1">First</a>
</li>
<li class="page-item">
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ page_obj.previous_page_number }}">Previous</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active">
<span class="page-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ page_obj.next_page_number }}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?{% for key, value in request.GET.items %}{% if key != 'page' %}{{ key }}={{ value }}&{% endif %}{% endfor %}page={{ page_obj.paginator.num_pages }}">Last</a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@ -15,11 +15,12 @@
</div> </div>
<div class="ms-auto"> <div class="ms-auto">
<div class="btn-group"> <div class="btn-group">
<a href="{% url 'inpatients:bed_form' object.pk %}" class="btn btn-primary"> <a href="{% url 'inpatients:bed_update' object.pk %}" class="btn btn-primary">
<i class="fas fa-edit me-2"></i>Edit Bed <i class="fas fa-edit me-2"></i>Edit Bed
</a> </a>
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown"> <button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown">
<span class="visually-hidden">Toggle Dropdown</span> <span class="visually-hidden">Toggle Dropdown</span>
<i class="fas fa-ellipsis-v"></i>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="updateBedStatus('available')"> <li><a class="dropdown-item" href="#" onclick="updateBedStatus('available')">
@ -35,9 +36,9 @@
<i class="fas fa-broom text-primary me-2"></i>Mark for Cleaning <i class="fas fa-broom text-primary me-2"></i>Mark for Cleaning
</a></li> </a></li>
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="{% url 'inpatients:bed_confirm_delete' object.pk %}"> {# <li><a class="dropdown-item text-danger" href="{% url 'inpatients:bed_confirm_delete' object.pk %}">#}
<i class="fas fa-trash me-2"></i>Delete Bed {# <i class="fas fa-trash me-2"></i>Delete Bed#}
</a></li> {# </a></li>#}
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -45,6 +45,15 @@
border-color: #b6effb; border-color: #b6effb;
} }
.bed-reserved {
background-color: #fff3cd;
color: #856404;
}
.bed-out_of_order {
background-color: #b6b4b4;
color: #434242;
}
.bed-status-available { .bed-status-available {
color: #198754; color: #198754;
} }
@ -65,6 +74,40 @@
color: #0dcaf0; color: #0dcaf0;
} }
.bed-status-reserved {
color: #ffc107;
}
.bed-status-out_of_order {
color: #6c757d;
}
.bg-available {
background-color: #198754;
}
.bg-occupied {
background-color: #dc3545;
}
.bg-maintenance {
background-color: #fd7e14;
}
.bg-blocked {
background-color: #6c757d;
}
.bg-cleaning {
background-color: #0dcaf0;
}
.bg-reserved {
background-color: #ffc107;
}
.bg-out_of_order {
background-color: #6c757d;
}
.bed-icon { .bed-icon {
position: relative; position: relative;
} }
@ -257,13 +300,13 @@
<div class="progress-bar bg-info" <div class="progress-bar bg-info"
role="progressbar" role="progressbar"
style="width: {{ occupancy_rate }}%" style="width: {{ occupancy_rate }}%"
title="{{ occupancy_rate }}% occupancy"> title="{{ occupancy_rate|floatformat:0 }}% occupancy">
{{ occupancy_rate }}% {{ occupancy_rate|floatformat:0}}%
</div> </div>
</div> </div>
</div> </div>
<div class="ms-3"> <div class="ms-3">
<h4 class="mb-0">{{ occupancy_rate }}%</h4> <h4 class="mb-0">{{ occupancy_rate|floatformat:0 }}%</h4>
</div> </div>
</div> </div>
<div class="mt-2"> <div class="mt-2">
@ -278,17 +321,18 @@
<h5 class="card-title mb-0">Ward Distribution</h5> <h5 class="card-title mb-0">Ward Distribution</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
{% for ward in ward_stats %} {% for ward in wards %}
<div class="d-flex justify-content-between align-items-center mb-2"> <div class="d-flex justify-content-between align-items-center mb-2">
<span>{{ ward.name }}</span> <span>{{ ward.name }}</span>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<div class="progress me-2" style="width: 100px; height: 8px;"> <div class="progress me-2" style="width: 100px; height: 8px;">
<div class="progress-bar bg-{{ ward.occupancy_color }}" <div class="progress-bar
bg-{%if ward.occupancy_rate >= 90%}red{% elif ward.occupancy_rate >= 75 %}orange{% elif ward.occupancy_rate >= 50 %}blue{% else %}green{% endif %}"
role="progressbar" role="progressbar"
style="width: {{ ward.occupancy_rate }}%"> style="width: {{ ward.occupancy_rate }}%">
</div> </div>
</div> </div>
<span class="text-muted">{{ ward.occupied }}/{{ ward.total }}</span> <span class="text-muted">{{ ward.occupancy_rate|floatformat:0 }}%</span>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
@ -332,23 +376,23 @@
<div class="col-md-3"> <div class="col-md-3">
<select class="form-select" id="statusFilter"> <select class="form-select" id="statusFilter">
<option value="">All Status</option> <option value="">All Status</option>
<option value="available">Available</option> <option value="AVAILABLE">Available</option>
<option value="occupied">Occupied</option> <option value="OCCUPIED">Occupied</option>
<option value="maintenance">Maintenance</option> <option value="MAINTENANCE">Maintenance</option>
<option value="blocked">Blocked</option> <option value="BLOCKED">Blocked</option>
<option value="cleaning">Cleaning</option> <option value="CLEANING">Cleaning</option>
</select>
</div>
<div class="col-md-3">
<select class="form-select" id="bedTypeFilter">
<option value="">All Types</option>
<option value="standard">Standard</option>
<option value="icu">ICU</option>
<option value="isolation">Isolation</option>
<option value="pediatric">Pediatric</option>
<option value="maternity">Maternity</option>
</select> </select>
</div> </div>
{# <div class="col-md-3">#}
{# <select class="form-select" id="bedTypeFilter">#}
{# <option value="">All Types</option>#}
{# <option value="standard">Standard</option>#}
{# <option value="icu">ICU</option>#}
{# <option value="isolation">Isolation</option>#}
{# <option value="pediatric">Pediatric</option>#}
{# <option value="maternity">Maternity</option>#}
{# </select>#}
{# </div>#}
<div class="col-md-3"> <div class="col-md-3">
<div class="input-group"> <div class="input-group">
<input type="text" class="form-control" placeholder="Search beds..." id="bedSearch"> <input type="text" class="form-control" placeholder="Search beds..." id="bedSearch">
@ -370,30 +414,80 @@
<div class="row"> <div class="row">
{% for bed in ward.beds.all %} {% for bed in ward.beds.all %}
<div class="col-md-2 col-sm-3 col-4 mb-3"> <div class="col-md-2 col-sm-3 col-4 mb-3">
<div class="bed-card card h-100 bed-{{ bed.status }}" <div class="bed-card card h-100 bed-{{ bed.status|lower }}"
data-bed-id="{{ bed.id }}" data-bed-id="{{ bed.id }}"
data-status="{{ bed.status }}" data-status="{{ bed.status|lower }}"
data-type="{{ bed.bed_type }}" data-type="{{ bed.bed_type }}"
onclick="selectBed('{{ bed.id }}')"> onclick="selectBed('{{ bed.id }}')">
<div class="card-body p-2 text-center"> <div class="card-body p-2 text-center">
<div class="bed-icon mb-2"> <div class="bed-icon mb-2">
<i class="fa fa-bed fa-2x bed-status-{{ bed.status }}"></i> <i class="fa fa-bed fa-2x bed-status-{{ bed.status|lower }}"></i>
</div> </div>
<h6 class="card-title mb-1">{{ bed.bed_number }}</h6> <h6 class="card-title mb-1">{{ bed.bed_number }}</h6>
<small class="text-muted">{{ bed.room_number }}</small> <small class="text-muted">{{ bed.room_number }}</small>
{% if bed.current_patient %} {% if bed.current_admission %}
<div class="mt-2"> <div class="mt-2">
<small class="text-truncate d-block">{{ bed.current_patient.get_full_name }}</small> <small class="text-truncate d-block">{{ bed.current_patient.get_full_name }}</small>
<small class="text-muted">{{ bed.occupied_since|timesince }} ago</small> <small class="text-muted">{{ bed.occupied_since|timesince }} ago</small>
</div> </div>
{% endif %} {% endif %}
<div class="bed-status-badge"> <div class="bed-status-badge">
<span class="badge bg-{{ bed.status_color }} badge-sm"> <span class="badge bg-{{ bed.status|lower }} badge-sm">
{{ bed.get_status_display }} {{ bed.get_status_display }}
</span> </span>
</div> </div>
</div> </div>
</div>
<div class="card-footer">
<div class="btn-group btn-group-sm">
<a class="btn btn-sm btn-primary" href="{% url 'inpatients:bed_detail' bed.id %}" title="View Details">
<i class="fa fa-eye"></i>
</a>
{# <button class="btn btn-outline-primary" onclick="viewBedDetails('{{ bed.id }}')" title="View Details">#}
{# <i class="fa fa-eye"></i>#}
{# </button>#}
<a class="btn btn-sm btn-secondary" href="{% url 'inpatients:bed_update' bed.id %}" title="Edit">
<i class="fa fa-edit"></i>
</a>
{% if bed.status == 'AVAILABLE' %}
<a class="btn btn-sm btn-success" href="{% url 'inpatients:admission_create' %}" title="Assign Patient">
<i class="fa fa-user-plus"></i>
</a>
{% elif bed.status == 'OCCUPIED' %}
{% if bed.current_admission %}
<a class="btn btn-sm btn-warning" href="{% url 'inpatients:discharge_patient' bed.current_admission.id %}" title="Discharge">
<i class="fa fa-sign-out-alt"></i>
</a>
{% endif %}
{# <button class="btn btn-outline-warning" onclick="dischargePatient('{{ bed.current_admission.id }}')" title="Discharge">#}
{# <i class="fa fa-sign-out-alt"></i>#}
{# </button>#}
{% endif %}
<div class="btn-group btn-group-sm">
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" title="More Actions">
<i class="fa fa-ellipsis-v"></i>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{% url 'inpatients:maintenance_bed' bed.id %}">
<i class="fa fa-tools me-2"></i>Schedule Maintenance
</a></li>
<li><a class="dropdown-item" href="#" onclick="scheduleCleaning('{{ bed.id }}')">
<i class="fa fa-broom me-2"></i>Schedule Cleaning
</a></li>
<li><a class="dropdown-item" href="#" onclick="blockBed('{{ bed.id }}')">
<i class="fa fa-ban me-2"></i>Block Bed
</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="viewHistory('{{ bed.id }}')">
<i class="fa fa-history me-2"></i>View History
</a></li>
</ul>
</div>
</div>
</div>
</div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
@ -418,14 +512,14 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for bed in all_beds %} {% for bed in beds %}
<tr data-bed-id="{{ bed.id }}" <tr data-bed-id="{{ bed.id }}"
data-status="{{ bed.status }}" data-status="{{ bed.status|lower }}"
data-type="{{ bed.bed_type }}" data-type="{{ bed.bed_type }}"
data-ward="{{ bed.ward.id }}"> data-ward="{{ bed.ward.id }}">
<td> <td>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<i class="fa fa-bed me-2 bed-status-{{ bed.status }}"></i> <i class="fa fa-bed me-2 bed-{{ bed.status|lower }}"></i>
<strong>{{ bed.bed_number }}</strong> <strong>{{ bed.bed_number }}</strong>
</div> </div>
</td> </td>
@ -435,15 +529,15 @@
<span class="badge bg-secondary">{{ bed.get_bed_type_display }}</span> <span class="badge bg-secondary">{{ bed.get_bed_type_display }}</span>
</td> </td>
<td> <td>
<span class="badge bg-{{ bed.status_color }}"> <span class="badge bg-{{ bed.status|lower }}">
{{ bed.get_status_display }} {{ bed.get_status_display }}
</span> </span>
</td> </td>
<td> <td>
{% if bed.current_patient %} {% if bed.current_admission %}
<div> <div>
<strong>{{ bed.current_patient.get_full_name }}</strong> <strong>{{ bed.current_patient.get_full_name }}</strong>
<br><small class="text-muted">{{ bed.current_patient.patient_id }}</small> <br><small class="text-muted">{{ bed.current_patient.mrn }}</small>
</div> </div>
{% else %} {% else %}
<span class="text-muted">-</span> <span class="text-muted">-</span>
@ -461,20 +555,28 @@
</td> </td>
<td> <td>
<div class="btn-group btn-group-sm"> <div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary" onclick="viewBedDetails('{{ bed.id }}')" title="View Details"> <a class="btn btn-outline-primary" href="{% url 'inpatients:bed_detail' bed.id %}" title="View Details">
<i class="fa fa-eye"></i> <i class="fa fa-eye"></i>
</button> </a>
{# <button class="btn btn-outline-primary" onclick="viewBedDetails('{{ bed.id }}')" title="View Details">#}
{# <i class="fa fa-eye"></i>#}
{# </button>#}
<button class="btn btn-outline-secondary" onclick="editBed('{{ bed.id }}')" title="Edit"> <button class="btn btn-outline-secondary" onclick="editBed('{{ bed.id }}')" title="Edit">
<i class="fa fa-edit"></i> <i class="fa fa-edit"></i>
</button> </button>
{% if bed.status == 'available' %} {% if bed.status == 'AVAILABLE' %}
<button class="btn btn-outline-success" onclick="assignPatient('{{ bed.id }}')" title="Assign Patient"> <button class="btn btn-outline-success" onclick="assignPatient('{{ bed.id }}')" title="Assign Patient">
<i class="fa fa-user-plus"></i> <i class="fa fa-user-plus"></i>
</button> </button>
{% elif bed.status == 'occupied' %} {% elif bed.status == 'OCCUPIED' %}
<button class="btn btn-outline-warning" onclick="dischargePatient('{{ bed.id }}')" title="Discharge"> {% if bed.current_admission %}
<a class="btn btn-outline-warning" href="{% url 'inpatients:discharge_patient' bed.current_admission.id %}" title="Discharge">
<i class="fa fa-sign-out-alt"></i> <i class="fa fa-sign-out-alt"></i>
</button> </a>
{% endif %}
{# <button class="btn btn-outline-warning" onclick="dischargePatient('{{ bed.current_admission.id }}')" title="Discharge">#}
{# <i class="fa fa-sign-out-alt"></i>#}
{# </button>#}
{% endif %} {% endif %}
<div class="btn-group btn-group-sm"> <div class="btn-group btn-group-sm">
<button class="btn btn-outline-info dropdown-toggle" data-bs-toggle="dropdown" title="More Actions"> <button class="btn btn-outline-info dropdown-toggle" data-bs-toggle="dropdown" title="More Actions">
@ -502,6 +604,11 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<div class="text-center">
{% if is_paginated %}
{% include 'partial/pagination.html' %}
{% endif %}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -582,6 +689,7 @@
{% endblock %} {% endblock %}
{% block js %} {% block js %}
<script src="{% static 'plugins/dropzone/dist/min/dropzone.min.js' %}"></script>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
setupEventHandlers(); setupEventHandlers();
@ -696,21 +804,21 @@ function assignPatientToBed() {
}); });
} }
function dischargePatient(bedId) { {#function dischargePatient(admissionId) {#}
if (confirm('Are you sure you want to discharge the patient from this bed?')) { {# if (confirm('Are you sure you want to discharge the patient from this bed?')) {#}
$.post('', { {# $.post('{% url 'inpatients:discharge_patient' 0 %}'.replace('0', admissionId), {#}
bed_id: bedId, {#bed_id: bedId,#}
csrfmiddlewaretoken: '{{ csrf_token }}' {# csrfmiddlewaretoken: '{{ csrf_token }}'#}
}, function(data) { {# }, function(data) {#}
if (data.success) { {# if (data.success) {#}
toastr.success('Patient discharged successfully'); {# toastr.success('Patient discharged successfully');#}
updateBedStatuses(); {# updateBedStatuses();#}
} else { {# } else {#}
toastr.error('Failed to discharge patient: ' + data.error); {# toastr.error('Failed to discharge patient: ' + data.error);#}
} {# }#}
}); {# });#}
} {# }#}
} {# }#}
function scheduleMaintenance(bedId) { function scheduleMaintenance(bedId) {
$.post('', { $.post('', {
@ -792,8 +900,8 @@ function updateBedStatuses() {
.addClass('bed-' + bed.status); .addClass('bed-' + bed.status);
bedCard.find('.bed-status-badge .badge') bedCard.find('.bed-status-badge .badge')
.removeClass('bg-success bg-danger bg-warning bg-secondary bg-info') .removeClass('bg-success bg-danger bg-warning bg-secondary bg-info')
.addClass('bg-' + bed.status_color) .addClass('bg-' + bed.status)
.text(bed.status_display); .text(bed.status);
// Update row // Update row
bedRow.find('.badge').first() bedRow.find('.badge').first()

View File

@ -297,7 +297,7 @@
<small class="text-muted">{{ ward.get_ward_type_display }}</small> <small class="text-muted">{{ ward.get_ward_type_display }}</small>
</div> </div>
<div class="text-end"> <div class="text-end">
<span class="badge bg-primary">{{ ward.occupied_beds }}/{{ ward.total_beds }}</span> <span class="badge bg-primary">{{ ward.o }}/{{ ward.total_beds }}</span>
<br><small class="text-muted">{{ ward.occupancy_rate|floatformat:0 }}%</small> <br><small class="text-muted">{{ ward.occupancy_rate|floatformat:0 }}%</small>
</div> </div>
</div> </div>

View File

@ -0,0 +1,670 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Bed Maintenance Management{% endblock %}
{% block extra_css %}
<link href="{% static 'assets/plugins/datatables.net-bs5/css/dataTables.bootstrap5.min.css' %}" rel="stylesheet" />
<link href="{% static 'assets/plugins/datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css' %}" rel="stylesheet" />
<style>
.maintenance-status-scheduled { color: #ffc107; }
.maintenance-status-in-progress { color: #17a2b8; }
.maintenance-status-completed { color: #28a745; }
.maintenance-status-cancelled { color: #6c757d; }
.maintenance-status-overdue { color: #dc3545; }
.priority-low { color: #28a745; }
.priority-medium { color: #ffc107; }
.priority-high { color: #fd7e14; }
.priority-critical { color: #dc3545; }
.bed-status-available { background-color: #d4edda; color: #155724; }
.bed-status-occupied { background-color: #f8d7da; color: #721c24; }
.bed-status-maintenance { background-color: #fff3cd; color: #856404; }
.bed-status-out-of-order { background-color: #f5c6cb; color: #721c24; }
.maintenance-card {
transition: all 0.3s ease;
border-left: 4px solid #dee2e6;
}
.maintenance-card.scheduled { border-left-color: #ffc107; }
.maintenance-card.in-progress { border-left-color: #17a2b8; }
.maintenance-card.completed { border-left-color: #28a745; }
.maintenance-card.overdue { border-left-color: #dc3545; }
.maintenance-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.stats-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 10px;
}
.stats-card.warning {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.stats-card.success {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.stats-card.info {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
@media (max-width: 768px) {
.maintenance-card {
margin-bottom: 15px;
}
.table-responsive {
font-size: 0.875rem;
}
}
</style>
{% endblock %}
{% block content %}
<div id="content" class="app-content">
<!-- Page Header -->
<div class="d-flex align-items-center mb-3">
<div>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'inpatients:dashboard' %}">Inpatients</a></li>
<li class="breadcrumb-item active">Bed Maintenance</li>
</ol>
<h1 class="page-header mb-0">
<i class="fas fa-tools me-2"></i>Bed Maintenance Management
</h1>
</div>
<div class="ms-auto">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#scheduleMaintenanceModal">
<i class="fas fa-plus me-1"></i>Schedule Maintenance
</button>
</div>
</div>
<!-- Statistics Cards -->
<div class="row mb-4">
<div class="col-xl-3 col-md-6">
<div class="card stats-card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="text-white-75 mb-1">Total Beds</div>
<div class="h3 mb-0 text-white">{{ stats.total_beds|default:0 }}</div>
</div>
<div class="ms-3">
<i class="fas fa-bed fa-2x text-white-50"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card stats-card warning">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="text-white-75 mb-1">Under Maintenance</div>
<div class="h3 mb-0 text-white">{{ stats.under_maintenance|default:0 }}</div>
</div>
<div class="ms-3">
<i class="fas fa-wrench fa-2x text-white-50"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card stats-card success">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="text-white-75 mb-1">Scheduled Today</div>
<div class="h3 mb-0 text-white">{{ stats.scheduled_today|default:0 }}</div>
</div>
<div class="ms-3">
<i class="fas fa-calendar-check fa-2x text-white-50"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="card stats-card info">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<div class="text-white-75 mb-1">Overdue</div>
<div class="h3 mb-0 text-white">{{ stats.overdue|default:0 }}</div>
</div>
<div class="ms-3">
<i class="fas fa-exclamation-triangle fa-2x text-white-50"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Filters and Search -->
<div class="card mb-4">
<div class="card-body">
<div class="row g-3">
<div class="col-md-3">
<label class="form-label">Ward/Unit</label>
<select class="form-select" id="ward-filter">
<option value="">All Wards</option>
{% for ward in wards %}
<option value="{{ ward.id }}">{{ ward.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-3">
<label class="form-label">Maintenance Status</label>
<select class="form-select" id="status-filter">
<option value="">All Statuses</option>
<option value="scheduled">Scheduled</option>
<option value="in_progress">In Progress</option>
<option value="completed">Completed</option>
<option value="cancelled">Cancelled</option>
<option value="overdue">Overdue</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">Priority</label>
<select class="form-select" id="priority-filter">
<option value="">All Priorities</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
<option value="critical">Critical</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">Date Range</label>
<select class="form-select" id="date-filter">
<option value="">All Dates</option>
<option value="today">Today</option>
<option value="week">This Week</option>
<option value="month">This Month</option>
<option value="overdue">Overdue</option>
</select>
</div>
</div>
<div class="row mt-3">
<div class="col-md-6">
<div class="input-group">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text" class="form-control" id="search-input" placeholder="Search beds, rooms, or maintenance notes...">
</div>
</div>
<div class="col-md-6 text-end">
<button type="button" class="btn btn-outline-secondary me-2" onclick="clearFilters()">
<i class="fas fa-times me-1"></i>Clear Filters
</button>
<button type="button" class="btn btn-success" onclick="exportMaintenanceReport()">
<i class="fas fa-file-excel me-1"></i>Export Report
</button>
</div>
</div>
</div>
</div>
<!-- Maintenance Schedule Table -->
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">
<i class="fas fa-list me-2"></i>Maintenance Schedule
</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table id="maintenanceTable" class="table table-striped table-hover">
<thead>
<tr>
<th>Bed</th>
<th>Ward/Room</th>
<th>Maintenance Type</th>
<th>Priority</th>
<th>Scheduled Date</th>
<th>Status</th>
<th>Assigned To</th>
<th>Progress</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for maintenance in maintenance_schedules %}
<tr>
<td>
<div class="d-flex align-items-center">
<i class="fas fa-bed me-2 text-muted"></i>
<div>
<div class="fw-bold">{{ maintenance.bed.bed_number }}</div>
<small class="text-muted">{{ maintenance.bed.bed_type }}</small>
</div>
</div>
</td>
<td>
<div>
<div class="fw-bold">{{ maintenance.bed.ward.name }}</div>
<small class="text-muted">Room {{ maintenance.bed.room_number }}</small>
</div>
</td>
<td>
<span class="badge bg-info">{{ maintenance.get_maintenance_type_display }}</span>
</td>
<td>
<span class="badge priority-{{ maintenance.priority }}">
{{ maintenance.get_priority_display }}
</span>
</td>
<td>
<div>
<div>{{ maintenance.scheduled_date|date:"M d, Y" }}</div>
<small class="text-muted">{{ maintenance.scheduled_time|time:"H:i" }}</small>
</div>
</td>
<td>
<span class="badge maintenance-status-{{ maintenance.status }}">
{{ maintenance.get_status_display }}
</span>
</td>
<td>
{% if maintenance.assigned_to %}
<div>
<div class="fw-bold">{{ maintenance.assigned_to.get_full_name }}</div>
<small class="text-muted">{{ maintenance.assigned_to.department }}</small>
</div>
{% else %}
<span class="text-muted">Not assigned</span>
{% endif %}
</td>
<td>
<div class="progress" style="height: 6px;">
<div class="progress-bar" role="progressbar"
style="width: {{ maintenance.progress_percentage }}%"
aria-valuenow="{{ maintenance.progress_percentage }}"
aria-valuemin="0" aria-valuemax="100"></div>
</div>
<small class="text-muted">{{ maintenance.progress_percentage }}%</small>
</td>
<td>
<div class="btn-group" role="group">
<button type="button" class="btn btn-sm btn-outline-primary"
onclick="viewMaintenance('{{ maintenance.id }}')">
<i class="fas fa-eye"></i>
</button>
{% if maintenance.status == 'scheduled' %}
<button type="button" class="btn btn-sm btn-outline-success"
onclick="startMaintenance('{{ maintenance.id }}')">
<i class="fas fa-play"></i>
</button>
{% endif %}
{% if maintenance.status == 'in_progress' %}
<button type="button" class="btn btn-sm btn-outline-warning"
onclick="updateProgress('{{ maintenance.id }}')">
<i class="fas fa-edit"></i>
</button>
{% endif %}
<button type="button" class="btn btn-sm btn-outline-secondary"
onclick="editMaintenance('{{ maintenance.id }}')">
<i class="fas fa-edit"></i>
</button>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="9" class="text-center py-4">
<div class="text-muted">
<i class="fas fa-tools fa-3x mb-3"></i>
<p>No maintenance schedules found.</p>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#scheduleMaintenanceModal">
<i class="fas fa-plus me-1"></i>Schedule First Maintenance
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Schedule Maintenance Modal -->
<div class="modal fade" id="scheduleMaintenanceModal" tabindex="-1" aria-labelledby="scheduleMaintenanceModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="scheduleMaintenanceModalLabel">
<i class="fas fa-calendar-plus me-2"></i>Schedule Bed Maintenance
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="scheduleMaintenanceForm" method="post">
{% csrf_token %}
<div class="modal-body">
<div class="row">
<div class="col-md-6">
<div class="form-group mb-3">
<label class="form-label">Ward <span class="text-danger">*</span></label>
<select class="form-select" name="ward" id="ward-select" required>
<option value="">Select ward...</option>
{% for ward in wards %}
<option value="{{ ward.id }}">{{ ward.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-3">
<label class="form-label">Bed <span class="text-danger">*</span></label>
<select class="form-select" name="bed" id="bed-select" required>
<option value="">Select bed...</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group mb-3">
<label class="form-label">Maintenance Type <span class="text-danger">*</span></label>
<select class="form-select" name="maintenance_type" required>
<option value="">Select type...</option>
<option value="routine">Routine Inspection</option>
<option value="cleaning">Deep Cleaning</option>
<option value="repair">Repair</option>
<option value="replacement">Part Replacement</option>
<option value="calibration">Equipment Calibration</option>
<option value="safety_check">Safety Check</option>
<option value="preventive">Preventive Maintenance</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-3">
<label class="form-label">Priority <span class="text-danger">*</span></label>
<select class="form-select" name="priority" required>
<option value="low">Low</option>
<option value="medium" selected>Medium</option>
<option value="high">High</option>
<option value="critical">Critical</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group mb-3">
<label class="form-label">Scheduled Date <span class="text-danger">*</span></label>
<input type="date" class="form-control" name="scheduled_date" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-3">
<label class="form-label">Scheduled Time <span class="text-danger">*</span></label>
<input type="time" class="form-control" name="scheduled_time" required>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group mb-3">
<label class="form-label">Assign To</label>
<select class="form-select" name="assigned_to">
<option value="">Select technician...</option>
{% for technician in maintenance_staff %}
<option value="{{ technician.id }}">{{ technician.get_full_name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group mb-3">
<label class="form-label">Estimated Duration (hours)</label>
<input type="number" class="form-control" name="estimated_duration"
min="0.5" step="0.5" placeholder="2.0">
</div>
</div>
</div>
<div class="form-group mb-3">
<label class="form-label">Description/Notes</label>
<textarea class="form-control" name="description" rows="3"
placeholder="Describe the maintenance work to be performed..."></textarea>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="notify_staff" id="notify-staff" checked>
<label class="form-check-label" for="notify-staff">
Notify assigned staff member
</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times me-1"></i>Cancel
</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>Schedule Maintenance
</button>
</div>
</form>
</div>
</div>
</div>
<!-- View Maintenance Modal -->
<div class="modal fade" id="viewMaintenanceModal" tabindex="-1" aria-labelledby="viewMaintenanceModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="viewMaintenanceModalLabel">
<i class="fas fa-eye me-2"></i>Maintenance Details
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" id="maintenance-details">
<!-- Content loaded via AJAX -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times me-1"></i>Close
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script src="{% static 'assets/plugins/datatables.net/js/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-responsive/js/dataTables.responsive.min.js' %}"></script>
<script src="{% static 'assets/plugins/datatables.net-responsive-bs5/js/responsive.bootstrap5.min.js' %}"></script>
<script>
$(document).ready(function() {
// Initialize DataTable
const table = $('#maintenanceTable').DataTable({
responsive: true,
pageLength: 25,
order: [[4, 'asc']], // Sort by scheduled date
columnDefs: [
{ orderable: false, targets: [8] } // Actions column
]
});
// Filter functionality
$('#ward-filter, #status-filter, #priority-filter, #date-filter').on('change', function() {
applyFilters();
});
$('#search-input').on('keyup', function() {
table.search(this.value).draw();
});
// Ward selection for bed loading
$('#ward-select').on('change', function() {
const wardId = $(this).val();
const bedSelect = $('#bed-select');
bedSelect.empty().append('<option value="">Loading beds...</option>');
if (wardId) {
$.ajax({
url: '{% url "inpatients:get_ward_beds" %}',
data: { ward_id: wardId },
success: function(data) {
bedSelect.empty().append('<option value="">Select bed...</option>');
data.beds.forEach(function(bed) {
bedSelect.append(`<option value="${bed.id}">${bed.bed_number} - ${bed.bed_type}</option>`);
});
},
error: function() {
bedSelect.empty().append('<option value="">Error loading beds</option>');
}
});
} else {
bedSelect.empty().append('<option value="">Select bed...</option>');
}
});
// Form submission
$('#scheduleMaintenanceForm').on('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
$.ajax({
url: '{% url "inpatients:schedule_maintenance" %}',
method: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(response) {
if (response.success) {
$('#scheduleMaintenanceModal').modal('hide');
location.reload(); // Refresh page to show new maintenance
} else {
alert('Error scheduling maintenance: ' + response.message);
}
},
error: function() {
alert('Error scheduling maintenance. Please try again.');
}
});
});
});
function applyFilters() {
const ward = $('#ward-filter').val();
const status = $('#status-filter').val();
const priority = $('#priority-filter').val();
const dateRange = $('#date-filter').val();
// Apply filters to DataTable
const table = $('#maintenanceTable').DataTable();
// Custom filtering logic would go here
// For now, we'll just trigger a page refresh with query parameters
const params = new URLSearchParams();
if (ward) params.append('ward', ward);
if (status) params.append('status', status);
if (priority) params.append('priority', priority);
if (dateRange) params.append('date_range', dateRange);
const url = window.location.pathname + (params.toString() ? '?' + params.toString() : '');
window.location.href = url;
}
function clearFilters() {
$('#ward-filter, #status-filter, #priority-filter, #date-filter').val('');
$('#search-input').val('');
window.location.href = window.location.pathname;
}
function viewMaintenance(maintenanceId) {
$.ajax({
url: '{% url "inpatients:maintenance_detail" pk="0" %}'.replace('0', maintenanceId),
success: function(data) {
$('#maintenance-details').html(data);
$('#viewMaintenanceModal').modal('show');
},
error: function() {
alert('Error loading maintenance details.');
}
});
}
function startMaintenance(maintenanceId) {
if (confirm('Start this maintenance task?')) {
$.ajax({
url: '{% url "inpatients:start_maintenance" pk="0" %}'.replace('0', maintenanceId),
method: 'POST',
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.success) {
location.reload();
} else {
alert('Error starting maintenance: ' + response.message);
}
},
error: function() {
alert('Error starting maintenance.');
}
});
}
}
function updateProgress(maintenanceId) {
const progress = prompt('Enter progress percentage (0-100):');
if (progress !== null && progress >= 0 && progress <= 100) {
$.ajax({
url: '{% url "inpatients:update_maintenance_progress" pk="0" %}'.replace('0', maintenanceId),
method: 'POST',
data: {
'progress': progress,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.success) {
location.reload();
} else {
alert('Error updating progress: ' + response.message);
}
},
error: function() {
alert('Error updating progress.');
}
});
}
}
function editMaintenance(maintenanceId) {
window.location.href = '{% url "inpatients:edit_maintenance" pk="0" %}'.replace('0', maintenanceId);
}
function exportMaintenanceReport() {
const params = new URLSearchParams(window.location.search);
params.append('export', 'excel');
window.location.href = '{% url "inpatients:maintenance_report" %}?' + params.toString();
}
</script>
{% endblock %}