update
This commit is contained in:
parent
fd2f7259c0
commit
f941a8e608
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -262,7 +262,48 @@ class Bed(models.Model):
|
||||
"""
|
||||
Hospital bed model for tracking individual bed status and assignments.
|
||||
"""
|
||||
|
||||
BED_TYPE_CHOICES = [
|
||||
('STANDARD', 'Standard Bed'),
|
||||
('ICU', 'ICU Bed'),
|
||||
('CARDIAC', 'Cardiac Monitoring'),
|
||||
('ISOLATION', 'Isolation Bed'),
|
||||
('BARIATRIC', 'Bariatric Bed'),
|
||||
('PEDIATRIC', 'Pediatric Bed'),
|
||||
('NEONATAL', 'Neonatal Bed'),
|
||||
('MATERNITY', 'Maternity Bed'),
|
||||
('PSYCHIATRIC', 'Psychiatric Bed'),
|
||||
('STRETCHER', 'Stretcher/Gurney'),
|
||||
('RECLINER', 'Recliner Chair'),
|
||||
('OTHER', 'Other'),
|
||||
]
|
||||
ROOM_TYPE_CHOICES = [
|
||||
('PRIVATE', 'Private Room'),
|
||||
('SEMI_PRIVATE', 'Semi-Private'),
|
||||
('SHARED', 'Shared Room'),
|
||||
('ICU', 'ICU Room'),
|
||||
('ISOLATION', 'Isolation Room'),
|
||||
]
|
||||
STATUS_CHOICES = [
|
||||
('AVAILABLE', 'Available'),
|
||||
('OCCUPIED', 'Occupied'),
|
||||
('RESERVED', 'Reserved'),
|
||||
('MAINTENANCE', 'Under Maintenance'),
|
||||
('CLEANING', 'Being Cleaned'),
|
||||
('OUT_OF_ORDER', 'Out of Order'),
|
||||
('BLOCKED', 'Blocked'),
|
||||
]
|
||||
CLEANING_LEVEL_CHOICES = [
|
||||
('STANDARD', 'Standard Cleaning'),
|
||||
('DEEP', 'Deep Cleaning'),
|
||||
('ISOLATION', 'Isolation Cleaning'),
|
||||
('TERMINAL', 'Terminal Cleaning'),
|
||||
]
|
||||
BED_POSITION_CHOICES = [
|
||||
('WINDOW', 'Window Side'),
|
||||
('DOOR', 'Door Side'),
|
||||
('CENTER', 'Center'),
|
||||
('CORNER', 'Corner'),
|
||||
]
|
||||
# Ward relationship
|
||||
ward = models.ForeignKey(
|
||||
Ward,
|
||||
@ -284,48 +325,21 @@ class Bed(models.Model):
|
||||
# Bed Type and Configuration
|
||||
bed_type = models.CharField(
|
||||
max_length=30,
|
||||
choices=[
|
||||
('STANDARD', 'Standard Bed'),
|
||||
('ICU', 'ICU Bed'),
|
||||
('CARDIAC', 'Cardiac Monitoring'),
|
||||
('ISOLATION', 'Isolation Bed'),
|
||||
('BARIATRIC', 'Bariatric Bed'),
|
||||
('PEDIATRIC', 'Pediatric Bed'),
|
||||
('NEONATAL', 'Neonatal Bed'),
|
||||
('MATERNITY', 'Maternity Bed'),
|
||||
('PSYCHIATRIC', 'Psychiatric Bed'),
|
||||
('STRETCHER', 'Stretcher/Gurney'),
|
||||
('RECLINER', 'Recliner Chair'),
|
||||
('OTHER', 'Other'),
|
||||
],
|
||||
choices=BED_TYPE_CHOICES,
|
||||
default='STANDARD',
|
||||
help_text='Type of bed'
|
||||
)
|
||||
|
||||
room_type = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('PRIVATE', 'Private Room'),
|
||||
('SEMI_PRIVATE', 'Semi-Private'),
|
||||
('SHARED', 'Shared Room'),
|
||||
('ICU', 'ICU Room'),
|
||||
('ISOLATION', 'Isolation Room'),
|
||||
],
|
||||
choices=ROOM_TYPE_CHOICES,
|
||||
help_text='Type of room'
|
||||
)
|
||||
|
||||
# Bed Status
|
||||
status = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('AVAILABLE', 'Available'),
|
||||
('OCCUPIED', 'Occupied'),
|
||||
('RESERVED', 'Reserved'),
|
||||
('MAINTENANCE', 'Under Maintenance'),
|
||||
('CLEANING', 'Being Cleaned'),
|
||||
('OUT_OF_ORDER', 'Out of Order'),
|
||||
('BLOCKED', 'Blocked'),
|
||||
],
|
||||
choices=STATUS_CHOICES,
|
||||
default='AVAILABLE',
|
||||
help_text='Current bed status'
|
||||
)
|
||||
@ -405,12 +419,7 @@ class Bed(models.Model):
|
||||
)
|
||||
cleaning_level = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('STANDARD', 'Standard Cleaning'),
|
||||
('DEEP', 'Deep Cleaning'),
|
||||
('ISOLATION', 'Isolation Cleaning'),
|
||||
('TERMINAL', 'Terminal Cleaning'),
|
||||
],
|
||||
choices=CLEANING_LEVEL_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Level of last cleaning'
|
||||
@ -439,12 +448,7 @@ class Bed(models.Model):
|
||||
# Location Details
|
||||
bed_position = models.CharField(
|
||||
max_length=20,
|
||||
choices=[
|
||||
('WINDOW', 'Window Side'),
|
||||
('DOOR', 'Door Side'),
|
||||
('CENTER', 'Center'),
|
||||
('CORNER', 'Corner'),
|
||||
],
|
||||
choices=BED_POSITION_CHOICES,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='Position within room'
|
||||
|
||||
@ -403,6 +403,22 @@ def create_saudi_transfers(admissions):
|
||||
to_bed = random.choice(available_beds)
|
||||
to_ward = to_bed.ward
|
||||
|
||||
# Get users who can request transfers (physicians, nurses, etc.)
|
||||
requesting_users = User.objects.filter(
|
||||
tenant=admission.tenant,
|
||||
is_active=True,
|
||||
role__in=['PHYSICIAN', 'NURSE', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT']
|
||||
)
|
||||
|
||||
if not requesting_users.exists():
|
||||
# Fallback to any active user from the tenant
|
||||
requesting_users = User.objects.filter(tenant=admission.tenant, is_active=True)
|
||||
|
||||
if not requesting_users.exists():
|
||||
continue # Skip if no users available
|
||||
|
||||
requested_by = random.choice(requesting_users)
|
||||
|
||||
requested_datetime = admission.admission_datetime + timedelta(hours=random.randint(1, 72))
|
||||
status = random.choice(transfer_statuses)
|
||||
|
||||
@ -415,6 +431,15 @@ def create_saudi_transfers(admissions):
|
||||
if status in ['in_progress', 'completed']:
|
||||
actual_datetime = scheduled_datetime + timedelta(minutes=random.randint(-30, 60))
|
||||
|
||||
# Get transport team members
|
||||
transport_team_members = []
|
||||
nurses = User.objects.filter(
|
||||
tenant=admission.tenant,
|
||||
role__in=['NURSE', 'NURSE_PRACTITIONER']
|
||||
)
|
||||
if nurses:
|
||||
transport_team_members = random.sample(list(nurses), min(2, nurses.count()))
|
||||
|
||||
transfer = Transfer.objects.create(
|
||||
transfer_id=uuid.uuid4(),
|
||||
admission=admission,
|
||||
@ -424,6 +449,7 @@ def create_saudi_transfers(admissions):
|
||||
from_bed=admission.current_bed,
|
||||
to_ward=to_ward,
|
||||
to_bed=to_bed,
|
||||
requested_by=requested_by, # Add the required field
|
||||
requested_datetime=requested_datetime,
|
||||
scheduled_datetime=scheduled_datetime,
|
||||
actual_datetime=actual_datetime,
|
||||
@ -435,7 +461,6 @@ def create_saudi_transfers(admissions):
|
||||
]),
|
||||
priority=random.choice(priorities),
|
||||
transport_method=random.choice(transport_methods),
|
||||
transport_team=['Nursing staff', 'Transport aide'] if random.choice([True, False]) else [],
|
||||
equipment_needed=['IV pole', 'Oxygen tank', 'Monitor'] if random.choice([True, False]) else [],
|
||||
supplies_needed=['Medications', 'Patient chart', 'Personal items'] if random.choice([True, False]) else [],
|
||||
patient_condition=random.choice(['stable', 'unstable', 'critical', 'improving']),
|
||||
@ -455,6 +480,10 @@ def create_saudi_transfers(admissions):
|
||||
updated_at=requested_datetime + timedelta(hours=random.randint(1, 24))
|
||||
)
|
||||
|
||||
# Set transport team using .set() method for many-to-many relationship
|
||||
if transport_team_members:
|
||||
transfer.transport_team.set(transport_team_members)
|
||||
|
||||
# Update admission current location if transfer completed
|
||||
if status == 'completed':
|
||||
admission.current_ward = to_ward
|
||||
@ -570,7 +599,7 @@ def create_saudi_discharge_summaries(admissions):
|
||||
social_worker_involved=random.choice([True, False]),
|
||||
case_manager_involved=random.choice([True, False]),
|
||||
readmission_risk=random.choice(['low', 'moderate', 'high']),
|
||||
patient_satisfaction=random.choice(['excellent', 'good', 'fair', 'poor']),
|
||||
patient_satisfaction=random.randint(1, 5), # Changed to numeric scale (1-5)
|
||||
discharging_physician=admission.attending_physician,
|
||||
summary_completed=True,
|
||||
summary_signed=True,
|
||||
@ -623,12 +652,14 @@ def create_saudi_surgery_schedules(tenants, surgeries_per_tenant=30):
|
||||
nurses = list(User.objects.filter(tenant=tenant, role__in=['NURSE', 'NURSE_PRACTITIONER']))
|
||||
admissions = list(Admission.objects.filter(tenant=tenant, status='active'))
|
||||
|
||||
if not patients or not surgeons or not nurses:
|
||||
if not patients or not surgeons or not nurses or not admissions:
|
||||
print(f"Insufficient data for tenant {tenant.name}: patients={len(patients)}, surgeons={len(surgeons)}, nurses={len(nurses)}, admissions={len(admissions)}")
|
||||
continue
|
||||
|
||||
for i in range(surgeries_per_tenant):
|
||||
patient = random.choice(patients)
|
||||
admission = random.choice(admissions) if admissions and random.choice([True, False]) else None
|
||||
for i in range(min(surgeries_per_tenant, len(admissions))): # Don't exceed available admissions
|
||||
# Always use an admission - required field
|
||||
admission = random.choice(admissions)
|
||||
patient = admission.patient # Use patient from the admission
|
||||
procedure = random.choice(SAUDI_SURGICAL_PROCEDURES)
|
||||
|
||||
# Generate surgery date (within next 30 days)
|
||||
@ -653,7 +684,7 @@ def create_saudi_surgery_schedules(tenants, surgeries_per_tenant=30):
|
||||
tenant=tenant,
|
||||
surgery_id=uuid.uuid4(),
|
||||
patient=patient,
|
||||
admission=admission,
|
||||
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],
|
||||
@ -693,14 +724,14 @@ def create_saudi_surgery_schedules(tenants, surgeries_per_tenant=30):
|
||||
updated_at=django_timezone.now() - timedelta(hours=random.randint(1, 24))
|
||||
)
|
||||
|
||||
# Set assistant surgeons
|
||||
# Set assistant surgeons using .set() method for many-to-many relationship
|
||||
assistant_surgeons = random.sample(surgeons, random.randint(0, 2))
|
||||
if assistant_surgeons:
|
||||
surgery.assistant_surgeons.set(assistant_surgeons)
|
||||
|
||||
surgeries.append(surgery)
|
||||
|
||||
print(f"Created {surgeries_per_tenant} surgeries for {tenant.name}")
|
||||
print(f"Created {min(surgeries_per_tenant, len(admissions))} surgeries for {tenant.name}")
|
||||
|
||||
return surgeries
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -393,7 +393,7 @@ class MedicationListView(LoginRequiredMixin, ListView):
|
||||
if active_only:
|
||||
queryset = queryset.filter(is_active=True)
|
||||
|
||||
return queryset.order_by('name')
|
||||
return queryset.order_by('brand_name')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
@ -336,33 +336,7 @@
|
||||
|
||||
<!-- Pagination -->
|
||||
{% if is_paginated %}
|
||||
<nav aria-label="Bed pagination">
|
||||
<ul class="pagination justify-content-center">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page=1">« First</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
<li class="page-item active">
|
||||
<span class="page-link">
|
||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
|
||||
</span>
|
||||
</li>
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Last »</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% include 'partial/pagination.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -148,8 +148,8 @@
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="content" class="app-content">
|
||||
<div class="container">
|
||||
|
||||
<div class="container-fluid">
|
||||
<ul 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>
|
||||
@ -578,7 +578,7 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
|
||||
@ -150,13 +150,13 @@
|
||||
</button>
|
||||
|
||||
{% if transfer.status not in 'COMPLETED,CANCELLED,REJECTED' %}
|
||||
<button class="btn btn-outline-secondary"
|
||||
title="Cancel"
|
||||
hx-post="{% url 'inpatients:cancel_transfer' transfer.id %}"
|
||||
hx-confirm="Cancel this transfer?"
|
||||
hx-swap="none">
|
||||
<i class="fas fa-ban"></i>
|
||||
</button>
|
||||
{# <button class="btn btn-outline-secondary" #}
|
||||
{# title="Cancel"#}
|
||||
{# hx-post="{% url 'inpatients:cancel_transfer' transfer.id %}"#}
|
||||
{# hx-confirm="Cancel this transfer?"#}
|
||||
{# hx-swap="none">#}
|
||||
{# <i class="fas fa-ban"></i>#}
|
||||
{# </button>#}
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@ -300,35 +300,35 @@
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-secondary">{{ item.location.name }}</span>
|
||||
<span class="badge bg-secondary">{{ item.storage_location }}</span>
|
||||
{% if item.location.description %}
|
||||
<br><small class="text-muted">{{ item.location.description }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="fw-bold {% if item.is_low_stock %}text-warning{% elif item.current_stock == 0 %}text-danger{% else %}text-success{% endif %}">
|
||||
{{ item.current_stock }}
|
||||
<span class="fw-bold {% if item.is_low_stock %}text-warning{% elif item.quantity_available == 0 %}text-danger{% else %}text-success{% endif %}">
|
||||
{{ item.quantity_available }}
|
||||
</span>
|
||||
<span class="text-muted ms-1">{{ item.get_unit_of_measure_display }}</span>
|
||||
<span class="text-muted ms-1">{{ item.bin_location }}</span>
|
||||
</div>
|
||||
{% if item.is_low_stock %}
|
||||
<small class="text-warning">
|
||||
<i class="fas fa-exclamation-triangle me-1"></i>Low Stock
|
||||
</small>
|
||||
{% elif item.current_stock == 0 %}
|
||||
{% elif item.quantity_available == 0 %}
|
||||
<small class="text-danger">
|
||||
<i class="fas fa-times-circle me-1"></i>Out of Stock
|
||||
</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-muted">{{ item.minimum_stock_level }}</span>
|
||||
<span class="text-muted">{{ item.reorder_point }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="font-monospace">{{ item.lot_number }}</span>
|
||||
{% if item.supplier %}
|
||||
<br><small class="text-muted">{{ item.supplier.name }}</small>
|
||||
<br><small class="text-muted">{{ item.supplier }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
@ -347,8 +347,8 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if item.unit_cost %}
|
||||
<div>${{ item.unit_cost|floatformat:2 }}</div>
|
||||
<small class="text-muted">Total: ${{ item.total_value|floatformat:2 }}</small>
|
||||
<div><span class="symbol">ê</span>{{ item.unit_cost|floatformat:'2g' }}</div>
|
||||
<small class="text-muted">Total: <span class="symbol">ê</span>{{ item.total_cost|floatformat:'2g' }}</small>
|
||||
{% else %}
|
||||
<span class="text-muted">N/A</span>
|
||||
{% endif %}
|
||||
@ -356,7 +356,7 @@
|
||||
<td>
|
||||
{% if item.is_expired %}
|
||||
<span class="badge bg-danger">Expired</span>
|
||||
{% elif item.current_stock == 0 %}
|
||||
{% elif item.quantity_available == 0 %}
|
||||
<span class="badge bg-danger">Out of Stock</span>
|
||||
{% elif item.is_low_stock %}
|
||||
<span class="badge bg-warning text-dark">Low Stock</span>
|
||||
@ -396,36 +396,8 @@
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
{% if page_obj.has_other_pages %}
|
||||
<div class="card-footer">
|
||||
<nav aria-label="Inventory pagination">
|
||||
<ul class="pagination justify-content-center mb-0">
|
||||
{% if page_obj.has_previous %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}">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="?page={{ num }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}">{{ num }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}">Next</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
{% if is_paginated %}
|
||||
{% include 'partial/pagination.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user