update
This commit is contained in:
parent
f941a8e608
commit
99858b4075
@ -33,6 +33,7 @@ APPOINTMENT_TYPES = [
|
||||
'THERAPY', 'VACCINATION', 'SCREENING', 'EMERGENCY', 'TELEMEDICINE'
|
||||
]
|
||||
|
||||
|
||||
SAUDI_CHIEF_COMPLAINTS = [
|
||||
'Chest pain and shortness of breath',
|
||||
'Abdominal pain and nausea',
|
||||
|
||||
43
core_data.py
43
core_data.py
@ -64,12 +64,12 @@ ARABIC_FAMILY_NAMES = [
|
||||
]
|
||||
|
||||
|
||||
def create_saudi_tenants(count=20):
|
||||
def create_saudi_tenants(count=1):
|
||||
"""Create Saudi healthcare tenants"""
|
||||
tenants = []
|
||||
|
||||
for i in range(count):
|
||||
hospital_name = random.choice(SAUDI_HOSPITALS)
|
||||
hospital_name = 'King Faisal Specialist Hospital'
|
||||
city = random.choice(SAUDI_CITIES)
|
||||
province = random.choice(SAUDI_PROVINCES)
|
||||
|
||||
@ -112,6 +112,8 @@ def create_super_user():
|
||||
tenant1 = Tenant.objects.get(pk=1)
|
||||
User.objects.create_superuser(
|
||||
username="admin",
|
||||
first_name="Marwan",
|
||||
last_name="Alwali",
|
||||
email="marwan@tenhal.sa",
|
||||
password="Kfsh&rc9788",
|
||||
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',
|
||||
'message': 'Hospital operating hours have been adjusted for the holy month of Ramadan. Please check the updated schedule.',
|
||||
'type': 'schedule',
|
||||
'priority': 'high'
|
||||
'type': 'INFO',
|
||||
'priority': 'HIGH'
|
||||
},
|
||||
{
|
||||
'title': 'CBAHI Accreditation Renewal',
|
||||
'message': 'Annual CBAHI accreditation renewal is due. Please ensure all documentation is up to date.',
|
||||
'type': 'compliance',
|
||||
'priority': 'critical'
|
||||
'type': 'INFO',
|
||||
'priority': 'URGENT'
|
||||
},
|
||||
{
|
||||
'title': 'Ministry of Health Guidelines',
|
||||
'message': 'New MOH guidelines for patient data privacy have been published. Review required.',
|
||||
'type': 'policy',
|
||||
'priority': 'high'
|
||||
'type': 'UPDATE',
|
||||
'priority': 'HIGH'
|
||||
},
|
||||
{
|
||||
'title': 'Hajj Season Preparation',
|
||||
'message': 'Special preparations for Hajj season medical services are now in effect.',
|
||||
'type': 'seasonal',
|
||||
'priority': 'medium'
|
||||
'type': 'UPDATE',
|
||||
'priority': 'MEDIUM'
|
||||
},
|
||||
{
|
||||
'title': 'Arabic Language Interface',
|
||||
'message': 'The system now supports full Arabic language interface. Switch in your profile settings.',
|
||||
'type': 'feature',
|
||||
'priority': 'low'
|
||||
'type': 'FEATURE',
|
||||
'priority': 'LOW'
|
||||
}
|
||||
]
|
||||
|
||||
@ -333,7 +335,7 @@ def create_saudi_audit_logs(tenants, count_per_tenant=100):
|
||||
'vital_signs_recorded', 'medication_administered'
|
||||
]
|
||||
|
||||
risk_levels = ['low', 'medium', 'high', 'critical']
|
||||
risk_levels = AuditLogEntry.RISK_LEVEL_CHOICES
|
||||
|
||||
for tenant in tenants:
|
||||
for _ in range(count_per_tenant):
|
||||
@ -343,8 +345,8 @@ def create_saudi_audit_logs(tenants, count_per_tenant=100):
|
||||
tenant=tenant,
|
||||
log_id=uuid.uuid4(),
|
||||
event_type=event_type,
|
||||
event_category='clinical' if event_type in ['medical_record_access',
|
||||
'prescription_issued'] else 'administrative',
|
||||
event_category='CLINICAL_DATA' if event_type in ['medical_record_access',
|
||||
'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_role=random.choice(['doctor', 'nurse', 'pharmacist', 'admin', 'receptionist']),
|
||||
session_key=uuid.uuid4(),
|
||||
@ -379,10 +381,7 @@ def create_saudi_integration_logs(tenants, count_per_tenant=50):
|
||||
"""Create Saudi-specific integration logs"""
|
||||
integration_logs = []
|
||||
|
||||
saudi_integrations = [
|
||||
'MOH_HESN', 'NPHIES', 'Absher', 'SAUDI_POST', 'SAMA_PAYMENTS',
|
||||
'CBAHI_PORTAL', 'SFDA_PORTAL', 'ZATCA_INTEGRATION'
|
||||
]
|
||||
saudi_integrations = IntegrationLog.INTEGRATION_TYPE_CHOICES
|
||||
|
||||
message_types = [
|
||||
'patient_eligibility', 'claim_submission', 'payment_notification',
|
||||
@ -400,7 +399,7 @@ def create_saudi_integration_logs(tenants, count_per_tenant=50):
|
||||
integration_type=integration_type,
|
||||
direction=random.choice(['inbound', 'outbound']),
|
||||
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_id=f"MSG-{uuid.uuid4().hex[:12].upper()}",
|
||||
correlation_id=uuid.uuid4(),
|
||||
@ -414,7 +413,7 @@ def create_saudi_integration_logs(tenants, count_per_tenant=50):
|
||||
'response_code': '200',
|
||||
'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_message=None if random.choice([True, True, True, False]) else 'Authentication failed',
|
||||
processing_time_ms=random.randint(100, 5000),
|
||||
@ -433,7 +432,7 @@ def main():
|
||||
|
||||
# Create tenants
|
||||
print("\n1. Creating Saudi Healthcare Tenants...")
|
||||
tenants = create_saudi_tenants(15)
|
||||
tenants = create_saudi_tenants(1)
|
||||
|
||||
# Create Super user
|
||||
print("\n2. Creating Super User...")
|
||||
|
||||
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
Binary file not shown.
69
hr/models.py
69
hr/models.py
@ -17,7 +17,38 @@ class Employee(models.Model):
|
||||
"""
|
||||
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 = models.ForeignKey(
|
||||
'core.Tenant',
|
||||
@ -135,26 +166,14 @@ class Employee(models.Model):
|
||||
)
|
||||
gender = models.CharField(
|
||||
max_length=10,
|
||||
choices=[
|
||||
('MALE', 'Male'),
|
||||
('FEMALE', 'Female'),
|
||||
('OTHER', 'Other'),
|
||||
('UNKNOWN', 'Unknown'),
|
||||
],
|
||||
choices=GENDER_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Gender'
|
||||
)
|
||||
marital_status = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('SINGLE', 'Single'),
|
||||
('MARRIED', 'Married'),
|
||||
('DIVORCED', 'Divorced'),
|
||||
('WIDOWED', 'Widowed'),
|
||||
('SEPARATED', 'Separated'),
|
||||
('OTHER', 'Other'),
|
||||
],
|
||||
choices=MARITAL_STATUS_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Marital status'
|
||||
@ -175,28 +194,12 @@ class Employee(models.Model):
|
||||
)
|
||||
employment_type = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('FULL_TIME', 'Full Time'),
|
||||
('PART_TIME', 'Part Time'),
|
||||
('CONTRACT', 'Contract'),
|
||||
('TEMPORARY', 'Temporary'),
|
||||
('INTERN', 'Intern'),
|
||||
('VOLUNTEER', 'Volunteer'),
|
||||
('PER_DIEM', 'Per Diem'),
|
||||
('CONSULTANT', 'Consultant'),
|
||||
],
|
||||
choices=EMPLOYMENT_TYPE_CHOICES,
|
||||
help_text='Employment type'
|
||||
)
|
||||
employment_status = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('ACTIVE', 'Active'),
|
||||
('INACTIVE', 'Inactive'),
|
||||
('TERMINATED', 'Terminated'),
|
||||
('SUSPENDED', 'Suspended'),
|
||||
('LEAVE', 'On Leave'),
|
||||
('RETIRED', 'Retired'),
|
||||
],
|
||||
choices=EMPLOYMENT_STATUS_CHOICES,
|
||||
default='ACTIVE',
|
||||
help_text='Employment status'
|
||||
)
|
||||
|
||||
60
hr_data.py
60
hr_data.py
@ -199,7 +199,7 @@ def create_saudi_employees(tenants, departments, employees_per_tenant=150):
|
||||
|
||||
# Determine employment type and status
|
||||
employment_type = random.choices(
|
||||
['FULL_TIME', 'PART_TIME', 'CONTRACT', 'PER_DIEM'],
|
||||
['FULL_TIME', 'PART_TIME', 'CONTRACT', 'CONTRACT'],
|
||||
weights=[70, 15, 10, 5]
|
||||
)[0]
|
||||
|
||||
@ -355,40 +355,42 @@ def create_saudi_schedules(employees, schedules_per_employee=2):
|
||||
|
||||
schedule_patterns = {
|
||||
'DAY_SHIFT': {
|
||||
'monday': {'start': '07:00', 'end': '15:00'},
|
||||
'tuesday': {'start': '07:00', 'end': '15:00'},
|
||||
'wednesday': {'start': '07:00', 'end': '15:00'},
|
||||
'thursday': {'start': '07:00', 'end': '15:00'},
|
||||
'friday': {'start': '07:00', 'end': '12:00'}, # Half day Friday
|
||||
'saturday': {'start': '07:00', 'end': '15: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'
|
||||
'sunday': {'start': '07:00', 'end': '19:00'},
|
||||
'monday': {'start': '07:00', 'end': '19:00'},
|
||||
'tuesday': {'start': '07:00', 'end': '19:00'},
|
||||
'wednesday': {'start': '07:00', 'end': '19:00'},
|
||||
'thursday': {'start': '07:00', 'end': '19:00'},
|
||||
'friday': {'start': '07:00', 'end': '19:00'},
|
||||
'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'
|
||||
# },
|
||||
'NIGHT_SHIFT': {
|
||||
'monday': {'start': '23:00', 'end': '07:00'},
|
||||
'tuesday': {'start': '23:00', 'end': '07:00'},
|
||||
'wednesday': {'start': '23:00', 'end': '07:00'},
|
||||
'thursday': {'start': '23:00', 'end': '07:00'},
|
||||
'friday': 'off',
|
||||
'saturday': {'start': '23:00', 'end': '07:00'},
|
||||
'sunday': {'start': '23:00', 'end': '07:00'}
|
||||
'sunday': {'start': '19:00', 'end': '07:00'},
|
||||
'monday': {'start': '19:00', 'end': '07:00'},
|
||||
'tuesday': {'start': '19:00', 'end': '07:00'},
|
||||
'wednesday': {'start': '19:00', 'end': '07:00'},
|
||||
'thursday': {'start': '19:00', 'end': '07:00'},
|
||||
'friday': {'start': '19:00', 'end': '07:00'},
|
||||
'saturday': {'start': '19:00', 'end': '07:00'},
|
||||
|
||||
},
|
||||
'ADMIN_HOURS': {
|
||||
'sunday': {'start': '08:00', 'end': '17:00'},
|
||||
'monday': {'start': '08:00', 'end': '17:00'},
|
||||
'tuesday': {'start': '08:00', 'end': '17:00'},
|
||||
'wednesday': {'start': '08:00', 'end': '17:00'},
|
||||
'thursday': {'start': '08:00', 'end': '17:00'},
|
||||
'friday': {'start': '08:00', 'end': '12:00'},
|
||||
'thursday': {'start': '08:00', 'end': '16:00'},
|
||||
'friday': 'off',
|
||||
'saturday': 'off',
|
||||
'sunday': 'off'
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,7 +465,7 @@ def create_schedule_assignments(schedules, days_back=30):
|
||||
|
||||
shift_types = {
|
||||
'DAY_SHIFT': 'DAY',
|
||||
'EVENING_SHIFT': 'EVENING',
|
||||
# 'EVENING_SHIFT': 'EVENING',
|
||||
'NIGHT_SHIFT': 'NIGHT',
|
||||
'ADMIN_HOURS': 'DAY'
|
||||
}
|
||||
@ -491,7 +493,7 @@ def create_schedule_assignments(schedules, days_back=30):
|
||||
pattern_key = None
|
||||
for pattern_name, pattern_data in [
|
||||
('DAY_SHIFT', schedule.schedule_pattern),
|
||||
('EVENING_SHIFT', schedule.schedule_pattern),
|
||||
# ('EVENING_SHIFT', schedule.schedule_pattern),
|
||||
('NIGHT_SHIFT', schedule.schedule_pattern),
|
||||
('ADMIN_HOURS', schedule.schedule_pattern)
|
||||
]:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -15,7 +15,7 @@ class WardAdmin(admin.ModelAdmin):
|
||||
Admin interface for Ward model.
|
||||
"""
|
||||
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'
|
||||
]
|
||||
list_filter = [
|
||||
@ -70,9 +70,10 @@ class WardAdmin(admin.ModelAdmin):
|
||||
color = 'orange'
|
||||
else:
|
||||
color = 'green'
|
||||
filled_beds = obj.total_beds - obj.available_beds
|
||||
return format_html(
|
||||
'<span style="color: {};">{:.1f}% ({}/{})</span>',
|
||||
color, rate, obj.total_beds - obj.available_beds, obj.total_beds
|
||||
'<span style="color: {};">{}% ({}/{})</span>',
|
||||
color, rate, filled_beds, obj.total_beds
|
||||
)
|
||||
occupancy_display.short_description = 'Occupancy'
|
||||
|
||||
|
||||
18
inpatients/migrations/0002_alter_ward_ward_id.py
Normal file
18
inpatients/migrations/0002_alter_ward_ward_id.py
Normal 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),
|
||||
),
|
||||
]
|
||||
31
inpatients/migrations/0003_alter_bed_bed_position.py
Normal file
31
inpatients/migrations/0003_alter_bed_bed_position.py
Normal 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,
|
||||
),
|
||||
),
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
@ -11,12 +11,54 @@ from django.conf import settings
|
||||
from datetime import timedelta, datetime, time
|
||||
import json
|
||||
|
||||
from kombu.transport.redis import PRIORITY_STEPS
|
||||
|
||||
|
||||
class Ward(models.Model):
|
||||
"""
|
||||
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 = models.ForeignKey(
|
||||
'core.Tenant',
|
||||
@ -27,7 +69,7 @@ class Ward(models.Model):
|
||||
|
||||
# Ward Information
|
||||
ward_id = models.CharField(
|
||||
max_length=20,
|
||||
max_length=50,
|
||||
help_text='Unique ward identifier'
|
||||
)
|
||||
name = models.CharField(
|
||||
@ -43,47 +85,13 @@ class Ward(models.Model):
|
||||
# Ward Type and Specialty
|
||||
ward_type = models.CharField(
|
||||
max_length=50,
|
||||
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'),
|
||||
],
|
||||
choices=WARD_TYPE_CHOICES,
|
||||
help_text='Type of ward'
|
||||
)
|
||||
|
||||
specialty = models.CharField(
|
||||
max_length=100,
|
||||
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'),
|
||||
],
|
||||
choices=SPECIALITY_CHOICES,
|
||||
help_text='Medical specialty'
|
||||
)
|
||||
|
||||
@ -175,11 +183,7 @@ class Ward(models.Model):
|
||||
)
|
||||
gender_restrictions = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('NONE', 'No Restrictions'),
|
||||
('MALE_ONLY', 'Male Only'),
|
||||
('FEMALE_ONLY', 'Female Only'),
|
||||
],
|
||||
choices=GENDER_RESTRICTIONS_CHOICES,
|
||||
default='NONE',
|
||||
help_text='Gender restrictions'
|
||||
)
|
||||
@ -238,7 +242,7 @@ class Ward(models.Model):
|
||||
unique_together = ['tenant', 'ward_id']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.ward_id})"
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def occupancy_rate(self):
|
||||
@ -246,9 +250,10 @@ class Ward(models.Model):
|
||||
Calculate current occupancy rate.
|
||||
"""
|
||||
occupied_beds = self.beds.filter(status='OCCUPIED').count()
|
||||
if self.total_beds > 0:
|
||||
return (occupied_beds / self.total_beds) * 100
|
||||
return 0
|
||||
if self.total_beds == 0:
|
||||
return 0.0
|
||||
return 100 * (occupied_beds / self.total_beds)
|
||||
|
||||
|
||||
@property
|
||||
def available_beds(self):
|
||||
@ -257,6 +262,10 @@ class Ward(models.Model):
|
||||
"""
|
||||
return self.beds.filter(status='AVAILABLE').count()
|
||||
|
||||
@property
|
||||
def occupied_beds(self):
|
||||
return self.beds.filter(status='OCCUPIED').count()
|
||||
|
||||
|
||||
class Bed(models.Model):
|
||||
"""
|
||||
@ -299,10 +308,12 @@ class Bed(models.Model):
|
||||
('TERMINAL', 'Terminal Cleaning'),
|
||||
]
|
||||
BED_POSITION_CHOICES = [
|
||||
('WINDOW', 'Window Side'),
|
||||
('DOOR', 'Door Side'),
|
||||
('CENTER', 'Center'),
|
||||
('CORNER', 'Corner'),
|
||||
('A', 'A'),
|
||||
('B', 'B'),
|
||||
('C', 'C'),
|
||||
('D', 'D'),
|
||||
('E', 'E'),
|
||||
('F', 'F'),
|
||||
]
|
||||
# Ward relationship
|
||||
ward = models.ForeignKey(
|
||||
@ -503,7 +514,69 @@ class Admission(models.Model):
|
||||
"""
|
||||
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 = models.ForeignKey(
|
||||
'core.Tenant',
|
||||
@ -534,33 +607,13 @@ class Admission(models.Model):
|
||||
)
|
||||
admission_type = models.CharField(
|
||||
max_length=30,
|
||||
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'),
|
||||
],
|
||||
choices=ADMISSION_TYPE_CHOICES,
|
||||
help_text='Type of admission'
|
||||
)
|
||||
|
||||
admission_source = models.CharField(
|
||||
max_length=30,
|
||||
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'),
|
||||
],
|
||||
choices=ADMISSION_SOURCE_CHOICES,
|
||||
help_text='Source of admission'
|
||||
)
|
||||
|
||||
@ -616,15 +669,7 @@ class Admission(models.Model):
|
||||
# Admission Status
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('PENDING', 'Pending Admission'),
|
||||
('ADMITTED', 'Admitted'),
|
||||
('TRANSFERRED', 'Transferred'),
|
||||
('DISCHARGED', 'Discharged'),
|
||||
('DECEASED', 'Deceased'),
|
||||
('LEFT_AMA', 'Left Against Medical Advice'),
|
||||
('CANCELLED', 'Cancelled'),
|
||||
],
|
||||
choices=STATUS_CHOICES,
|
||||
default='PENDING',
|
||||
help_text='Current admission status'
|
||||
)
|
||||
@ -632,12 +677,7 @@ class Admission(models.Model):
|
||||
# Priority and Acuity
|
||||
priority = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('ROUTINE', 'Routine'),
|
||||
('URGENT', 'Urgent'),
|
||||
('EMERGENT', 'Emergent'),
|
||||
('CRITICAL', 'Critical'),
|
||||
],
|
||||
choices=PRIORITY_CHOICES,
|
||||
default='ROUTINE',
|
||||
help_text='Admission priority'
|
||||
)
|
||||
@ -691,17 +731,7 @@ class Admission(models.Model):
|
||||
)
|
||||
discharge_disposition = models.CharField(
|
||||
max_length=30,
|
||||
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'),
|
||||
],
|
||||
choices=DISCHARGE_DISPOSITION_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Discharge disposition'
|
||||
@ -714,13 +744,7 @@ class Admission(models.Model):
|
||||
)
|
||||
isolation_type = models.CharField(
|
||||
max_length=30,
|
||||
choices=[
|
||||
('CONTACT', 'Contact Precautions'),
|
||||
('DROPLET', 'Droplet Precautions'),
|
||||
('AIRBORNE', 'Airborne Precautions'),
|
||||
('PROTECTIVE', 'Protective Isolation'),
|
||||
('STRICT', 'Strict Isolation'),
|
||||
],
|
||||
choices=ISOLATION_TYPE_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Type of isolation required'
|
||||
@ -747,14 +771,7 @@ class Admission(models.Model):
|
||||
# Code Status
|
||||
code_status = models.CharField(
|
||||
max_length=20,
|
||||
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'),
|
||||
],
|
||||
choices=CODE_STATUS_CHOICES,
|
||||
default='FULL_CODE',
|
||||
help_text='Code status'
|
||||
)
|
||||
@ -822,11 +839,50 @@ class Admission(models.Model):
|
||||
return self.status in ['PENDING', 'ADMITTED', 'TRANSFERRED']
|
||||
|
||||
|
||||
|
||||
|
||||
class Transfer(models.Model):
|
||||
"""
|
||||
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_id = models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
@ -852,13 +908,7 @@ class Transfer(models.Model):
|
||||
# Transfer Details
|
||||
transfer_type = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('WARD', 'Ward Transfer'),
|
||||
('BED', 'Bed Transfer'),
|
||||
('ROOM', 'Room Transfer'),
|
||||
('UNIT', 'Unit Transfer'),
|
||||
('FACILITY', 'Facility Transfer'),
|
||||
],
|
||||
choices=TRANSFER_TYPE_CHOICES,
|
||||
help_text='Type of transfer'
|
||||
)
|
||||
|
||||
@ -913,15 +963,7 @@ class Transfer(models.Model):
|
||||
# Transfer Status
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('REQUESTED', 'Requested'),
|
||||
('APPROVED', 'Approved'),
|
||||
('SCHEDULED', 'Scheduled'),
|
||||
('IN_PROGRESS', 'In Progress'),
|
||||
('COMPLETED', 'Completed'),
|
||||
('CANCELLED', 'Cancelled'),
|
||||
('DELAYED', 'Delayed'),
|
||||
],
|
||||
choices=STATUS_CHOICES,
|
||||
default='REQUESTED',
|
||||
help_text='Transfer status'
|
||||
)
|
||||
@ -932,12 +974,7 @@ class Transfer(models.Model):
|
||||
)
|
||||
priority = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('ROUTINE', 'Routine'),
|
||||
('URGENT', 'Urgent'),
|
||||
('EMERGENT', 'Emergent'),
|
||||
('STAT', 'STAT'),
|
||||
],
|
||||
choices=PRIORITY_CHOICES,
|
||||
default='ROUTINE',
|
||||
help_text='Transfer priority'
|
||||
)
|
||||
@ -969,14 +1006,7 @@ class Transfer(models.Model):
|
||||
# Transport Information
|
||||
transport_method = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('WHEELCHAIR', 'Wheelchair'),
|
||||
('STRETCHER', 'Stretcher'),
|
||||
('BED', 'Hospital Bed'),
|
||||
('AMBULATORY', 'Walking'),
|
||||
('AMBULANCE', 'Ambulance'),
|
||||
('OTHER', 'Other'),
|
||||
],
|
||||
choices=TRANSPORT_METHOD_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Method of transport'
|
||||
@ -1003,13 +1033,7 @@ class Transfer(models.Model):
|
||||
# Clinical Information
|
||||
patient_condition = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('STABLE', 'Stable'),
|
||||
('UNSTABLE', 'Unstable'),
|
||||
('CRITICAL', 'Critical'),
|
||||
('IMPROVING', 'Improving'),
|
||||
('DETERIORATING', 'Deteriorating'),
|
||||
],
|
||||
choices=PATIENT_CONDITION_CHOICES,
|
||||
help_text='Patient condition at time of transfer'
|
||||
)
|
||||
vital_signs = models.JSONField(
|
||||
@ -1083,7 +1107,37 @@ class DischargeSummary(models.Model):
|
||||
"""
|
||||
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 = models.OneToOneField(
|
||||
Admission,
|
||||
@ -1200,15 +1254,7 @@ class DischargeSummary(models.Model):
|
||||
# Discharge Disposition
|
||||
discharge_disposition = models.CharField(
|
||||
max_length=30,
|
||||
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'),
|
||||
],
|
||||
choices=DISCHARGE_DISPOSITION_CHOICES,
|
||||
help_text='Discharge disposition'
|
||||
)
|
||||
discharge_location = models.CharField(
|
||||
@ -1225,15 +1271,7 @@ class DischargeSummary(models.Model):
|
||||
)
|
||||
transportation_method = models.CharField(
|
||||
max_length=30,
|
||||
choices=[
|
||||
('PRIVATE', 'Private Vehicle'),
|
||||
('TAXI', 'Taxi'),
|
||||
('AMBULANCE', 'Ambulance'),
|
||||
('MEDICAL_TRANSPORT', 'Medical Transport'),
|
||||
('PUBLIC_TRANSPORT', 'Public Transportation'),
|
||||
('WALKING', 'Walking'),
|
||||
('OTHER', 'Other'),
|
||||
],
|
||||
choices=TRANSPORTATION_METHOD_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Method of transportation'
|
||||
@ -1264,12 +1302,7 @@ class DischargeSummary(models.Model):
|
||||
)
|
||||
patient_understanding = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('EXCELLENT', 'Excellent'),
|
||||
('GOOD', 'Good'),
|
||||
('FAIR', 'Fair'),
|
||||
('POOR', 'Poor'),
|
||||
],
|
||||
choices=PATIENT_UNDERSTANDING_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Patient understanding of instructions'
|
||||
@ -1296,12 +1329,7 @@ class DischargeSummary(models.Model):
|
||||
# Quality Measures
|
||||
readmission_risk = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('LOW', 'Low Risk'),
|
||||
('MODERATE', 'Moderate Risk'),
|
||||
('HIGH', 'High Risk'),
|
||||
('VERY_HIGH', 'Very High Risk'),
|
||||
],
|
||||
choices=READMISSION_RISK_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Risk of readmission'
|
||||
@ -1374,7 +1402,52 @@ class SurgerySchedule(models.Model):
|
||||
"""
|
||||
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 = models.ForeignKey(
|
||||
'core.Tenant',
|
||||
@ -1418,18 +1491,7 @@ class SurgerySchedule(models.Model):
|
||||
)
|
||||
surgery_type = models.CharField(
|
||||
max_length=30,
|
||||
choices=[
|
||||
('ELECTIVE', 'Elective'),
|
||||
('URGENT', 'Urgent'),
|
||||
('EMERGENT', 'Emergent'),
|
||||
('TRAUMA', 'Trauma'),
|
||||
('TRANSPLANT', 'Transplant'),
|
||||
('CARDIAC', 'Cardiac'),
|
||||
('NEUROSURGERY', 'Neurosurgery'),
|
||||
('ORTHOPEDIC', 'Orthopedic'),
|
||||
('GENERAL', 'General Surgery'),
|
||||
('OTHER', 'Other'),
|
||||
],
|
||||
choices=SURGERY_TYPE_CHOICES,
|
||||
help_text='Type of surgery'
|
||||
)
|
||||
|
||||
@ -1497,16 +1559,7 @@ class SurgerySchedule(models.Model):
|
||||
# Anesthesia Information
|
||||
anesthesia_type = models.CharField(
|
||||
max_length=30,
|
||||
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'),
|
||||
],
|
||||
choices=ANESTHESIA_TYPE_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Type of anesthesia'
|
||||
@ -1551,16 +1604,7 @@ class SurgerySchedule(models.Model):
|
||||
# Surgery Status
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('SCHEDULED', 'Scheduled'),
|
||||
('CONFIRMED', 'Confirmed'),
|
||||
('PREP', 'Pre-operative Prep'),
|
||||
('IN_PROGRESS', 'In Progress'),
|
||||
('COMPLETED', 'Completed'),
|
||||
('CANCELLED', 'Cancelled'),
|
||||
('POSTPONED', 'Postponed'),
|
||||
('DELAYED', 'Delayed'),
|
||||
],
|
||||
choices=STATUS_CHOICES,
|
||||
default='SCHEDULED',
|
||||
help_text='Surgery status'
|
||||
)
|
||||
@ -1602,14 +1646,7 @@ class SurgerySchedule(models.Model):
|
||||
# Recovery Information
|
||||
recovery_location = models.CharField(
|
||||
max_length=50,
|
||||
choices=[
|
||||
('PACU', 'Post-Anesthesia Care Unit'),
|
||||
('ICU', 'Intensive Care Unit'),
|
||||
('WARD', 'Regular Ward'),
|
||||
('SAME_DAY', 'Same Day Surgery'),
|
||||
('HOME', 'Home'),
|
||||
('OTHER', 'Other'),
|
||||
],
|
||||
choices=RECOVERY_LOCATION_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Post-operative recovery location'
|
||||
@ -1618,12 +1655,7 @@ class SurgerySchedule(models.Model):
|
||||
# Priority and Urgency
|
||||
priority = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('ROUTINE', 'Routine'),
|
||||
('URGENT', 'Urgent'),
|
||||
('EMERGENT', 'Emergent'),
|
||||
('STAT', 'STAT'),
|
||||
],
|
||||
choices=PRIORITY_CHOICES,
|
||||
default='ROUTINE',
|
||||
help_text='Surgery priority'
|
||||
)
|
||||
|
||||
@ -11,10 +11,12 @@ urlpatterns = [
|
||||
# Main views
|
||||
path('', views.InpatientDashboardView.as_view(), name='dashboard'),
|
||||
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('admissions/', views.AdmissionListView.as_view(), name='admission_list'),
|
||||
path('admissions/<int:pk>/', views.AdmissionDetailView.as_view(), name='admission_detail'),
|
||||
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('surgery/', views.SurgeryScheduleView.as_view(), name='surgery_schedule'),
|
||||
|
||||
@ -37,6 +39,7 @@ urlpatterns = [
|
||||
|
||||
# Actions
|
||||
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>/complete/', views.complete_transfer, name='complete_transfer'),
|
||||
path('bed/<int:bed_id>/status/', views.update_bed_status, name='update_bed_status'),
|
||||
|
||||
10
inpatients/utils.py
Normal file
10
inpatients/utils.py
Normal 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
|
||||
@ -1,7 +1,7 @@
|
||||
"""
|
||||
Views for inpatients app.
|
||||
"""
|
||||
|
||||
from django.conf.locale import te
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
@ -423,10 +423,10 @@ class BedDetailView(LoginRequiredMixin, DetailView):
|
||||
|
||||
# Get bed history - admissions that used this bed
|
||||
context['admission_history'] = Admission.objects.filter(
|
||||
Q(initial_bed=bed) | Q(current_bed=bed)
|
||||
Q(current_bed=bed) | Q(current_bed=bed)
|
||||
).select_related(
|
||||
'patient', 'admitting_physician'
|
||||
).order_by('-admitted_at')[:10]
|
||||
).order_by('-admission_datetime')[:10]
|
||||
|
||||
# Get maintenance history if available
|
||||
# This would require a model to track maintenance events
|
||||
@ -554,10 +554,29 @@ class BedManagementView(LoginRequiredMixin, ListView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
tenant = self.request.user.tenant
|
||||
wards = Ward.objects.filter(tenant=tenant, is_active=True)
|
||||
|
||||
occupancy_rates = []
|
||||
for ward in wards:
|
||||
try:
|
||||
occupancy_rates.append(ward.occupancy_rate)
|
||||
except Exception:
|
||||
pass # Optional: handle edge cases if any
|
||||
|
||||
average_occupancy = (
|
||||
sum(occupancy_rates) / len(occupancy_rates)
|
||||
if occupancy_rates else 0
|
||||
)
|
||||
context.update({
|
||||
'wards': Ward.objects.filter(tenant=self.request.user.tenant, is_active=True),
|
||||
'bed_statuses': Bed._meta.get_field('status').choices,
|
||||
'bed_types': Bed._meta.get_field('bed_type').choices,
|
||||
'wards': Ward.objects.filter(tenant=tenant, is_active=True),
|
||||
'bed_statuses': Bed.STATUS_CHOICES,
|
||||
'bed_types': Bed.BED_TYPE_CHOICES,
|
||||
'total_beds': Bed.objects.filter(ward__tenant=tenant).count(),
|
||||
'available_beds': Bed.objects.filter(ward__tenant=tenant, status='AVAILABLE').count(),
|
||||
'occupied_beds': Bed.objects.filter(ward__tenant=tenant, status='OCCUPIED').count(),
|
||||
'maintenance_beds': Bed.objects.filter(ward__tenant=tenant, status='MAINTENANCE').count(),
|
||||
'occupancy_rate': average_occupancy,
|
||||
})
|
||||
return context
|
||||
|
||||
@ -607,8 +626,8 @@ class AdmissionListView(LoginRequiredMixin, ListView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context.update({
|
||||
'admission_statuses': Admission._meta.get_field('status').choices,
|
||||
'admission_types': Admission._meta.get_field('admission_type').choices,
|
||||
'admission_statuses': Admission.STATUS_CHOICES,
|
||||
'admission_types': Admission.ADMISSION_TYPE_CHOICES,
|
||||
'wards': Ward.objects.filter(tenant=self.request.user.tenant, is_active=True),
|
||||
})
|
||||
return context
|
||||
@ -1540,62 +1559,62 @@ def maintenance_bed(request, pk):
|
||||
# return context
|
||||
#
|
||||
#
|
||||
# class WardDetailView(LoginRequiredMixin, DetailView):
|
||||
# """
|
||||
# Detail view for a ward.
|
||||
# """
|
||||
# model = Ward
|
||||
# template_name = 'inpatients/ward_detail.html'
|
||||
# context_object_name = 'ward'
|
||||
#
|
||||
# def get_queryset(self):
|
||||
# """Filter wards by tenant."""
|
||||
# return Ward.objects.filter(
|
||||
# tenant=self.request.user.tenant
|
||||
# ).select_related('nurse_manager')
|
||||
#
|
||||
# def get_context_data(self, **kwargs):
|
||||
# context = super().get_context_data(**kwargs)
|
||||
# ward = self.get_object()
|
||||
#
|
||||
# # Get beds for this ward with patient information
|
||||
# context['beds'] = Bed.objects.filter(
|
||||
# ward=ward
|
||||
# ).select_related(
|
||||
# 'current_patient', 'current_admission'
|
||||
# ).order_by('room_number', 'bed_number')
|
||||
#
|
||||
# # Group beds by room for display
|
||||
# rooms = {}
|
||||
# for bed in context['beds']:
|
||||
# room_num = bed.room_number
|
||||
# if room_num not in rooms:
|
||||
# rooms[room_num] = []
|
||||
# rooms[room_num].append(bed)
|
||||
# context['rooms'] = rooms
|
||||
#
|
||||
# # Get ward statistics
|
||||
# context['total_beds'] = context['beds'].count()
|
||||
# context['available_beds'] = context['beds'].filter(status='AVAILABLE').count()
|
||||
# context['occupied_beds'] = context['beds'].filter(status='OCCUPIED').count()
|
||||
# context['maintenance_beds'] = context['beds'].filter(
|
||||
# status__in=['MAINTENANCE', 'OUT_OF_ORDER', 'CLEANING']
|
||||
# ).count()
|
||||
#
|
||||
# if context['total_beds'] > 0:
|
||||
# context['occupancy_rate'] = (context['occupied_beds'] / context['total_beds']) * 100
|
||||
# else:
|
||||
# context['occupancy_rate'] = 0
|
||||
#
|
||||
# # Get recent admissions to this ward
|
||||
# context['recent_admissions'] = Admission.objects.filter(
|
||||
# Q(initial_ward=ward) | Q(current_bed__ward=ward),
|
||||
# status__in=['ADMITTED', 'READY_FOR_DISCHARGE']
|
||||
# ).select_related(
|
||||
# 'patient', 'admitting_physician'
|
||||
# ).order_by('-admitted_at')[:10]
|
||||
#
|
||||
# return context
|
||||
class WardDetailView(LoginRequiredMixin, DetailView):
|
||||
"""
|
||||
Detail view for a ward.
|
||||
"""
|
||||
model = Ward
|
||||
template_name = 'inpatients/ward_detail.html'
|
||||
context_object_name = 'ward'
|
||||
|
||||
def get_queryset(self):
|
||||
"""Filter wards by tenant."""
|
||||
return Ward.objects.filter(
|
||||
tenant=self.request.user.tenant
|
||||
).select_related('nurse_manager')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
ward = self.get_object()
|
||||
|
||||
# Get beds for this ward with patient information
|
||||
context['beds'] = Bed.objects.filter(
|
||||
ward=ward
|
||||
).select_related(
|
||||
'current_patient', 'current_admission'
|
||||
).order_by('room_number', 'bed_number')
|
||||
|
||||
# Group beds by room for display
|
||||
rooms = {}
|
||||
for bed in context['beds']:
|
||||
room_num = bed.room_number
|
||||
if room_num not in rooms:
|
||||
rooms[room_num] = []
|
||||
rooms[room_num].append(bed)
|
||||
context['rooms'] = rooms
|
||||
|
||||
# Get ward statistics
|
||||
context['total_beds'] = context['beds'].count()
|
||||
context['available_beds'] = context['beds'].filter(status='AVAILABLE').count()
|
||||
context['occupied_beds'] = context['beds'].filter(status='OCCUPIED').count()
|
||||
context['maintenance_beds'] = context['beds'].filter(
|
||||
status__in=['MAINTENANCE', 'OUT_OF_ORDER', 'CLEANING']
|
||||
).count()
|
||||
|
||||
if context['total_beds'] > 0:
|
||||
context['occupancy_rate'] = (context['occupied_beds'] / context['total_beds']) * 100
|
||||
else:
|
||||
context['occupancy_rate'] = 0
|
||||
|
||||
# Get recent admissions to this ward
|
||||
context['recent_admissions'] = Admission.objects.filter(
|
||||
Q(initial_ward=ward) | Q(current_bed__ward=ward),
|
||||
status__in=['ADMITTED', 'READY_FOR_DISCHARGE']
|
||||
).select_related(
|
||||
'patient', 'admitting_physician'
|
||||
).order_by('-admitted_at')[:10]
|
||||
|
||||
return context
|
||||
#
|
||||
#
|
||||
# class WardCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||||
@ -2069,96 +2088,99 @@ def maintenance_bed(request, pk):
|
||||
# return reverse('inpatients:admission_detail', kwargs={'pk': self.object.pk})
|
||||
#
|
||||
#
|
||||
# class AdmissionUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||||
# """
|
||||
# Update view for an admission.
|
||||
# """
|
||||
# model = Admission
|
||||
# form_class = AdmissionForm
|
||||
# template_name = 'inpatients/admission_form.html'
|
||||
# permission_required = 'inpatients.change_admission'
|
||||
#
|
||||
# def get_queryset(self):
|
||||
# """Filter admissions by tenant."""
|
||||
# return Admission.objects.filter(tenant=self.request.user.tenant)
|
||||
#
|
||||
# def get_form_kwargs(self):
|
||||
# kwargs = super().get_form_kwargs()
|
||||
# kwargs['user'] = self.request.user
|
||||
# return kwargs
|
||||
#
|
||||
# def form_valid(self, form):
|
||||
# # Check if status is changing to ADMITTED
|
||||
# old_status = self.get_object().status
|
||||
# new_status = form.instance.status
|
||||
#
|
||||
# response = super().form_valid(form)
|
||||
#
|
||||
# # If changing to ADMITTED, assign the bed
|
||||
# if old_status != 'ADMITTED' and new_status == 'ADMITTED' and form.instance.initial_bed:
|
||||
# form.instance.initial_bed.assign_patient(form.instance.patient, form.instance)
|
||||
# messages.success(self.request, _('Patient admitted and assigned to bed successfully'))
|
||||
# else:
|
||||
# messages.success(self.request, _('Admission updated successfully'))
|
||||
#
|
||||
# return response
|
||||
#
|
||||
# def get_success_url(self):
|
||||
# return reverse('inpatients:admission_detail', kwargs={'pk': self.object.pk})
|
||||
#
|
||||
#
|
||||
# @login_required
|
||||
class AdmissionUpdateView(LoginRequiredMixin, UpdateView):
|
||||
"""
|
||||
Update view for an admission.
|
||||
"""
|
||||
model = Admission
|
||||
form_class = AdmissionForm
|
||||
template_name = 'inpatients/admissions/admission_form.html'
|
||||
permission_required = 'inpatients.change_admission'
|
||||
|
||||
def get_queryset(self):
|
||||
"""Filter admissions by tenant."""
|
||||
return Admission.objects.filter(tenant=self.request.user.tenant)
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs['user'] = self.request.user
|
||||
return kwargs
|
||||
|
||||
def form_valid(self, form):
|
||||
# Check if status is changing to ADMITTED
|
||||
old_status = self.get_object().status
|
||||
new_status = form.instance.status
|
||||
|
||||
response = super().form_valid(form)
|
||||
|
||||
# If changing to ADMITTED, assign the bed
|
||||
if old_status != 'ADMITTED' and new_status == 'ADMITTED' and form.instance.initial_bed:
|
||||
form.instance.initial_bed.assign_patient(form.instance.patient, form.instance)
|
||||
messages.success(self.request, _('Patient admitted and assigned to bed successfully'))
|
||||
else:
|
||||
messages.success(self.request, _('Admission updated successfully'))
|
||||
|
||||
return response
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('inpatients:admission_detail', kwargs={'pk': self.object.pk})
|
||||
|
||||
|
||||
@login_required
|
||||
# @permission_required('inpatients.change_admission')
|
||||
# def discharge_patient(request, pk):
|
||||
# """
|
||||
# View to discharge a patient.
|
||||
# """
|
||||
# admission = get_object_or_404(
|
||||
# Admission,
|
||||
# pk=pk,
|
||||
# tenant=request.user.tenant
|
||||
# )
|
||||
#
|
||||
# # Only admitted patients can be discharged
|
||||
# if admission.status not in ['ADMITTED', 'READY_FOR_DISCHARGE']:
|
||||
# messages.error(request, _('Only admitted patients or patients ready for discharge can be discharged'))
|
||||
# return redirect('inpatients:admission_detail', pk=admission.pk)
|
||||
#
|
||||
# if request.method == 'POST':
|
||||
# summary_form = DischargeSummaryForm(
|
||||
# request.POST,
|
||||
# user=request.user,
|
||||
# admission=admission
|
||||
# )
|
||||
#
|
||||
# if summary_form.is_valid():
|
||||
# summary = summary_form.save(commit=False)
|
||||
# summary.patient = admission.patient
|
||||
# summary.created_by = request.user
|
||||
# summary.save()
|
||||
#
|
||||
# # Link summary to admission and discharge
|
||||
# admission.discharge_summary = summary
|
||||
# admission.discharge(request.user)
|
||||
#
|
||||
# messages.success(request, _('Patient discharged successfully'))
|
||||
# return redirect('inpatients:admission_detail', pk=admission.pk)
|
||||
# else:
|
||||
# initial = {
|
||||
# 'patient': admission.patient,
|
||||
# 'discharge_diagnosis': admission.admitting_diagnosis,
|
||||
# 'doctor_name': request.user.get_full_name() if request.user.role in ['DOCTOR', 'SPECIALIST'] else ''
|
||||
# }
|
||||
# summary_form = DischargeSummaryForm(
|
||||
# initial=initial,
|
||||
# user=request.user,
|
||||
# admission=admission
|
||||
# )
|
||||
#
|
||||
# return render(request, 'inpatients/discharge_form.html', {
|
||||
# 'admission': admission,
|
||||
# 'form': summary_form
|
||||
# })
|
||||
def discharge_patient(request, pk):
|
||||
print("function is right")
|
||||
print(pk)
|
||||
"""
|
||||
View to discharge a patient.
|
||||
"""
|
||||
admission = get_object_or_404(
|
||||
Admission,
|
||||
pk=pk,
|
||||
tenant=request.user.tenant
|
||||
)
|
||||
print(admission)
|
||||
|
||||
# Only admitted patients can be discharged
|
||||
if admission.status not in ['ADMITTED', 'READY_FOR_DISCHARGE']:
|
||||
messages.error(request, _('Only admitted patients or patients ready for discharge can be discharged'))
|
||||
return redirect('inpatients:admission_detail', pk=admission.pk)
|
||||
|
||||
if request.method == 'POST':
|
||||
summary_form = DischargeSummaryForm(
|
||||
request.POST,
|
||||
user=request.user,
|
||||
admission=admission
|
||||
)
|
||||
|
||||
if summary_form.is_valid():
|
||||
summary = summary_form.save(commit=False)
|
||||
summary.patient = admission.patient
|
||||
summary.created_by = request.user
|
||||
summary.save()
|
||||
|
||||
# Link summary to admission and discharge
|
||||
admission.discharge_summary = summary
|
||||
# admission.status = 'DISCHARGED'
|
||||
|
||||
messages.success(request, _('Patient discharged successfully'))
|
||||
return redirect('inpatients:admission_detail', pk=admission.pk)
|
||||
else:
|
||||
initial = {
|
||||
'patient': admission.patient,
|
||||
'discharge_diagnosis': admission.admitting_diagnosis,
|
||||
'doctor_name': request.user.get_full_name() if request.user.role in ['DOCTOR', 'SPECIALIST'] else ''
|
||||
}
|
||||
summary_form = DischargeSummaryForm(
|
||||
initial=initial,
|
||||
user=request.user,
|
||||
admission=admission
|
||||
)
|
||||
|
||||
return render(request, 'inpatients/discharges/discharge_form.html', {
|
||||
'admission': admission,
|
||||
'form': summary_form
|
||||
})
|
||||
#
|
||||
#
|
||||
# @login_required
|
||||
@ -2955,33 +2977,36 @@ def maintenance_bed(request, pk):
|
||||
# return redirect('inpatients:bed_detail', pk=bed.pk)
|
||||
#
|
||||
#
|
||||
# @login_required
|
||||
@login_required
|
||||
# @permission_required('inpatients.change_bed')
|
||||
# def maintenance_bed(request, pk):
|
||||
# """
|
||||
# Mark a bed for maintenance.
|
||||
# """
|
||||
# bed = get_object_or_404(
|
||||
# Bed,
|
||||
# pk=pk,
|
||||
# ward__tenant=request.user.tenant
|
||||
# )
|
||||
#
|
||||
# # Only available beds can be marked for maintenance
|
||||
# if bed.status != 'AVAILABLE':
|
||||
# messages.error(request, _('Only available beds can be marked for maintenance'))
|
||||
# return redirect('inpatients:bed_detail', pk=bed.pk)
|
||||
#
|
||||
# if request.method == 'POST':
|
||||
# notes = request.POST.get('notes')
|
||||
#
|
||||
# bed.mark_maintenance(notes)
|
||||
# messages.success(request, _('Bed marked for maintenance successfully'))
|
||||
# return redirect('inpatients:bed_detail', pk=bed.pk)
|
||||
#
|
||||
# return render(request, 'inpatients/maintenance_bed.html', {
|
||||
# 'bed': bed
|
||||
# })
|
||||
def maintenance_bed(request, pk):
|
||||
"""
|
||||
Mark a bed for maintenance.
|
||||
"""
|
||||
bed = get_object_or_404(
|
||||
Bed,
|
||||
pk=pk,
|
||||
ward__tenant=request.user.tenant
|
||||
)
|
||||
|
||||
# Only available beds can be marked for maintenance
|
||||
if bed.status != 'AVAILABLE':
|
||||
messages.error(request, _('Only available beds can be marked for maintenance'))
|
||||
return redirect('inpatients:bed_detail', pk=bed.pk)
|
||||
|
||||
if request.method == 'POST':
|
||||
notes = request.POST.get('notes')
|
||||
|
||||
# bed.mark_maintenance(notes)
|
||||
bed.status = 'MAINTENANCE'
|
||||
bed.notes = notes
|
||||
bed.save()
|
||||
messages.success(request, _('Bed marked for maintenance successfully'))
|
||||
return redirect('inpatients:bed_detail', pk=bed.pk)
|
||||
|
||||
return render(request, 'inpatients/maintenance_bed.html', {
|
||||
'bed': bed
|
||||
})
|
||||
#
|
||||
#
|
||||
# @login_required
|
||||
|
||||
@ -24,13 +24,10 @@ SAUDI_WARD_NAMES = [
|
||||
'Orthopedic Ward', 'Maternity Ward', 'Pediatric Ward', 'Oncology Ward'
|
||||
]
|
||||
|
||||
SAUDI_SPECIALTIES = [
|
||||
'Internal Medicine', 'Cardiology', 'Cardiac Surgery', 'Orthopedics',
|
||||
'General Surgery', 'Neurology', 'Neurosurgery', 'Oncology',
|
||||
'Pediatrics', 'Obstetrics and Gynecology', 'Emergency Medicine',
|
||||
'Intensive Care', 'Anesthesiology', 'Psychiatry', 'Dermatology',
|
||||
'Ophthalmology', 'ENT', 'Urology', 'Nephrology', 'Endocrinology'
|
||||
]
|
||||
SAUDI_SPECIALTIES = ['GENERAL_MEDICINE','SURGERY','CARDIOLOGY','NEUROLOGY','ONCOLOGY',
|
||||
'PEDIATRICS','OBSTETRICS','GYNECOLOGY','ORTHOPEDICS','PSYCHIATRY',
|
||||
'EMERGENCY','CRITICAL_CARE','REHABILITATION',
|
||||
]
|
||||
|
||||
SAUDI_BUILDINGS = [
|
||||
'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"""
|
||||
wards = []
|
||||
|
||||
ward_types = [
|
||||
('medical', 'Medical Ward'),
|
||||
('surgical', 'Surgical Ward'),
|
||||
('icu', 'Intensive Care Unit'),
|
||||
('pediatric', 'Pediatric Ward'),
|
||||
('maternity', 'Maternity Ward'),
|
||||
('emergency', 'Emergency Ward'),
|
||||
('psychiatric', 'Psychiatric Ward'),
|
||||
('rehabilitation', 'Rehabilitation Ward')
|
||||
]
|
||||
ward_types =['GENERAL','SURGICAL','ICU','CCU','NICU','PICU','EMERGENCY','MATERNITY','PEDIATRIC',
|
||||
'ONCOLOGY','CARDIAC','ORTHOPEDIC','NEUROLOGY','PSYCHIATRY','REHABILITATION',
|
||||
'ISOLATION','STEP_DOWN',
|
||||
]
|
||||
|
||||
for tenant in tenants:
|
||||
for i in range(wards_per_tenant):
|
||||
@ -123,7 +114,7 @@ def create_saudi_wards(tenants, wards_per_tenant=12):
|
||||
ward_id=uuid.uuid4(),
|
||||
name=ward_name,
|
||||
description=f"Specialized {specialty.lower()} ward providing comprehensive inpatient care",
|
||||
ward_type=ward_type[0],
|
||||
ward_type=ward_type,
|
||||
specialty=specialty,
|
||||
total_beds=total_beds,
|
||||
private_rooms=private_rooms,
|
||||
@ -176,23 +167,15 @@ def create_saudi_beds(wards):
|
||||
beds = []
|
||||
|
||||
bed_types = [
|
||||
('standard', 'Standard Hospital Bed'),
|
||||
('electric', 'Electric Adjustable Bed'),
|
||||
('icu', 'ICU Bed'),
|
||||
('bariatric', 'Bariatric Bed'),
|
||||
('pediatric', 'Pediatric Bed')
|
||||
'STANDARD','ICU','CARDIAC','ISOLATION','BARIATRIC','PEDIATRIC',
|
||||
'NEONATAL','MATERNITY','PSYCHIATRIC','STRETCHER','RECLINER'
|
||||
]
|
||||
|
||||
room_types = [
|
||||
('private', 'Private Room'),
|
||||
('semi_private', 'Semi-Private Room'),
|
||||
('shared', 'Shared Room'),
|
||||
('isolation', 'Isolation Room')
|
||||
]
|
||||
room_types = ['PRIVATE','SEMI_PRIVATE','SHARED','ICU','ISOLATION']
|
||||
|
||||
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 bed_num in range(1, ward.total_beds + 1):
|
||||
@ -213,13 +196,13 @@ def create_saudi_beds(wards):
|
||||
ward=ward,
|
||||
bed_number=bed_number,
|
||||
room_number=room_number,
|
||||
bed_type=bed_type[0],
|
||||
room_type=room_type[0],
|
||||
bed_type=bed_type,
|
||||
room_type=room_type,
|
||||
status=status,
|
||||
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(
|
||||
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=[
|
||||
'Cardiac Monitor', 'IV Pole', 'Oxygen Outlet', 'Suction Outlet',
|
||||
'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"""
|
||||
admissions = []
|
||||
|
||||
admission_types = [
|
||||
('emergency', 'Emergency Admission'),
|
||||
('elective', 'Elective Admission'),
|
||||
('urgent', 'Urgent Admission'),
|
||||
('transfer', 'Transfer from Another Facility')
|
||||
]
|
||||
admission_types = ['EMERGENCY','ELECTIVE','URGENT','OBSERVATION','DAY_SURGERY',
|
||||
'TRANSFER','READMISSION','DIRECT',
|
||||
]
|
||||
|
||||
admission_sources = [
|
||||
('emergency_department', 'Emergency Department'),
|
||||
('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')
|
||||
]
|
||||
admission_sources = ['EMERGENCY','OUTPATIENT','PHYSICIAN_OFFICE','TRANSFER',
|
||||
'NURSING_HOME','HOME','AMBULATORY_SURGERY'
|
||||
]
|
||||
|
||||
statuses = ['active', 'discharged', 'transferred']
|
||||
priorities = ['routine', 'urgent', 'emergent', 'critical']
|
||||
statuses = ['PENDING','ADMITTED','TRANSFERRED','DISCHARGED','DECEASED','LEFT_AMA','CANCELLED',]
|
||||
priorities = ['ROUTINE','URGENT','EMERGENT','CRITICAL']
|
||||
acuity_levels = ['1', '2', '3', '4', '5'] # 1 = highest acuity
|
||||
|
||||
isolation_types = [
|
||||
'contact', 'droplet', 'airborne', 'protective', 'contact_plus',
|
||||
'strict_isolation', 'reverse_isolation'
|
||||
]
|
||||
isolation_types = ['CONTACT','DROPLET','AIRBORNE','PROTECTIVE','STRICT',]
|
||||
|
||||
code_statuses = [
|
||||
'full_code', 'dnr', 'dni', 'comfort_care', 'limited_intervention'
|
||||
]
|
||||
code_statuses = ['FULL_CODE','DNR','DNI','DNR_DNI','COMFORT_CARE','LIMITED',]
|
||||
|
||||
for tenant in tenants:
|
||||
# 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(),
|
||||
patient=patient,
|
||||
admission_datetime=admission_datetime,
|
||||
admission_type=random.choice(admission_types)[0],
|
||||
admission_source=random.choice(admission_sources)[0],
|
||||
admission_type=random.choice(admission_types),
|
||||
admission_source=random.choice(admission_sources),
|
||||
chief_complaint=random.choice([
|
||||
'Chest pain', 'Shortness of breath', 'Abdominal pain',
|
||||
'Fever and chills', 'Weakness and fatigue', 'Nausea and vomiting',
|
||||
@ -375,17 +345,11 @@ def create_saudi_transfers(admissions):
|
||||
"""Create transfer records for Saudi patients"""
|
||||
transfers = []
|
||||
|
||||
transfer_types = [
|
||||
('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_types = ['WARD','BED','ROOM','UNIT','FACILITY',]
|
||||
|
||||
transfer_statuses = ['requested', 'approved', 'in_progress', 'completed', 'cancelled']
|
||||
transport_methods = ['bed', 'wheelchair', 'stretcher', 'walking', 'ambulance']
|
||||
priorities = ['routine', 'urgent', 'emergent', 'stat']
|
||||
transfer_statuses = ['REQUESTED','APPROVED','SCHEDULED','IN_PROGRESS','COMPLETED','CANCELLED','DELAYED',]
|
||||
transport_methods = ['WHEELCHAIR','STRETCHER','BED','AMBULATORY','AMBULANCE','OTHER',]
|
||||
priorities = ['ROUTINE','URGENT','EMERGENT','STAT',]
|
||||
|
||||
# Create transfers for 30% of admissions
|
||||
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
|
||||
available_beds = Bed.objects.filter(
|
||||
ward__tenant=admission.tenant,
|
||||
status='available'
|
||||
status='AVAILABLE'
|
||||
).exclude(ward=admission.current_ward)
|
||||
|
||||
if not available_beds:
|
||||
@ -425,10 +389,10 @@ def create_saudi_transfers(admissions):
|
||||
scheduled_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))
|
||||
|
||||
if status in ['in_progress', 'completed']:
|
||||
if status in ['IN_PROGRESS', 'COMPLETED']:
|
||||
actual_datetime = scheduled_datetime + timedelta(minutes=random.randint(-30, 60))
|
||||
|
||||
# Get transport team members
|
||||
@ -444,7 +408,7 @@ def create_saudi_transfers(admissions):
|
||||
transfer_id=uuid.uuid4(),
|
||||
admission=admission,
|
||||
patient=admission.patient,
|
||||
transfer_type=transfer_type[0],
|
||||
transfer_type=transfer_type,
|
||||
from_ward=admission.current_ward,
|
||||
from_bed=admission.current_bed,
|
||||
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'])}",
|
||||
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,
|
||||
complications="None noted" if status == 'completed' else None,
|
||||
notes=f"Transfer completed successfully" if status == 'completed' 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,
|
||||
notes=f"Transfer completed successfully" if status == 'COMPLETED' else None,
|
||||
created_at=requested_datetime,
|
||||
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)
|
||||
|
||||
# Update admission current location if transfer completed
|
||||
if status == 'completed':
|
||||
if status == 'COMPLETED':
|
||||
admission.current_ward = to_ward
|
||||
admission.current_bed = to_bed
|
||||
admission.save()
|
||||
|
||||
# Update bed statuses
|
||||
transfer.from_bed.status = 'available'
|
||||
transfer.from_bed.status = 'AVAILABLE'
|
||||
transfer.from_bed.current_patient = None
|
||||
transfer.from_bed.current_admission = None
|
||||
transfer.from_bed.occupied_since = None
|
||||
transfer.from_bed.save()
|
||||
|
||||
to_bed.status = 'occupied'
|
||||
to_bed.status = 'OCCUPIED'
|
||||
to_bed.current_patient = admission.patient
|
||||
to_bed.current_admission = admission
|
||||
to_bed.occupied_since = actual_datetime
|
||||
@ -513,18 +477,9 @@ def create_saudi_discharge_summaries(admissions):
|
||||
"""Create discharge summaries for discharged Saudi patients"""
|
||||
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 = [
|
||||
('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')
|
||||
]
|
||||
discharge_dispositions = ['HOME','HOME_HEALTH','NURSING_HOME','REHAB_FACILITY','HOSPICE','TRANSFER','DECEASED','LEFT_AMA','OTHER',]
|
||||
|
||||
for admission in discharged_admissions:
|
||||
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"
|
||||
],
|
||||
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",
|
||||
transportation_arranged=True,
|
||||
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"""
|
||||
surgeries = []
|
||||
|
||||
surgery_types = [
|
||||
('elective', 'Elective Surgery'),
|
||||
('emergency', 'Emergency Surgery'),
|
||||
('urgent', 'Urgent Surgery'),
|
||||
('add_on', 'Add-on Surgery')
|
||||
]
|
||||
surgery_types = ['ELECTIVE','URGENT','EMERGENT','TRAUMA','TRANSPLANT',
|
||||
'CARDIAC','NEUROSURGERY','ORTHOPEDIC','GENERAL','OTHER',]
|
||||
|
||||
anesthesia_types = [
|
||||
('general', 'General Anesthesia'),
|
||||
('regional', 'Regional Anesthesia'),
|
||||
('local', 'Local Anesthesia'),
|
||||
('mac', 'Monitored Anesthesia Care'),
|
||||
('spinal', 'Spinal Anesthesia'),
|
||||
('epidural', 'Epidural Anesthesia')
|
||||
]
|
||||
anesthesia_types = ['GENERAL','REGIONAL','LOCAL','SPINAL','EPIDURAL','MAC','SEDATION','OTHER',]
|
||||
|
||||
surgery_statuses = [
|
||||
('scheduled', 'Scheduled'),
|
||||
('confirmed', 'Confirmed'),
|
||||
('in_progress', 'In Progress'),
|
||||
('completed', 'Completed'),
|
||||
('cancelled', 'Cancelled'),
|
||||
('delayed', 'Delayed')
|
||||
]
|
||||
surgery_statuses = ['SCHEDULED','CONFIRMED','PREP','IN_PROGRESS','COMPLETED','CANCELLED','POSTPONED','DELAYED',]
|
||||
|
||||
priorities = ['routine', 'urgent', 'emergent', 'elective']
|
||||
priorities = ['ROUTINE','URGENT','EMERGENT','STAT',]
|
||||
|
||||
for tenant in tenants:
|
||||
# Get required staff and patients
|
||||
@ -674,9 +611,9 @@ def create_saudi_surgery_schedules(tenants, surgeries_per_tenant=30):
|
||||
actual_end_time = 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)
|
||||
if status == 'completed':
|
||||
if status == 'COMPLETED':
|
||||
actual_duration = estimated_duration + random.randint(-30, 60)
|
||||
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
|
||||
procedure_name=procedure,
|
||||
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_start_time=start_time,
|
||||
estimated_duration_minutes=estimated_duration,
|
||||
@ -697,7 +634,7 @@ def create_saudi_surgery_schedules(tenants, surgeries_per_tenant=30):
|
||||
anesthesiologist=random.choice(anesthesiologists),
|
||||
scrub_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_orders=[
|
||||
'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_end_time=actual_end_time,
|
||||
actual_duration_minutes=actual_duration,
|
||||
postop_diagnosis=random.choice(SAUDI_COMMON_DIAGNOSES) 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,
|
||||
postop_diagnosis=random.choice(SAUDI_COMMON_DIAGNOSES) 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,
|
||||
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),
|
||||
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)),
|
||||
updated_at=django_timezone.now() - timedelta(hours=random.randint(1, 24))
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -47,7 +47,7 @@ SAUDI_STATES = [
|
||||
'Jazan Province', 'Madinah Province', 'Qassim Province', 'Tabuk Province'
|
||||
]
|
||||
|
||||
SAUDI_LANGUAGES = ['Arabic', 'English', 'Urdu', 'Hindi', 'Bengali', 'Tagalog']
|
||||
SAUDI_LANGUAGES = ['Arabic', 'English']
|
||||
|
||||
SAUDI_ALLERGIES = [
|
||||
'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)
|
||||
|
||||
# Generate unique MRN
|
||||
mrn = f"MRN{tenant.id}{random.randint(100000, 999999)}"
|
||||
mrn = random.randint(100000, 999999)
|
||||
|
||||
# Check if MRN already exists
|
||||
while PatientProfile.objects.filter(mrn=mrn).exists():
|
||||
|
||||
BIN
templates/.DS_Store
vendored
BIN
templates/.DS_Store
vendored
Binary file not shown.
@ -18,10 +18,18 @@
|
||||
<!-- ================== BEGIN core-css ================== -->
|
||||
|
||||
<link href="{% static 'css/vendor.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/default/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/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>
|
||||
<!-- HTMX -->
|
||||
<script src="{% static 'js/htmx.min.js' %}"></script>
|
||||
|
||||
@ -261,22 +261,31 @@
|
||||
</div>
|
||||
<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>
|
||||
</td>
|
||||
<td>
|
||||
<div>{{ admission.ward.name }}</div>
|
||||
<small class="text-muted">Bed {{ admission.bed.bed_number }}</small>
|
||||
<div>{{ admission.current_ward.name }}</div>
|
||||
<small class="text-muted">Bed {{ admission.current_bed.bed_number }}</small>
|
||||
</td>
|
||||
<td>
|
||||
<div>{{ admission.admission_date|date:"M d, Y" }}</div>
|
||||
<small class="text-muted">{{ admission.admission_time|time:"H:i" }}</small>
|
||||
<div>{{ admission.admission_datetime|date:"M d, Y" }}</div>
|
||||
<small class="text-muted ">{{ admission.admission_datetime|time:"H:i" }}</small>
|
||||
</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 %}">
|
||||
{{ admission.get_admission_type_display }}
|
||||
</span>
|
||||
<div class="badge
|
||||
bg-{% if admission.admission_type == 'EMERGENCY' %}danger
|
||||
{% 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>
|
||||
{% if admission.attending_physician %}
|
||||
@ -315,9 +324,10 @@
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle dropdown-toggle-split"
|
||||
data-bs-toggle="dropdown">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<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
|
||||
</a></li>
|
||||
{% if admission.status == 'ADMITTED' %}
|
||||
@ -365,48 +375,7 @@
|
||||
|
||||
<!-- Pagination -->
|
||||
{% if is_paginated %}
|
||||
<div class="card-footer">
|
||||
<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>
|
||||
{% include 'partial/pagination.html'%}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -15,11 +15,12 @@
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
<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
|
||||
</a>
|
||||
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<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
|
||||
</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<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
|
||||
</a></li>
|
||||
{# <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#}
|
||||
{# </a></li>#}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -45,6 +45,15 @@
|
||||
border-color: #b6effb;
|
||||
}
|
||||
|
||||
.bed-reserved {
|
||||
background-color: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
.bed-out_of_order {
|
||||
background-color: #b6b4b4;
|
||||
color: #434242;
|
||||
}
|
||||
|
||||
.bed-status-available {
|
||||
color: #198754;
|
||||
}
|
||||
@ -65,6 +74,40 @@
|
||||
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 {
|
||||
position: relative;
|
||||
}
|
||||
@ -257,13 +300,13 @@
|
||||
<div class="progress-bar bg-info"
|
||||
role="progressbar"
|
||||
style="width: {{ occupancy_rate }}%"
|
||||
title="{{ occupancy_rate }}% occupancy">
|
||||
{{ occupancy_rate }}%
|
||||
title="{{ occupancy_rate|floatformat:0 }}% occupancy">
|
||||
{{ occupancy_rate|floatformat:0}}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ms-3">
|
||||
<h4 class="mb-0">{{ occupancy_rate }}%</h4>
|
||||
<h4 class="mb-0">{{ occupancy_rate|floatformat:0 }}%</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
@ -278,17 +321,18 @@
|
||||
<h5 class="card-title mb-0">Ward Distribution</h5>
|
||||
</div>
|
||||
<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">
|
||||
<span>{{ ward.name }}</span>
|
||||
<div class="d-flex align-items-center">
|
||||
<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"
|
||||
style="width: {{ ward.occupancy_rate }}%">
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-muted">{{ ward.occupied }}/{{ ward.total }}</span>
|
||||
<span class="text-muted">{{ ward.occupancy_rate|floatformat:0 }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
@ -332,23 +376,23 @@
|
||||
<div class="col-md-3">
|
||||
<select class="form-select" id="statusFilter">
|
||||
<option value="">All Status</option>
|
||||
<option value="available">Available</option>
|
||||
<option value="occupied">Occupied</option>
|
||||
<option value="maintenance">Maintenance</option>
|
||||
<option value="blocked">Blocked</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>
|
||||
<option value="AVAILABLE">Available</option>
|
||||
<option value="OCCUPIED">Occupied</option>
|
||||
<option value="MAINTENANCE">Maintenance</option>
|
||||
<option value="BLOCKED">Blocked</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>#}
|
||||
{# </div>#}
|
||||
<div class="col-md-3">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="Search beds..." id="bedSearch">
|
||||
@ -370,30 +414,80 @@
|
||||
<div class="row">
|
||||
{% for bed in ward.beds.all %}
|
||||
<div class="col-md-2 col-sm-3 col-4 mb-3">
|
||||
<div class="bed-card card h-100 bed-{{ bed.status }}"
|
||||
data-bed-id="{{ bed.id }}"
|
||||
data-status="{{ bed.status }}"
|
||||
<div class="bed-card card h-100 bed-{{ bed.status|lower }}"
|
||||
data-bed-id="{{ bed.id }}"
|
||||
data-status="{{ bed.status|lower }}"
|
||||
data-type="{{ bed.bed_type }}"
|
||||
onclick="selectBed('{{ bed.id }}')">
|
||||
<div class="card-body p-2 text-center">
|
||||
<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>
|
||||
<h6 class="card-title mb-1">{{ bed.bed_number }}</h6>
|
||||
<small class="text-muted">{{ bed.room_number }}</small>
|
||||
{% if bed.current_patient %}
|
||||
{% if bed.current_admission %}
|
||||
<div class="mt-2">
|
||||
<small class="text-truncate d-block">{{ bed.current_patient.get_full_name }}</small>
|
||||
<small class="text-muted">{{ bed.occupied_since|timesince }} ago</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
<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 }}
|
||||
</span>
|
||||
</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>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@ -418,14 +512,14 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for bed in all_beds %}
|
||||
{% for bed in beds %}
|
||||
<tr data-bed-id="{{ bed.id }}"
|
||||
data-status="{{ bed.status }}"
|
||||
data-status="{{ bed.status|lower }}"
|
||||
data-type="{{ bed.bed_type }}"
|
||||
data-ward="{{ bed.ward.id }}">
|
||||
<td>
|
||||
<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>
|
||||
</div>
|
||||
</td>
|
||||
@ -435,15 +529,15 @@
|
||||
<span class="badge bg-secondary">{{ bed.get_bed_type_display }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-{{ bed.status_color }}">
|
||||
<span class="badge bg-{{ bed.status|lower }}">
|
||||
{{ bed.get_status_display }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
{% if bed.current_patient %}
|
||||
{% if bed.current_admission %}
|
||||
<div>
|
||||
<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>
|
||||
{% else %}
|
||||
<span class="text-muted">-</span>
|
||||
@ -461,20 +555,28 @@
|
||||
</td>
|
||||
<td>
|
||||
<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>
|
||||
</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">
|
||||
<i class="fa fa-edit"></i>
|
||||
</button>
|
||||
{% if bed.status == 'available' %}
|
||||
{% if bed.status == 'AVAILABLE' %}
|
||||
<button class="btn btn-outline-success" onclick="assignPatient('{{ bed.id }}')" title="Assign Patient">
|
||||
<i class="fa fa-user-plus"></i>
|
||||
</button>
|
||||
{% elif bed.status == 'occupied' %}
|
||||
<button class="btn btn-outline-warning" onclick="dischargePatient('{{ bed.id }}')" title="Discharge">
|
||||
{% elif bed.status == 'OCCUPIED' %}
|
||||
{% 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>
|
||||
</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 %}
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn btn-outline-info dropdown-toggle" data-bs-toggle="dropdown" title="More Actions">
|
||||
@ -502,6 +604,11 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-center">
|
||||
{% if is_paginated %}
|
||||
{% include 'partial/pagination.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -582,6 +689,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
<script src="{% static 'plugins/dropzone/dist/min/dropzone.min.js' %}"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
setupEventHandlers();
|
||||
@ -696,21 +804,21 @@ function assignPatientToBed() {
|
||||
});
|
||||
}
|
||||
|
||||
function dischargePatient(bedId) {
|
||||
if (confirm('Are you sure you want to discharge the patient from this bed?')) {
|
||||
$.post('', {
|
||||
bed_id: bedId,
|
||||
csrfmiddlewaretoken: '{{ csrf_token }}'
|
||||
}, function(data) {
|
||||
if (data.success) {
|
||||
toastr.success('Patient discharged successfully');
|
||||
updateBedStatuses();
|
||||
} else {
|
||||
toastr.error('Failed to discharge patient: ' + data.error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
{#function dischargePatient(admissionId) {#}
|
||||
{# if (confirm('Are you sure you want to discharge the patient from this bed?')) {#}
|
||||
{# $.post('{% url 'inpatients:discharge_patient' 0 %}'.replace('0', admissionId), {#}
|
||||
{#bed_id: bedId,#}
|
||||
{# csrfmiddlewaretoken: '{{ csrf_token }}'#}
|
||||
{# }, function(data) {#}
|
||||
{# if (data.success) {#}
|
||||
{# toastr.success('Patient discharged successfully');#}
|
||||
{# updateBedStatuses();#}
|
||||
{# } else {#}
|
||||
{# toastr.error('Failed to discharge patient: ' + data.error);#}
|
||||
{# }#}
|
||||
{# });#}
|
||||
{# }#}
|
||||
{# }#}
|
||||
|
||||
function scheduleMaintenance(bedId) {
|
||||
$.post('', {
|
||||
@ -792,8 +900,8 @@ function updateBedStatuses() {
|
||||
.addClass('bed-' + bed.status);
|
||||
bedCard.find('.bed-status-badge .badge')
|
||||
.removeClass('bg-success bg-danger bg-warning bg-secondary bg-info')
|
||||
.addClass('bg-' + bed.status_color)
|
||||
.text(bed.status_display);
|
||||
.addClass('bg-' + bed.status)
|
||||
.text(bed.status);
|
||||
|
||||
// Update row
|
||||
bedRow.find('.badge').first()
|
||||
|
||||
@ -297,7 +297,7 @@
|
||||
<small class="text-muted">{{ ward.get_ward_type_display }}</small>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
670
templates/inpatients/maintenance_bed.html
Normal file
670
templates/inpatients/maintenance_bed.html
Normal 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 %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user