5851 lines
198 KiB
Plaintext
5851 lines
198 KiB
Plaintext
# import random
|
||
# import uuid
|
||
# from datetime import datetime, timedelta
|
||
# from decimal import Decimal
|
||
# from django.utils import timezone as django_timezone
|
||
# from django.contrib.auth import get_user_model
|
||
#
|
||
# from core.models import Tenant
|
||
# from inventory.models import InventoryItem, InventoryStock, InventoryLocation, PurchaseOrder, PurchaseOrderItem, \
|
||
# Supplier
|
||
#
|
||
# User = get_user_model()
|
||
#
|
||
# # Saudi Arabian Inventory Data
|
||
# SAUDI_MEDICAL_CATEGORIES = [
|
||
# 'Pharmaceuticals',
|
||
# 'Medical Devices',
|
||
# 'Surgical Instruments',
|
||
# 'Laboratory Supplies',
|
||
# 'PPE & Safety',
|
||
# 'IV Therapy',
|
||
# 'Emergency Supplies'
|
||
# ]
|
||
#
|
||
# SAUDI_SUPPLIERS = [
|
||
# 'Saudi Medical Supply Co.',
|
||
# 'Gulf Medical Equipment',
|
||
# 'Arabian Healthcare Supplies',
|
||
# 'Riyadh Medical Trading',
|
||
# 'Al-Dawaa Medical',
|
||
# 'Nahdi Medical Company',
|
||
# 'United Pharmaceuticals'
|
||
# ]
|
||
#
|
||
# SAUDI_CITIES = ['Riyadh', 'Jeddah', 'Dammam', 'Medina', 'Taif', 'Khobar']
|
||
#
|
||
# MEDICAL_ITEMS = [
|
||
# {'name': 'Paracetamol 500mg', 'category': 'Pharmaceuticals', 'unit': 'TAB'},
|
||
# {'name': 'Disposable Syringe 5ml', 'category': 'Medical Devices', 'unit': 'PCS'},
|
||
# {'name': 'Surgical Gloves Size M', 'category': 'PPE & Safety', 'unit': 'PAIR'},
|
||
# {'name': 'Blood Collection Tube', 'category': 'Laboratory Supplies', 'unit': 'PCS'},
|
||
# {'name': 'IV Bag Normal Saline', 'category': 'IV Therapy', 'unit': 'BAG'},
|
||
# {'name': 'Emergency Oxygen Mask', 'category': 'Emergency Supplies', 'unit': 'PCS'}
|
||
# ]
|
||
#
|
||
#
|
||
# def create_saudi_suppliers(tenants):
|
||
# """Create Saudi suppliers"""
|
||
# suppliers = []
|
||
#
|
||
# for tenant in tenants:
|
||
# print(f"Creating suppliers for {tenant.name}...")
|
||
#
|
||
# for i, supplier_name in enumerate(SAUDI_SUPPLIERS):
|
||
# supplier_code = f"SUP-{tenant.id}-{i + 1:03d}"
|
||
#
|
||
# try:
|
||
# supplier = Supplier.objects.create(
|
||
# tenant=tenant,
|
||
# supplier_code=supplier_code,
|
||
# name=supplier_name,
|
||
# supplier_type='DISTRIBUTOR',
|
||
# city=random.choice(SAUDI_CITIES),
|
||
# country='Saudi Arabia',
|
||
# is_active=True
|
||
# )
|
||
# suppliers.append(supplier)
|
||
# print(f" ✓ Created supplier: {supplier_name}")
|
||
#
|
||
# except Exception as e:
|
||
# print(f" ✗ Error creating supplier {supplier_name}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(suppliers)} suppliers")
|
||
# return suppliers
|
||
#
|
||
#
|
||
# def create_saudi_inventory_locations(tenants):
|
||
# """Create Saudi inventory locations"""
|
||
# locations = []
|
||
#
|
||
# storage_rooms = ['Pharmacy', 'Central Supply', 'OR Storage', 'ICU Supply', 'Ward Storage']
|
||
#
|
||
# for tenant in tenants:
|
||
# print(f"Creating locations for {tenant.name}...")
|
||
#
|
||
# for i, room in enumerate(storage_rooms):
|
||
# location_code = f"LOC-{tenant.id}-{i + 1:03d}"
|
||
#
|
||
# try:
|
||
# location = InventoryLocation.objects.create(
|
||
# tenant=tenant,
|
||
# location_code=location_code,
|
||
# name=f"{room} - {tenant.city}",
|
||
# description=f"Storage location in {room}",
|
||
# location_type='WAREHOUSE',
|
||
# building='Main Hospital',
|
||
# floor='Ground Floor',
|
||
# room=room,
|
||
# is_active=True
|
||
# )
|
||
# locations.append(location)
|
||
# print(f" ✓ Created location: {location.name}")
|
||
#
|
||
# except Exception as e:
|
||
# print(f" ✗ Error creating location {room}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(locations)} locations")
|
||
# return locations
|
||
#
|
||
#
|
||
# def create_saudi_inventory_items(tenants):
|
||
# """Create Saudi inventory items"""
|
||
# items = []
|
||
#
|
||
# for tenant in tenants:
|
||
# print(f"Creating items for {tenant.name}...")
|
||
#
|
||
# for i, item_data in enumerate(MEDICAL_ITEMS):
|
||
# item_code = f"ITM-{tenant.id}-{i + 1:03d}"
|
||
#
|
||
# try:
|
||
# item = InventoryItem.objects.create(
|
||
# tenant=tenant,
|
||
# item_code=item_code,
|
||
# item_name=item_data['name'],
|
||
# description=f"Medical item: {item_data['name']}",
|
||
# category=item_data['category'],
|
||
# subcategory=item_data['category'],
|
||
# item_type='STOCK',
|
||
# manufacturer='Saudi Medical Industries',
|
||
# unit_of_measure=item_data['unit'],
|
||
# package_size=1,
|
||
# unit_cost=Decimal(str(random.uniform(10, 100))),
|
||
# list_price=Decimal(str(random.uniform(15, 150))),
|
||
# has_expiration=item_data['category'] == 'Pharmaceuticals',
|
||
# is_active=True,
|
||
# is_tracked=True,
|
||
# reorder_point=random.randint(10, 50),
|
||
# reorder_quantity=random.randint(100, 500),
|
||
# max_stock_level=random.randint(500, 1000)
|
||
# )
|
||
# items.append(item)
|
||
# print(f" ✓ Created item: {item.item_name}")
|
||
#
|
||
# except Exception as e:
|
||
# print(f" ✗ Error creating item {item_data['name']}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(items)} items")
|
||
# return items
|
||
#
|
||
#
|
||
# def create_saudi_inventory_stock(items, locations):
|
||
# """Create Saudi inventory stock entries"""
|
||
# stocks = []
|
||
#
|
||
# for item in items:
|
||
# print(f"Creating stock for {item.item_name}...")
|
||
#
|
||
# # Get locations for this tenant
|
||
# tenant_locations = [loc for loc in locations if loc.tenant == item.tenant]
|
||
# if not tenant_locations:
|
||
# continue
|
||
#
|
||
# location = random.choice(tenant_locations)
|
||
#
|
||
# try:
|
||
# stock = InventoryStock.objects.create(
|
||
# inventory_item=item,
|
||
# location=location,
|
||
# quantity_on_hand=random.randint(50, 500),
|
||
# quantity_reserved=random.randint(0, 20),
|
||
# received_date=django_timezone.now().date() - timedelta(days=random.randint(1, 90)),
|
||
# expiration_date=django_timezone.now().date() + timedelta(days=365) if item.has_expiration else None,
|
||
# unit_cost=item.unit_cost,
|
||
# quality_status='AVAILABLE'
|
||
# )
|
||
# stocks.append(stock)
|
||
# print(f" ✓ Created stock for: {item.item_name}")
|
||
#
|
||
# except Exception as e:
|
||
# print(f" ✗ Error creating stock for {item.item_name}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(stocks)} stock entries")
|
||
# return stocks
|
||
#
|
||
#
|
||
# def create_saudi_purchase_orders(tenants, suppliers):
|
||
# """Create Saudi purchase orders"""
|
||
# orders = []
|
||
#
|
||
# for tenant in tenants:
|
||
# print(f"Creating purchase orders for {tenant.name}...")
|
||
#
|
||
# # Get suppliers for this tenant
|
||
# tenant_suppliers = [supplier for supplier in suppliers if supplier.tenant == tenant]
|
||
# if not tenant_suppliers:
|
||
# print(f" No suppliers found for {tenant.name}, skipping...")
|
||
# continue
|
||
#
|
||
# # Get delivery locations
|
||
# try:
|
||
# locations = InventoryLocation.objects.filter(tenant=tenant)
|
||
# delivery_location = locations.first() if locations.exists() else None
|
||
# except:
|
||
# delivery_location = None
|
||
#
|
||
# for i in range(3): # Create 3 orders per tenant
|
||
# po_number = f"PO-{tenant.id}-{django_timezone.now().year}-{i + 1:04d}"
|
||
# supplier = random.choice(tenant_suppliers)
|
||
#
|
||
# try:
|
||
# order = PurchaseOrder.objects.create(
|
||
# tenant=tenant,
|
||
# po_number=po_number,
|
||
# supplier=supplier,
|
||
# order_date=django_timezone.now().date() - timedelta(days=random.randint(1, 30)),
|
||
# requested_delivery_date=django_timezone.now().date() + timedelta(days=random.randint(7, 30)),
|
||
# order_type='STANDARD',
|
||
# priority='NORMAL',
|
||
# subtotal=Decimal(str(random.uniform(1000, 10000))),
|
||
# tax_amount=Decimal('0.00'),
|
||
# shipping_amount=Decimal('0.00'),
|
||
# total_amount=Decimal(str(random.uniform(1000, 10000))),
|
||
# status='DRAFT',
|
||
# delivery_location=delivery_location,
|
||
# payment_terms='NET_30'
|
||
# )
|
||
# orders.append(order)
|
||
# print(f" ✓ Created PO: {po_number}")
|
||
#
|
||
# except Exception as e:
|
||
# print(f" ✗ Error creating PO {po_number}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(orders)} purchase orders")
|
||
# return orders
|
||
#
|
||
#
|
||
# def create_saudi_purchase_order_items(orders, items):
|
||
# """Create Saudi purchase order items"""
|
||
# po_items = []
|
||
#
|
||
# for order in orders:
|
||
# print(f"Creating items for PO {order.po_number}...")
|
||
#
|
||
# # Get items for this tenant
|
||
# tenant_items = [item for item in items if item.tenant == order.tenant]
|
||
# if not tenant_items:
|
||
# continue
|
||
#
|
||
# # Create 2-3 items per order
|
||
# num_items = min(3, len(tenant_items))
|
||
# selected_items = random.sample(tenant_items, num_items)
|
||
#
|
||
# for line_num, item in enumerate(selected_items, 1):
|
||
# quantity_ordered = random.randint(10, 100)
|
||
# unit_price = item.unit_cost * Decimal(str(random.uniform(0.9, 1.1)))
|
||
# total_price = unit_price * quantity_ordered
|
||
#
|
||
# try:
|
||
# po_item = PurchaseOrderItem.objects.create(
|
||
# purchase_order=order,
|
||
# line_number=line_num,
|
||
# inventory_item=item,
|
||
# quantity_ordered=quantity_ordered,
|
||
# quantity_received=0,
|
||
# unit_price=unit_price,
|
||
# total_price=total_price,
|
||
# requested_delivery_date=order.requested_delivery_date,
|
||
# status='PENDING'
|
||
# )
|
||
# po_items.append(po_item)
|
||
# print(f" ✓ Created PO item: {item.item_name}")
|
||
#
|
||
# except Exception as e:
|
||
# print(f" ✗ Error creating PO item for {item.item_name}: {e}")
|
||
# continue
|
||
#
|
||
# print(f"Created {len(po_items)} purchase order items")
|
||
# return po_items
|
||
#
|
||
#
|
||
# def main():
|
||
# """Main function to create all Saudi inventory data"""
|
||
# print("🏥 Starting Saudi Inventory Data Generation...")
|
||
#
|
||
# # Get tenants
|
||
# try:
|
||
# tenants = list(Tenant.objects.filter(is_active=True)[:5]) # Limit to first 5 tenants
|
||
# if not tenants:
|
||
# print("❌ No active tenants found. Please run core_data.py first.")
|
||
# return
|
||
#
|
||
# print(f"📋 Found {len(tenants)} active tenants")
|
||
# except Exception as e:
|
||
# print(f"❌ Error getting tenants: {e}")
|
||
# return
|
||
#
|
||
# # Create data step by step
|
||
# print("\n1️⃣ Creating Suppliers...")
|
||
# suppliers = create_saudi_suppliers(tenants)
|
||
# if not suppliers:
|
||
# print("❌ No suppliers created. Stopping.")
|
||
# return
|
||
#
|
||
# print("\n2️⃣ Creating Locations...")
|
||
# locations = create_saudi_inventory_locations(tenants)
|
||
# if not locations:
|
||
# print("❌ No locations created. Stopping.")
|
||
# return
|
||
#
|
||
# print("\n3️⃣ Creating Items...")
|
||
# items = create_saudi_inventory_items(tenants)
|
||
# if not items:
|
||
# print("❌ No items created. Stopping.")
|
||
# return
|
||
#
|
||
# print("\n4️⃣ Creating Stock...")
|
||
# stocks = create_saudi_inventory_stock(items, locations)
|
||
#
|
||
# print("\n5️⃣ Creating Purchase Orders...")
|
||
# orders = create_saudi_purchase_orders(tenants, suppliers)
|
||
#
|
||
# print("\n6️⃣ Creating Purchase Order Items...")
|
||
# po_items = create_saudi_purchase_order_items(orders, items)
|
||
#
|
||
# print("\n🎉 Saudi Inventory Data Generation Complete!")
|
||
# print(f"📊 Summary:")
|
||
# print(f" - Suppliers: {len(suppliers)}")
|
||
# print(f" - Locations: {len(locations)}")
|
||
# print(f" - Items: {len(items)}")
|
||
# print(f" - Stock Entries: {len(stocks)}")
|
||
# print(f" - Purchase Orders: {len(orders)}")
|
||
# print(f" - PO Items: {len(po_items)}")
|
||
#
|
||
#
|
||
# if __name__ == "__main__":
|
||
# main()
|
||
|
||
|
||
|
||
|
||
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',
|
||
# on_delete=models.CASCADE,
|
||
# related_name='employees',
|
||
# help_text='Organization tenant'
|
||
# )
|
||
#
|
||
# # User relationship (optional - for employees who have system access)
|
||
# user = models.OneToOneField(
|
||
# settings.AUTH_USER_MODEL,
|
||
# on_delete=models.SET_NULL,
|
||
# null=True,
|
||
# blank=True,
|
||
# related_name='employee_profile',
|
||
# help_text='Associated user account'
|
||
# )
|
||
#
|
||
# # Employee Information
|
||
# employee_id = models.UUIDField(
|
||
# default=uuid.uuid4,
|
||
# unique=True,
|
||
# editable=False,
|
||
# help_text='Unique employee identifier'
|
||
# )
|
||
# employee_number = models.CharField(
|
||
# max_length=20,
|
||
# help_text='Employee number'
|
||
# )
|
||
#
|
||
# # Personal Information
|
||
# first_name = models.CharField(
|
||
# max_length=50,
|
||
# help_text='First name'
|
||
# )
|
||
# last_name = models.CharField(
|
||
# max_length=50,
|
||
# help_text='Last name'
|
||
# )
|
||
# middle_name = models.CharField(
|
||
# max_length=50,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Middle name'
|
||
# )
|
||
# preferred_name = models.CharField(
|
||
# max_length=50,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Preferred name'
|
||
# )
|
||
#
|
||
# # Contact Information
|
||
# email = models.EmailField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Email address'
|
||
# )
|
||
# phone = models.CharField(
|
||
# max_length=20,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Phone number'
|
||
# )
|
||
# mobile_phone = models.CharField(
|
||
# max_length=20,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Mobile phone number'
|
||
# )
|
||
#
|
||
# # Address Information
|
||
# address_line_1 = models.CharField(
|
||
# max_length=100,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Address line 1'
|
||
# )
|
||
# address_line_2 = models.CharField(
|
||
# max_length=100,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Address line 2'
|
||
# )
|
||
# city = models.CharField(
|
||
# max_length=50,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='City'
|
||
# )
|
||
# state = models.CharField(
|
||
# max_length=50,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='State/Province'
|
||
# )
|
||
# postal_code = models.CharField(
|
||
# max_length=20,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Postal/ZIP code'
|
||
# )
|
||
# country = models.CharField(
|
||
# max_length=50,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Country'
|
||
# )
|
||
# national_id = models.CharField(
|
||
# max_length=10,
|
||
# blank=True,
|
||
# null=True,
|
||
# unique=True,
|
||
# help_text='National ID'
|
||
# )
|
||
# # Personal Details
|
||
# date_of_birth = models.DateField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Date of birth'
|
||
# )
|
||
# gender = models.CharField(
|
||
# max_length=10,
|
||
# choices=GENDER_CHOICES,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Gender'
|
||
# )
|
||
# marital_status = models.CharField(
|
||
# max_length=20,
|
||
# choices=MARITAL_STATUS_CHOICES,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Marital status'
|
||
# )
|
||
#
|
||
# # Employment Information
|
||
# department = models.ForeignKey(
|
||
# 'Department',
|
||
# on_delete=models.SET_NULL,
|
||
# null=True,
|
||
# blank=True,
|
||
# related_name='employees',
|
||
# help_text='Department'
|
||
# )
|
||
# job_title = models.CharField(
|
||
# max_length=100,
|
||
# help_text='Job title'
|
||
# )
|
||
# employment_type = models.CharField(
|
||
# max_length=20,
|
||
# choices=EMPLOYMENT_TYPE_CHOICES,
|
||
# help_text='Employment type'
|
||
# )
|
||
# employment_status = models.CharField(
|
||
# max_length=20,
|
||
# choices=EMPLOYMENT_STATUS_CHOICES,
|
||
# default='ACTIVE',
|
||
# help_text='Employment status'
|
||
# )
|
||
#
|
||
# # Employment Dates
|
||
# hire_date = models.DateField(
|
||
# help_text='Hire date'
|
||
# )
|
||
# termination_date = models.DateField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Termination date'
|
||
# )
|
||
#
|
||
# # Supervisor Information
|
||
# supervisor = models.ForeignKey(
|
||
# 'self',
|
||
# on_delete=models.SET_NULL,
|
||
# null=True,
|
||
# blank=True,
|
||
# related_name='direct_reports',
|
||
# help_text='Direct supervisor'
|
||
# )
|
||
#
|
||
# # Work Schedule Information
|
||
# standard_hours_per_week = models.DecimalField(
|
||
# max_digits=5,
|
||
# decimal_places=2,
|
||
# default=Decimal('40.00'),
|
||
# help_text='Standard hours per week'
|
||
# )
|
||
# fte_percentage = models.DecimalField(
|
||
# max_digits=5,
|
||
# decimal_places=2,
|
||
# default=Decimal('100.00'),
|
||
# validators=[MinValueValidator(0), MaxValueValidator(100)],
|
||
# help_text='FTE percentage'
|
||
# )
|
||
#
|
||
# # Compensation Information
|
||
# hourly_rate = models.DecimalField(
|
||
# max_digits=10,
|
||
# decimal_places=2,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Hourly rate'
|
||
# )
|
||
# annual_salary = models.DecimalField(
|
||
# max_digits=12,
|
||
# decimal_places=2,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Annual salary'
|
||
# )
|
||
#
|
||
# # Professional Information
|
||
# license_number = models.CharField(
|
||
# max_length=50,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Professional license number'
|
||
# )
|
||
# license_expiry_date = models.DateField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='License expiry date'
|
||
# )
|
||
# certifications = models.JSONField(
|
||
# default=list,
|
||
# help_text='Professional certifications'
|
||
# )
|
||
#
|
||
# # Emergency Contact
|
||
# emergency_contact_name = models.CharField(
|
||
# max_length=100,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Emergency contact name'
|
||
# )
|
||
# emergency_contact_relationship = models.CharField(
|
||
# max_length=50,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Emergency contact relationship'
|
||
# )
|
||
# emergency_contact_phone = models.CharField(
|
||
# max_length=20,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Emergency contact phone'
|
||
# )
|
||
#
|
||
# # Notes
|
||
# notes = models.TextField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Employee notes'
|
||
# )
|
||
#
|
||
# # Metadata
|
||
# created_at = models.DateTimeField(auto_now_add=True)
|
||
# updated_at = models.DateTimeField(auto_now=True)
|
||
# created_by = models.ForeignKey(
|
||
# settings.AUTH_USER_MODEL,
|
||
# on_delete=models.SET_NULL,
|
||
# null=True,
|
||
# blank=True,
|
||
# related_name='created_employees',
|
||
# help_text='User who created the employee record'
|
||
# )
|
||
#
|
||
# class Meta:
|
||
# db_table = 'hr_employee'
|
||
# verbose_name = 'Employee'
|
||
# verbose_name_plural = 'Employees'
|
||
# ordering = ['last_name', 'first_name']
|
||
# indexes = [
|
||
# models.Index(fields=['tenant', 'employment_status']),
|
||
# models.Index(fields=['employee_number']),
|
||
# models.Index(fields=['last_name', 'first_name']),
|
||
# models.Index(fields=['department']),
|
||
# models.Index(fields=['hire_date']),
|
||
# ]
|
||
# unique_together = ['tenant', 'employee_number']
|
||
#
|
||
# def __str__(self):
|
||
# return f"{self.employee_number} - {self.get_full_name()}"
|
||
#
|
||
# def get_full_name(self):
|
||
# """
|
||
# Get employee's full name.
|
||
# """
|
||
# if self.middle_name:
|
||
# return f"{self.first_name} {self.middle_name} {self.last_name}"
|
||
# return f"{self.first_name} {self.last_name}"
|
||
#
|
||
# def get_display_name(self):
|
||
# """
|
||
# Get employee's display name (preferred name if available).
|
||
# """
|
||
# if self.preferred_name:
|
||
# return f"{self.preferred_name} {self.last_name}"
|
||
# return self.get_full_name()
|
||
#
|
||
# @property
|
||
# def age(self):
|
||
# """
|
||
# Calculate employee's age.
|
||
# """
|
||
# if self.date_of_birth:
|
||
# today = date.today()
|
||
# return today.year - self.date_of_birth.year - ((today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day))
|
||
# return None
|
||
#
|
||
# @property
|
||
# def years_of_service(self):
|
||
# """
|
||
# Calculate years of service.
|
||
# """
|
||
# if self.hire_date:
|
||
# end_date = self.termination_date or date.today()
|
||
# return (end_date - self.hire_date).days / 365.25
|
||
# return 0
|
||
#
|
||
# @property
|
||
# def is_license_expired(self):
|
||
# """
|
||
# Check if professional license is expired.
|
||
# """
|
||
# if self.license_expiry_date:
|
||
# return self.license_expiry_date < date.today()
|
||
# return False
|
||
#
|
||
# @property
|
||
# def full_address(self):
|
||
# """
|
||
# Get full address.
|
||
# """
|
||
# parts = [
|
||
# self.address_line_1,
|
||
# self.address_line_2,
|
||
# f"{self.city}, {self.state} {self.postal_code}",
|
||
# self.country
|
||
# ]
|
||
# return "\n".join([part for part in parts if part])
|
||
|
||
|
||
# class TrainingRecord(models.Model):
|
||
# """
|
||
# Training record model for employee training and certifications.
|
||
# """
|
||
#
|
||
# class TrainingType(models.TextChoices):
|
||
# ORIENTATION = 'ORIENTATION', 'Orientation'
|
||
# MANDATORY = 'MANDATORY', 'Mandatory Training'
|
||
# CONTINUING_ED = 'CONTINUING_ED', 'Continuing Education'
|
||
# CERTIFICATION = 'CERTIFICATION', 'Certification'
|
||
# SKILLS = 'SKILLS', 'Skills Training'
|
||
# SAFETY = 'SAFETY', 'Safety Training'
|
||
# COMPLIANCE = 'COMPLIANCE', 'Compliance Training'
|
||
# LEADERSHIP = 'LEADERSHIP', 'Leadership Development'
|
||
# TECHNICAL = 'TECHNICAL', 'Technical Training'
|
||
# OTHER = 'OTHER', 'Other'
|
||
#
|
||
# class TrainingStatus(models.TextChoices):
|
||
# SCHEDULED = 'SCHEDULED', 'Scheduled'
|
||
# IN_PROGRESS = 'IN_PROGRESS', 'In Progress'
|
||
# COMPLETED = 'COMPLETED', 'Completed'
|
||
# CANCELLED = 'CANCELLED', 'Cancelled'
|
||
# NO_SHOW = 'NO_SHOW', 'No Show'
|
||
# FAILED = 'FAILED', 'Failed'
|
||
#
|
||
# # Employee relationship
|
||
# employee = models.ForeignKey(
|
||
# Employee,
|
||
# on_delete=models.CASCADE,
|
||
# related_name='training_records',
|
||
# help_text='Employee'
|
||
# )
|
||
#
|
||
# # Training Information
|
||
# record_id = models.UUIDField(
|
||
# default=uuid.uuid4,
|
||
# unique=True,
|
||
# editable=False,
|
||
# help_text='Unique training record identifier'
|
||
# )
|
||
# training_name = models.CharField(
|
||
# max_length=200,
|
||
# help_text='Training name'
|
||
# )
|
||
# training_description = models.TextField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Training description'
|
||
# )
|
||
#
|
||
# # Training Type
|
||
# training_type = models.CharField(
|
||
# max_length=20,
|
||
# choices=TrainingType.choices,
|
||
# help_text='Training type'
|
||
# )
|
||
#
|
||
# # Training Provider
|
||
# training_provider = models.CharField(
|
||
# max_length=200,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Training provider'
|
||
# )
|
||
# instructor = models.CharField(
|
||
# max_length=100,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Instructor name'
|
||
# )
|
||
#
|
||
# # Training Dates
|
||
# training_date = models.DateField(
|
||
# help_text='Training date'
|
||
# )
|
||
# completion_date = models.DateField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Completion date'
|
||
# )
|
||
# expiry_date = models.DateField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Certification expiry date'
|
||
# )
|
||
#
|
||
# # Training Details
|
||
# duration_hours = models.DecimalField(
|
||
# max_digits=5,
|
||
# decimal_places=2,
|
||
# default=Decimal('0.00'),
|
||
# help_text='Training duration in hours'
|
||
# )
|
||
# credits_earned = models.DecimalField(
|
||
# max_digits=5,
|
||
# decimal_places=2,
|
||
# default=Decimal('0.00'),
|
||
# help_text='Credits earned'
|
||
# )
|
||
#
|
||
# # Training Status
|
||
# status = models.CharField(
|
||
# max_length=20,
|
||
# choices=TrainingStatus.choices,
|
||
# default='SCHEDULED',
|
||
# help_text='Training status'
|
||
# )
|
||
#
|
||
# # Results
|
||
# score = models.DecimalField(
|
||
# max_digits=5,
|
||
# decimal_places=2,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Training score/grade'
|
||
# )
|
||
# passed = models.BooleanField(
|
||
# default=False,
|
||
# help_text='Training passed'
|
||
# )
|
||
# is_certified = models.BooleanField(
|
||
# default=False,
|
||
# help_text='Training is certified'
|
||
# )
|
||
# # Certification Information
|
||
# certificate_number = models.CharField(
|
||
# max_length=50,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Certificate number'
|
||
# )
|
||
# certification_body = models.CharField(
|
||
# max_length=200,
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Certification body'
|
||
# )
|
||
#
|
||
# # Cost Information
|
||
# training_cost = models.DecimalField(
|
||
# max_digits=10,
|
||
# decimal_places=2,
|
||
# default=Decimal('0.00'),
|
||
# help_text='Training cost'
|
||
# )
|
||
#
|
||
# # Notes
|
||
# notes = models.TextField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Training notes'
|
||
# )
|
||
#
|
||
# # Metadata
|
||
# created_at = models.DateTimeField(auto_now_add=True)
|
||
# updated_at = models.DateTimeField(auto_now=True)
|
||
# created_by = models.ForeignKey(
|
||
# settings.AUTH_USER_MODEL,
|
||
# on_delete=models.SET_NULL,
|
||
# null=True,
|
||
# blank=True,
|
||
# related_name='created_training_records',
|
||
# help_text='User who created the training record'
|
||
# )
|
||
#
|
||
# class Meta:
|
||
# db_table = 'hr_training_record'
|
||
# verbose_name = 'Training Record'
|
||
# verbose_name_plural = 'Training Records'
|
||
# ordering = ['-training_date']
|
||
# indexes = [
|
||
# models.Index(fields=['employee', 'training_date']),
|
||
# models.Index(fields=['training_type']),
|
||
# models.Index(fields=['status']),
|
||
# models.Index(fields=['expiry_date']),
|
||
# ]
|
||
#
|
||
# def __str__(self):
|
||
# return f"{self.employee.get_full_name()} - {self.training_name}"
|
||
#
|
||
# @property
|
||
# def tenant(self):
|
||
# """
|
||
# Get tenant from employee.
|
||
# """
|
||
# return self.employee.tenant
|
||
#
|
||
# @property
|
||
# def is_expired(self):
|
||
# """
|
||
# Check if certification is expired.
|
||
# """
|
||
# if self.expiry_date:
|
||
# return self.expiry_date < date.today()
|
||
# return False
|
||
#
|
||
# @property
|
||
# def days_to_expiry(self):
|
||
# """
|
||
# Calculate days to expiry.
|
||
# """
|
||
# if self.expiry_date:
|
||
# return (self.expiry_date - date.today()).days
|
||
# return None
|
||
#
|
||
# @property
|
||
# def is_due_for_renewal(self):
|
||
# """
|
||
# Check if certification is due for renewal (within 30 days).
|
||
# """
|
||
# if self.expiry_date:
|
||
# return (self.expiry_date - date.today()).days <= 30
|
||
# return False
|
||
|
||
|
||
# class Certification(models.Model):
|
||
# tenant = models.ForeignKey('core.Tenant', on_delete=models.PROTECT, related_name='certifications')
|
||
# name = models.CharField(max_length=100)
|
||
# issuer = models.CharField(max_length=150, blank=True, null=True)
|
||
# is_clinical = models.BooleanField(default=False)
|
||
#
|
||
# class Meta:
|
||
# unique_together = [('tenant', 'name')]
|
||
#
|
||
# class EmployeeCertification(models.Model):
|
||
# employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name='employee_certifications')
|
||
# certification = models.ForeignKey(Certification, on_delete=models.PROTECT)
|
||
# credential_id = models.CharField(max_length=100, blank=True, null=True)
|
||
# issued_on = models.DateField(blank=True, null=True)
|
||
# expires_on = models.DateField(blank=True, null=True)
|
||
#
|
||
# class Meta:
|
||
# constraints = [
|
||
# models.UniqueConstraint(fields=['employee', 'certification'], name='uq_employee_cert_once')
|
||
# ]
|
||
|
||
|
||
# class DrugInteraction(models.Model):
|
||
# """
|
||
# Drug interaction model for tracking medication interactions.
|
||
# """
|
||
#
|
||
# class InteractionSeverity(models.TextChoices):
|
||
# MINOR = 'MINOR', 'Minor'
|
||
# MODERATE = 'MODERATE', 'Moderate'
|
||
# MAJOR = 'MAJOR', 'Major'
|
||
# CONTRAINDICATED = 'CONTRAINDICATED', 'Contraindicated'
|
||
#
|
||
# # Tenant relationship
|
||
# tenant = models.ForeignKey(
|
||
# 'core.Tenant',
|
||
# on_delete=models.CASCADE,
|
||
# related_name='drug_interactions',
|
||
# help_text='Organization tenant'
|
||
# )
|
||
#
|
||
# # Patient relationship
|
||
# patient = models.ForeignKey(
|
||
# 'patients.PatientProfile',
|
||
# on_delete=models.CASCADE,
|
||
# related_name='drug_interactions',
|
||
# help_text='Patient'
|
||
# )
|
||
#
|
||
# # Interaction details
|
||
# interaction_id = models.UUIDField(
|
||
# default=uuid.uuid4,
|
||
# unique=True,
|
||
# editable=False,
|
||
# help_text='Unique interaction identifier'
|
||
# )
|
||
#
|
||
# drug1 = models.CharField(
|
||
# max_length=100,
|
||
# help_text='First drug name'
|
||
# )
|
||
# drug2 = models.CharField(
|
||
# max_length=100,
|
||
# help_text='Second drug name'
|
||
# )
|
||
#
|
||
# severity = models.CharField(
|
||
# max_length=20,
|
||
# choices=InteractionSeverity.choices,
|
||
# help_text='Interaction severity'
|
||
# )
|
||
#
|
||
# description = models.TextField(
|
||
# help_text='Interaction description and clinical significance'
|
||
# )
|
||
#
|
||
# # Management
|
||
# management = models.TextField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Management recommendations'
|
||
# )
|
||
#
|
||
# # Status
|
||
# resolved = models.BooleanField(
|
||
# default=False,
|
||
# help_text='Interaction has been resolved'
|
||
# )
|
||
# resolved_at = models.DateTimeField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Date interaction was resolved'
|
||
# )
|
||
# resolved_by = models.ForeignKey(
|
||
# settings.AUTH_USER_MODEL,
|
||
# on_delete=models.SET_NULL,
|
||
# null=True,
|
||
# blank=True,
|
||
# related_name='resolved_interactions',
|
||
# help_text='Provider who resolved the interaction'
|
||
# )
|
||
#
|
||
# # Metadata
|
||
# created_at = models.DateTimeField(auto_now_add=True)
|
||
# updated_at = models.DateTimeField(auto_now=True)
|
||
# detected_at = models.DateTimeField(
|
||
# default=timezone.now,
|
||
# help_text='When interaction was detected'
|
||
# )
|
||
#
|
||
# class Meta:
|
||
# db_table = 'emr_drug_interaction'
|
||
# verbose_name = 'Drug Interaction'
|
||
# verbose_name_plural = 'Drug Interactions'
|
||
# ordering = ['-detected_at']
|
||
# indexes = [
|
||
# models.Index(fields=['tenant', 'resolved']),
|
||
# models.Index(fields=['patient', 'resolved']),
|
||
# models.Index(fields=['severity']),
|
||
# models.Index(fields=['detected_at']),
|
||
# ]
|
||
#
|
||
# def __str__(self):
|
||
# return f"{self.patient.get_full_name()} - {self.drug1} + {self.drug2}"
|
||
|
||
|
||
# class RiskAssessment(models.Model):
|
||
# """
|
||
# Risk assessment model for tracking patient risk scores.
|
||
# """
|
||
#
|
||
# class RiskLevel(models.TextChoices):
|
||
# LOW = 'LOW', 'Low Risk'
|
||
# MODERATE = 'MODERATE', 'Moderate Risk'
|
||
# HIGH = 'HIGH', 'High Risk'
|
||
# CRITICAL = 'CRITICAL', 'Critical Risk'
|
||
#
|
||
# # Tenant relationship
|
||
# tenant = models.ForeignKey(
|
||
# 'core.Tenant',
|
||
# on_delete=models.CASCADE,
|
||
# related_name='risk_assessments',
|
||
# help_text='Organization tenant'
|
||
# )
|
||
#
|
||
# # Patient relationship
|
||
# patient = models.ForeignKey(
|
||
# 'patients.PatientProfile',
|
||
# on_delete=models.CASCADE,
|
||
# related_name='risk_assessments',
|
||
# help_text='Patient'
|
||
# )
|
||
#
|
||
# # Assessment details
|
||
# assessment_id = models.UUIDField(
|
||
# default=uuid.uuid4,
|
||
# unique=True,
|
||
# editable=False,
|
||
# help_text='Unique assessment identifier'
|
||
# )
|
||
#
|
||
# assessment_type = models.CharField(
|
||
# max_length=100,
|
||
# help_text='Type of risk assessment (e.g., Fall Risk, Cardiac Risk)'
|
||
# )
|
||
#
|
||
# score = models.DecimalField(
|
||
# max_digits=5,
|
||
# decimal_places=2,
|
||
# help_text='Risk score value'
|
||
# )
|
||
#
|
||
# risk_level = models.CharField(
|
||
# max_length=20,
|
||
# choices=RiskLevel.choices,
|
||
# help_text='Calculated risk level'
|
||
# )
|
||
#
|
||
# description = models.TextField(
|
||
# blank=True,
|
||
# null=True,
|
||
# help_text='Assessment description and interpretation'
|
||
# )
|
||
#
|
||
# # Assessment details
|
||
# assessment_date = models.DateTimeField(
|
||
# default=timezone.now,
|
||
# help_text='Date and time of assessment'
|
||
# )
|
||
#
|
||
# factors = models.JSONField(
|
||
# default=dict,
|
||
# help_text='Risk factors considered'
|
||
# )
|
||
#
|
||
# recommendations = models.JSONField(
|
||
# default=list,
|
||
# help_text='Recommendations based on assessment'
|
||
# )
|
||
#
|
||
# # Provider information
|
||
# assessed_by = models.ForeignKey(
|
||
# settings.AUTH_USER_MODEL,
|
||
# on_delete=models.CASCADE,
|
||
# related_name='risk_assessments',
|
||
# help_text='Provider who performed assessment'
|
||
# )
|
||
#
|
||
# # Metadata
|
||
# created_at = models.DateTimeField(auto_now_add=True)
|
||
# updated_at = models.DateTimeField(auto_now=True)
|
||
#
|
||
# class Meta:
|
||
# db_table = 'emr_risk_assessment'
|
||
# verbose_name = 'Risk Assessment'
|
||
# verbose_name_plural = 'Risk Assessments'
|
||
# ordering = ['-assessment_date']
|
||
# indexes = [
|
||
# models.Index(fields=['tenant']),
|
||
# models.Index(fields=['patient']),
|
||
# models.Index(fields=['assessment_type']),
|
||
# models.Index(fields=['risk_level']),
|
||
# models.Index(fields=['assessment_date']),
|
||
# ]
|
||
#
|
||
# def __str__(self):
|
||
# return f"{self.patient.get_full_name()} - {self.assessment_type}: {self.score}"
|
||
|
||
|
||
#
|
||
# # ClinicalNote --------------------------------------------------------------------
|
||
#
|
||
# class ClinicalNoteListView(LoginRequiredMixin, TenantMixin, ListView):
|
||
# model = ClinicalNote
|
||
# template_name = 'emr/clinical_note_list.html'
|
||
# context_object_name = 'notes'
|
||
# paginate_by = 20
|
||
#
|
||
#
|
||
# class ClinicalNoteDetailView(LoginRequiredMixin, TenantMixin, DetailView):
|
||
# model = ClinicalNote
|
||
# template_name = 'emr/clinical_note_detail.html'
|
||
# context_object_name = 'note'
|
||
#
|
||
#
|
||
# class ClinicalNoteCreateView(
|
||
# LoginRequiredMixin, FormKwargsMixin,
|
||
# SuccessMessageMixin, CreateView
|
||
# ):
|
||
# model = ClinicalNote
|
||
# form_class = ClinicalNoteForm
|
||
# template_name = 'emr/clinical_note_form.html'
|
||
# success_message = _('Clinical note created successfully.')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.author = self.request.user
|
||
# response = super().form_valid(form)
|
||
# AuditLogEntry.objects.create(
|
||
# tenant=self.request.user.tenant,
|
||
# user=self.request.user,
|
||
# action='CREATE',
|
||
# model_name='ClinicalNote',
|
||
# object_id=str(self.object.pk),
|
||
# changes={'status': 'Clinical note created'}
|
||
# )
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse_lazy('emr:clinical_note_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class ClinicalNoteUpdateView(
|
||
# LoginRequiredMixin, FormKwargsMixin,
|
||
# SuccessMessageMixin, TenantMixin, UpdateView
|
||
# ):
|
||
# model = ClinicalNote
|
||
# form_class = ClinicalNoteForm
|
||
# template_name = 'emr/clinical_note_form.html'
|
||
# success_message = _('Clinical note updated successfully.')
|
||
#
|
||
# def form_valid(self, form):
|
||
# response = super().form_valid(form)
|
||
# AuditLogEntry.objects.create(
|
||
# tenant=self.request.user.tenant,
|
||
# user=self.request.user,
|
||
# action='UPDATE',
|
||
# model_name='ClinicalNote',
|
||
# object_id=str(self.object.pk),
|
||
# changes={'status': 'Clinical note updated'}
|
||
# )
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse_lazy('emr:clinical_note_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class ClinicalNoteDeleteView(
|
||
# LoginRequiredMixin, TenantMixin,
|
||
# SuccessMessageMixin, DeleteView
|
||
# ):
|
||
# model = ClinicalNote
|
||
# template_name = 'emr/clinical_note_confirm_delete.html'
|
||
# success_url = reverse_lazy('emr:clinical_note_list')
|
||
# success_message = _('Clinical note deleted successfully.')
|
||
#
|
||
# def delete(self, request, *args, **kwargs):
|
||
# cn = self.get_object()
|
||
# AuditLogEntry.objects.create(
|
||
# tenant=request.user.tenant,
|
||
# user=request.user,
|
||
# action='DELETE',
|
||
# model_name='ClinicalNote',
|
||
# object_id=str(cn.pk),
|
||
# changes={'status': 'Clinical note deleted'}
|
||
# )
|
||
# messages.success(request, self.success_message)
|
||
# return super().delete(request, *args, **kwargs)
|
||
#
|
||
#
|
||
# NoteTemplate -------------------------------------------------------------------
|
||
|
||
|
||
#
|
||
# # encounters/views.py
|
||
#
|
||
# from django.shortcuts import get_object_or_404, redirect, render
|
||
# from django.urls import reverse_lazy
|
||
# from django.contrib.auth.decorators import login_required
|
||
# from django.contrib.auth.mixins import LoginRequiredMixin
|
||
# from django.contrib import messages
|
||
# from django.views.generic import (
|
||
# TemplateView, ListView, DetailView, CreateView, UpdateView, DeleteView
|
||
# )
|
||
# from django.views.generic.edit import FormMixin
|
||
# from django.contrib.messages.views import SuccessMessageMixin
|
||
# from django.db.models import Q, Avg
|
||
# from django.utils import timezone
|
||
# from django.http import JsonResponse
|
||
#
|
||
# from .models import *
|
||
# from .forms import *
|
||
# from core.models import AuditLogEntry
|
||
# from patients.models import PatientProfile
|
||
# from django.utils.translation import gettext_lazy as _
|
||
#
|
||
#
|
||
# # Mixins -------------------------------------------------------------------------
|
||
#
|
||
# class TenantMixin:
|
||
# def get_queryset(self):
|
||
# qs = super().get_queryset()
|
||
# tenant = getattr(self.request.user, 'tenant', None)
|
||
# if tenant and not self.request.user.is_superuser:
|
||
# # Models with patient FK:
|
||
# if hasattr(qs.model, 'patient'):
|
||
# return qs.filter(patient__tenant=tenant)
|
||
# # NoteTemplate uses tenant directly:
|
||
# return qs.filter(tenant=tenant)
|
||
# return qs
|
||
#
|
||
# def get_object(self, queryset=None):
|
||
# qs = queryset or self.get_queryset()
|
||
# return super().get_object(qs)
|
||
#
|
||
#
|
||
# class FormKwargsMixin:
|
||
# def get_form_kwargs(self):
|
||
# kw = super().get_form_kwargs()
|
||
# kw['user'] = self.request.user
|
||
# kw['tenant'] = getattr(self.request.user, 'tenant', None)
|
||
# return kw
|
||
#
|
||
#
|
||
# # Dashboard ----------------------------------------------------------------------
|
||
#
|
||
# class DashboardView(LoginRequiredMixin, TemplateView):
|
||
# template_name = 'emr/dashboard.html'
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# ctx = super().get_context_data(**kwargs)
|
||
# tenant = getattr(self.request.user, 'tenant', None)
|
||
# today = timezone.now().date()
|
||
# week_ago = today - timezone.timedelta(days=7)
|
||
#
|
||
# enc = Encounter.objects.filter(patient__tenant=tenant)
|
||
# vs = VitalSigns.objects.filter(encounter__patient__tenant=tenant)
|
||
# pb = ProblemList.objects.filter(patient__tenant=tenant)
|
||
# cp = CarePlan.objects.filter(patient__tenant=tenant)
|
||
# cn = ClinicalNote.objects.filter(encounter__patient__tenant=tenant)
|
||
#
|
||
# ctx.update({
|
||
# 'total_encounters': enc.count(),
|
||
# 'encounters_today': enc.filter(scheduled_datetime__date=today).count(),
|
||
# 'encounters_this_week': enc.filter(scheduled_datetime__date__gte=week_ago).count(),
|
||
# 'active_encounters': enc.filter(status='IN_PROGRESS').count(),
|
||
#
|
||
# 'total_vital_signs': vs.count(),
|
||
# 'vital_signs_today': vs.filter(recorded_at__date=today).count(),
|
||
# 'avg_temp_week': vs.filter(recorded_at__date__gte=week_ago).aggregate(Avg('temperature'))['temperature__avg'],
|
||
# 'avg_hr_week': vs.filter(recorded_at__date__gte=week_ago).aggregate(Avg('heart_rate'))['heart_rate__avg'],
|
||
#
|
||
# 'total_problems': pb.count(),
|
||
# 'active_problems': pb.filter(status='ACTIVE').count(),
|
||
# 'resolved_problems': pb.filter(status='RESOLVED').count(),
|
||
#
|
||
# 'total_care_plans': cp.count(),
|
||
# 'active_care_plans': cp.filter(status='ACTIVE').count(),
|
||
# 'completed_care_plans': cp.filter(status='COMPLETED').count(),
|
||
#
|
||
# 'total_notes': cn.count(),
|
||
# 'notes_today': cn.filter(created_at__date=today).count(),
|
||
#
|
||
# 'recent_encounters': enc.select_related('patient','provider').order_by('-scheduled_datetime')[:5],
|
||
# 'recent_vitals': vs.select_related('encounter','recorded_by').order_by('-recorded_at')[:5],
|
||
# 'recent_notes': cn.select_related('encounter','author').order_by('-created_at')[:5],
|
||
# })
|
||
# return ctx
|
||
#
|
||
#
|
||
# # Encounter ----------------------------------------------------------------------
|
||
#
|
||
# class EncounterListView(LoginRequiredMixin, TenantMixin, FormMixin, ListView):
|
||
# model = Encounter
|
||
# template_name = 'emr/encounter_list.html'
|
||
# context_object_name = 'encounters'
|
||
# paginate_by = 20
|
||
# form_class = EMRSearchForm
|
||
#
|
||
# def get_queryset(self):
|
||
# qs = super().get_queryset().select_related('patient','provider').order_by('-scheduled_datetime')
|
||
# if self.request.GET:
|
||
# form = self.get_form()
|
||
# if form.is_valid():
|
||
# cd = form.cleaned_data
|
||
# if cd.get('search'):
|
||
# qs = qs.filter(
|
||
# Q(patient__first_name__icontains=cd['search']) |
|
||
# Q(patient__last_name__icontains=cd['search']) |
|
||
# Q(chief_complaint__icontains=cd['search'])
|
||
# )
|
||
# for fld in ('patient','provider','encounter_type','status'):
|
||
# if cd.get(fld):
|
||
# qs = qs.filter(**{fld: cd[fld]})
|
||
# if cd.get('date_from'):
|
||
# qs = qs.filter(scheduled_datetime__date__gte=cd['date_from'])
|
||
# if cd.get('date_to'):
|
||
# qs = qs.filter(scheduled_datetime__date__lte=cd['date_to'])
|
||
# return qs
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# ctx = super().get_context_data(**kwargs)
|
||
# ctx['search_form'] = self.get_form()
|
||
# ctx['total_count'] = self.get_queryset().count()
|
||
# return ctx
|
||
#
|
||
#
|
||
# class EncounterDetailView(LoginRequiredMixin, TenantMixin, DetailView):
|
||
# model = Encounter
|
||
# template_name = 'emr/encounter_detail.html'
|
||
# context_object_name = 'encounter'
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# ctx = super().get_context_data(**kwargs)
|
||
# enc = self.object
|
||
# ctx.update({
|
||
# 'vital_signs': VitalSigns.objects.filter(encounter=enc).order_by('-recorded_at'),
|
||
# 'clinical_notes': ClinicalNote.objects.filter(encounter=enc).order_by('-created_at'),
|
||
# 'problems': ProblemList.objects.filter(patient=enc.patient, status='ACTIVE'),
|
||
# 'care_plans': CarePlan.objects.filter(patient=enc.patient, status='ACTIVE'),
|
||
# })
|
||
# return ctx
|
||
#
|
||
#
|
||
# class EncounterCreateView(
|
||
# LoginRequiredMixin, FormKwargsMixin,
|
||
# SuccessMessageMixin, CreateView
|
||
# ):
|
||
# model = Encounter
|
||
# form_class = EncounterForm
|
||
# template_name = 'emr/encounter_form.html'
|
||
# success_message = _('Encounter for %(patient)s created successfully.')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# response = super().form_valid(form)
|
||
# AuditLogEntry.objects.create(
|
||
# tenant=self.request.user.tenant,
|
||
# user=self.request.user,
|
||
# action='CREATE',
|
||
# model_name='Encounter',
|
||
# object_id=str(self.object.pk),
|
||
# changes={'status': 'Encounter created'}
|
||
# )
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse_lazy('emr:encounter_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class EncounterUpdateView(
|
||
# LoginRequiredMixin, FormKwargsMixin,
|
||
# SuccessMessageMixin, TenantMixin, UpdateView
|
||
# ):
|
||
# model = Encounter
|
||
# form_class = EncounterForm
|
||
# template_name = 'emr/encounter_form.html'
|
||
# success_message = _('Encounter for %(patient)s updated successfully.')
|
||
#
|
||
# def dispatch(self, request, *args, **kwargs):
|
||
# enc = self.get_object()
|
||
# if enc.status == 'COMPLETED' and not request.user.is_superuser:
|
||
# messages.error(request, _('Cannot modify a completed encounter.'))
|
||
# return redirect('emr:encounter_detail', pk=enc.pk)
|
||
# return super().dispatch(request, *args, **kwargs)
|
||
#
|
||
# def form_valid(self, form):
|
||
# response = super().form_valid(form)
|
||
# AuditLogEntry.objects.create(
|
||
# tenant=self.request.user.tenant,
|
||
# user=self.request.user,
|
||
# action='UPDATE',
|
||
# model_name='Encounter',
|
||
# object_id=str(self.object.pk),
|
||
# changes={'status': 'Encounter updated'}
|
||
# )
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse_lazy('emr:encounter_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class EncounterDeleteView(
|
||
# LoginRequiredMixin, TenantMixin,
|
||
# SuccessMessageMixin, DeleteView
|
||
# ):
|
||
# model = Encounter
|
||
# template_name = 'emr/encounter_confirm_delete.html'
|
||
# success_url = reverse_lazy('emr:encounter_list')
|
||
# success_message = _('Encounter deleted successfully.')
|
||
#
|
||
# def delete(self, request, *args, **kwargs):
|
||
# enc = self.get_object()
|
||
# AuditLogEntry.objects.create(
|
||
# tenant=request.user.tenant,
|
||
# user=request.user,
|
||
# action='DELETE',
|
||
# model_name='Encounter',
|
||
# object_id=str(enc.pk),
|
||
# changes={'status': 'Encounter deleted'}
|
||
# )
|
||
# messages.success(request, self.success_message)
|
||
# return super().delete(request, *args, **kwargs)
|
||
#
|
||
#
|
||
# # VitalSigns ----------------------------------------------------------------------
|
||
#
|
||
# class VitalSignsListView(LoginRequiredMixin, TenantMixin, ListView):
|
||
# model = VitalSigns
|
||
# template_name = 'emr/vital_signs_list.html'
|
||
# context_object_name = 'vital_signs'
|
||
# paginate_by = 20
|
||
#
|
||
# def get_queryset(self):
|
||
# qs = super().get_queryset().select_related('encounter','recorded_by').order_by('-recorded_at')
|
||
# # (Search/filter logic would use a VitalSignsSearchForm — omitted for brevity)
|
||
# return qs
|
||
#
|
||
|
||
# class VitalSignsUpdateView(LoginRequiredMixin, FormKwargsMixin, SuccessMessageMixin, TenantMixin, UpdateView):
|
||
# model = VitalSigns
|
||
# form_class = VitalSignsForm
|
||
# template_name = 'emr/vital_signs_form.html'
|
||
# success_message = _('Vital signs updated successfully.')
|
||
# # implement form_valid, get_success_url, etc.
|
||
#
|
||
# class VitalSignsDeleteView(LoginRequiredMixin, TenantMixin, SuccessMessageMixin, DeleteView):
|
||
# model = VitalSigns
|
||
# template_name = 'emr/vital_signs_confirm_delete.html'
|
||
# success_url = reverse_lazy('emr:vitalsigns_list')
|
||
# success_message = _('Vital signs entry deleted.')
|
||
|
||
|
||
#
|
||
# """
|
||
# Radiology app views with healthcare-focused CRUD operations.
|
||
# Implements appropriate access patterns for radiology and imaging workflows.
|
||
# """
|
||
#
|
||
# 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, PermissionRequiredMixin
|
||
# from django.views.generic import (
|
||
# ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView
|
||
# )
|
||
# from django.http import JsonResponse, HttpResponse
|
||
# from django.db.models import Q, Count, Avg, Sum, F
|
||
# from django.utils import timezone
|
||
# from django.contrib import messages
|
||
# from django.urls import reverse_lazy, reverse
|
||
# from django.core.paginator import Paginator
|
||
# from django.template.loader import render_to_string
|
||
# from datetime import datetime, timedelta, date
|
||
# import json
|
||
#
|
||
# from core.utils import AuditLogger
|
||
# from .models import (
|
||
# ImagingOrder, ImagingStudy, ImagingSeries, DICOMImage,
|
||
# RadiologyReport, ReportTemplate
|
||
# )
|
||
# from .forms import (
|
||
# ImagingOrderForm, ImagingStudyForm, RadiologyReportForm,
|
||
# ReportTemplateForm, ImagingSeriesForm
|
||
# )
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # DASHBOARD AND OVERVIEW VIEWS
|
||
# # ============================================================================
|
||
#
|
||
# class RadiologyDashboardView(LoginRequiredMixin, TemplateView):
|
||
# """
|
||
# Main radiology dashboard with key metrics and recent activity.
|
||
# """
|
||
# template_name = 'radiology/dashboard.html'
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# tenant = self.request.user.tenant
|
||
# today = timezone.now().date()
|
||
#
|
||
# # Dashboard statistics
|
||
# context.update({
|
||
# 'pending_orders': ImagingOrder.objects.filter(
|
||
# tenant=tenant,
|
||
# status='PENDING'
|
||
# ).count(),
|
||
# 'scheduled_studies': ImagingStudy.objects.filter(
|
||
# tenant=tenant,
|
||
# status='SCHEDULED'
|
||
# ).count(),
|
||
# 'in_progress_studies': ImagingStudy.objects.filter(
|
||
# tenant=tenant,
|
||
# status='IN_PROGRESS'
|
||
# ).count(),
|
||
# 'studies_completed_today': ImagingStudy.objects.filter(
|
||
# tenant=tenant,
|
||
# completed_datetime__date=today,
|
||
# status='COMPLETED'
|
||
# ).count(),
|
||
# 'reports_pending': RadiologyReport.objects.filter(
|
||
# tenant=tenant,
|
||
# status='DRAFT'
|
||
# ).count(),
|
||
# 'reports_signed_today': RadiologyReport.objects.filter(
|
||
# tenant=tenant,
|
||
# signed_datetime__date=today,
|
||
# status='SIGNED'
|
||
# ).count(),
|
||
# 'critical_findings': RadiologyReport.objects.filter(
|
||
# tenant=tenant,
|
||
# has_critical_findings=True,
|
||
# status='SIGNED',
|
||
# signed_datetime__date=today
|
||
# ).count(),
|
||
# 'total_images_today': DICOMImage.objects.filter(
|
||
# tenant=tenant,
|
||
# created_at__date=today
|
||
# ).count(),
|
||
# })
|
||
#
|
||
# # Recent orders
|
||
# context['recent_orders'] = ImagingOrder.objects.filter(
|
||
# tenant=tenant
|
||
# ).select_related('patient', 'ordering_provider').order_by('-order_datetime')[:10]
|
||
#
|
||
# # Recent studies
|
||
# context['recent_studies'] = ImagingStudy.objects.filter(
|
||
# tenant=tenant
|
||
# ).select_related('order__patient').order_by('-study_datetime')[:10]
|
||
#
|
||
# # Recent reports
|
||
# context['recent_reports'] = RadiologyReport.objects.filter(
|
||
# tenant=tenant,
|
||
# status='SIGNED'
|
||
# ).select_related('study__order__patient').order_by('-signed_datetime')[:10]
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # REPORT TEMPLATE VIEWS (FULL CRUD - Master Data)
|
||
# # ============================================================================
|
||
#
|
||
# class ReportTemplateListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all radiology report templates with filtering and search.
|
||
# """
|
||
# model = ReportTemplate
|
||
# template_name = 'radiology/report_template_list.html'
|
||
# context_object_name = 'report_templates'
|
||
# paginate_by = 25
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = ReportTemplate.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Search functionality
|
||
# search = self.request.GET.get('search')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(template_name__icontains=search) |
|
||
# Q(modality__icontains=search) |
|
||
# Q(body_part__icontains=search)
|
||
# )
|
||
#
|
||
# # Filter by modality
|
||
# modality = self.request.GET.get('modality')
|
||
# if modality:
|
||
# queryset = queryset.filter(modality=modality)
|
||
#
|
||
# # Filter by active status
|
||
# active_only = self.request.GET.get('active_only')
|
||
# if active_only:
|
||
# queryset = queryset.filter(is_active=True)
|
||
#
|
||
# return queryset.order_by('template_name')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'modalities': ReportTemplate._meta.get_field('modality').choices,
|
||
# 'search_query': self.request.GET.get('search', ''),
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class ReportTemplateDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about a report template.
|
||
# """
|
||
# model = ReportTemplate
|
||
# template_name = 'radiology/report_template_detail.html'
|
||
# context_object_name = 'report_template'
|
||
#
|
||
# def get_queryset(self):
|
||
# return ReportTemplate.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# template = self.object
|
||
#
|
||
# # Get recent reports using this template
|
||
# context['recent_reports'] = RadiologyReport.objects.filter(
|
||
# template=template,
|
||
# tenant=self.request.user.tenant
|
||
# ).select_related('study__order__patient').order_by('-created_at')[:10]
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class ReportTemplateCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create a new report template.
|
||
# """
|
||
# model = ReportTemplate
|
||
# form_class = ReportTemplateForm
|
||
# template_name = 'radiology/report_template_form.html'
|
||
# permission_required = 'radiology.add_reporttemplate'
|
||
# success_url = reverse_lazy('radiology:report_template_list')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.created_by = self.request.user
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='REPORT_TEMPLATE_CREATED',
|
||
# model='ReportTemplate',
|
||
# object_id=str(self.object.id),
|
||
# details={
|
||
# 'template_name': self.object.template_name,
|
||
# 'modality': self.object.modality
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, f'Report template "{self.object.template_name}" created successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# class ReportTemplateUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update an existing report template.
|
||
# """
|
||
# model = ReportTemplate
|
||
# form_class = ReportTemplateForm
|
||
# template_name = 'radiology/report_template_form.html'
|
||
# permission_required = 'radiology.change_reporttemplate'
|
||
#
|
||
# def get_queryset(self):
|
||
# return ReportTemplate.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse('radiology:report_template_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
# def form_valid(self, form):
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='REPORT_TEMPLATE_UPDATED',
|
||
# model='ReportTemplate',
|
||
# object_id=str(self.object.id),
|
||
# details={
|
||
# 'template_name': self.object.template_name,
|
||
# 'changes': form.changed_data
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, f'Report template "{self.object.template_name}" updated successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# class ReportTemplateDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
||
# """
|
||
# Delete a report template (soft delete by deactivating).
|
||
# """
|
||
# model = ReportTemplate
|
||
# template_name = 'radiology/report_template_confirm_delete.html'
|
||
# permission_required = 'radiology.delete_reporttemplate'
|
||
# success_url = reverse_lazy('radiology:report_template_list')
|
||
#
|
||
# def get_queryset(self):
|
||
# return ReportTemplate.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def delete(self, request, *args, **kwargs):
|
||
# self.object = self.get_object()
|
||
#
|
||
# # Soft delete by deactivating instead of actual deletion
|
||
# self.object.is_active = False
|
||
# self.object.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='REPORT_TEMPLATE_DEACTIVATED',
|
||
# model='ReportTemplate',
|
||
# object_id=str(self.object.id),
|
||
# details={'template_name': self.object.template_name}
|
||
# )
|
||
#
|
||
# messages.success(request, f'Report template "{self.object.template_name}" deactivated successfully.')
|
||
# return redirect(self.success_url)
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # IMAGING ORDER VIEWS (RESTRICTED CRUD - Clinical Orders)
|
||
# # ============================================================================
|
||
#
|
||
# class ImagingOrderListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all imaging orders with filtering and search.
|
||
# """
|
||
# model = ImagingOrder
|
||
# template_name = 'radiology/imaging_order_list.html'
|
||
# context_object_name = 'imaging_orders'
|
||
# paginate_by = 25
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = ImagingOrder.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Search functionality
|
||
# search = self.request.GET.get('search')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(patient__first_name__icontains=search) |
|
||
# Q(patient__last_name__icontains=search) |
|
||
# Q(patient__mrn__icontains=search) |
|
||
# Q(study_description__icontains=search)
|
||
# )
|
||
#
|
||
# # Filter by status
|
||
# status = self.request.GET.get('status')
|
||
# if status:
|
||
# queryset = queryset.filter(status=status)
|
||
#
|
||
# # Filter by modality
|
||
# modality = self.request.GET.get('modality')
|
||
# if modality:
|
||
# queryset = queryset.filter(modality=modality)
|
||
#
|
||
# # Filter by priority
|
||
# priority = self.request.GET.get('priority')
|
||
# if priority:
|
||
# queryset = queryset.filter(priority=priority)
|
||
#
|
||
# # Filter by date range
|
||
# date_from = self.request.GET.get('date_from')
|
||
# date_to = self.request.GET.get('date_to')
|
||
# if date_from:
|
||
# queryset = queryset.filter(order_datetime__date__gte=date_from)
|
||
# if date_to:
|
||
# queryset = queryset.filter(order_datetime__date__lte=date_to)
|
||
#
|
||
# return queryset.select_related(
|
||
# 'patient', 'ordering_provider'
|
||
# ).order_by('-order_datetime')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'statuses': ImagingOrder._meta.get_field('status').choices,
|
||
# 'modalities': ImagingOrder._meta.get_field('modality').choices,
|
||
# 'priorities': ImagingOrder._meta.get_field('priority').choices,
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class ImagingOrderDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about an imaging order.
|
||
# """
|
||
# model = ImagingOrder
|
||
# template_name = 'radiology/imaging_order_detail.html'
|
||
# context_object_name = 'imaging_order'
|
||
#
|
||
# def get_queryset(self):
|
||
# return ImagingOrder.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# imaging_order = self.object
|
||
#
|
||
# # Get studies for this order
|
||
# context['studies'] = imaging_order.studies.all().order_by('-study_datetime')
|
||
#
|
||
# # Get reports for this order
|
||
# context['reports'] = RadiologyReport.objects.filter(
|
||
# study__order=imaging_order,
|
||
# tenant=self.request.user.tenant
|
||
# ).order_by('-created_at')
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class ImagingOrderCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create a new imaging order.
|
||
# """
|
||
# model = ImagingOrder
|
||
# form_class = ImagingOrderForm
|
||
# template_name = 'radiology/imaging_order_form.html'
|
||
# permission_required = 'radiology.add_imagingorder'
|
||
# success_url = reverse_lazy('radiology:imaging_order_list')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.ordering_provider = self.request.user
|
||
# form.instance.order_datetime = timezone.now()
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='IMAGING_ORDER_CREATED',
|
||
# model='ImagingOrder',
|
||
# object_id=str(self.object.order_id),
|
||
# details={
|
||
# 'patient_name': f"{self.object.patient.first_name} {self.object.patient.last_name}",
|
||
# 'modality': self.object.modality,
|
||
# 'study_description': self.object.study_description,
|
||
# 'priority': self.object.priority
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'Imaging order created successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# class ImagingOrderUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update imaging order (limited to status and notes only).
|
||
# """
|
||
# model = ImagingOrder
|
||
# fields = ['status', 'notes'] # Restricted fields for clinical orders
|
||
# template_name = 'radiology/imaging_order_update_form.html'
|
||
# permission_required = 'radiology.change_imagingorder'
|
||
#
|
||
# def get_queryset(self):
|
||
# return ImagingOrder.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse('radiology:imaging_order_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
# def form_valid(self, form):
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='IMAGING_ORDER_UPDATED',
|
||
# model='ImagingOrder',
|
||
# object_id=str(self.object.order_id),
|
||
# details={
|
||
# 'patient_name': f"{self.object.patient.first_name} {self.object.patient.last_name}",
|
||
# 'changes': form.changed_data
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'Imaging order updated successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # IMAGING STUDY VIEWS (RESTRICTED CRUD - Clinical Data)
|
||
# # ============================================================================
|
||
#
|
||
# class ImagingStudyListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all imaging studies with filtering and search.
|
||
# """
|
||
# model = ImagingStudy
|
||
# template_name = 'radiology/imaging_study_list.html'
|
||
# context_object_name = 'imaging_studies'
|
||
# paginate_by = 25
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = ImagingStudy.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Search functionality
|
||
# search = self.request.GET.get('search')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(study_id__icontains=search) |
|
||
# Q(order__patient__first_name__icontains=search) |
|
||
# Q(order__patient__last_name__icontains=search) |
|
||
# Q(order__patient__mrn__icontains=search)
|
||
# )
|
||
#
|
||
# # Filter by status
|
||
# status = self.request.GET.get('status')
|
||
# if status:
|
||
# queryset = queryset.filter(status=status)
|
||
#
|
||
# # Filter by modality
|
||
# modality = self.request.GET.get('modality')
|
||
# if modality:
|
||
# queryset = queryset.filter(order__modality=modality)
|
||
#
|
||
# return queryset.select_related('order__patient').order_by('-study_datetime')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'statuses': ImagingStudy._meta.get_field('status').choices,
|
||
# 'modalities': ImagingOrder._meta.get_field('modality').choices,
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class ImagingStudyDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about an imaging study.
|
||
# """
|
||
# model = ImagingStudy
|
||
# template_name = 'radiology/imaging_study_detail.html'
|
||
# context_object_name = 'imaging_study'
|
||
#
|
||
# def get_queryset(self):
|
||
# return ImagingStudy.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# imaging_study = self.object
|
||
#
|
||
# # Get series for this study
|
||
# context['series'] = imaging_study.series.all().order_by('series_number')
|
||
#
|
||
# # Get reports for this study
|
||
# context['reports'] = imaging_study.reports.all().order_by('-created_at')
|
||
#
|
||
# # Get total image count
|
||
# context['total_images'] = DICOMImage.objects.filter(
|
||
# series__study=imaging_study,
|
||
# tenant=self.request.user.tenant
|
||
# ).count()
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class ImagingStudyCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create a new imaging study.
|
||
# """
|
||
# model = ImagingStudy
|
||
# form_class = ImagingStudyForm
|
||
# template_name = 'radiology/imaging_study_form.html'
|
||
# permission_required = 'radiology.add_imagingstudy'
|
||
# success_url = reverse_lazy('radiology:imaging_study_list')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.technologist = self.request.user
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='IMAGING_STUDY_CREATED',
|
||
# model='ImagingStudy',
|
||
# object_id=str(self.object.study_id),
|
||
# details={
|
||
# 'patient_name': f"{self.object.order.patient.first_name} {self.object.order.patient.last_name}",
|
||
# 'modality': self.object.order.modality
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'Imaging study created successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# class ImagingStudyUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update imaging study (limited to status and technical notes).
|
||
# """
|
||
# model = ImagingStudy
|
||
# fields = ['status', 'technical_notes'] # Restricted fields
|
||
# template_name = 'radiology/imaging_study_update_form.html'
|
||
# permission_required = 'radiology.change_imagingstudy'
|
||
#
|
||
# def get_queryset(self):
|
||
# return ImagingStudy.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse('radiology:imaging_study_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
# def form_valid(self, form):
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='IMAGING_STUDY_UPDATED',
|
||
# model='ImagingStudy',
|
||
# object_id=str(self.object.study_id),
|
||
# details={
|
||
# 'patient_name': f"{self.object.order.patient.first_name} {self.object.order.patient.last_name}",
|
||
# 'changes': form.changed_data
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'Imaging study updated successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # IMAGING SERIES VIEWS (RESTRICTED CRUD - Clinical Data)
|
||
# # ============================================================================
|
||
#
|
||
# class ImagingSeriesListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all imaging series with filtering and search.
|
||
# """
|
||
# model = ImagingSeries
|
||
# template_name = 'radiology/imaging_series_list.html'
|
||
# context_object_name = 'imaging_series'
|
||
# paginate_by = 25
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = ImagingSeries.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Filter by study
|
||
# study_id = self.request.GET.get('study')
|
||
# if study_id:
|
||
# queryset = queryset.filter(study_id=study_id)
|
||
#
|
||
# # Search functionality
|
||
# search = self.request.GET.get('search')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(series_description__icontains=search) |
|
||
# Q(study__order__patient__first_name__icontains=search) |
|
||
# Q(study__order__patient__last_name__icontains=search)
|
||
# )
|
||
#
|
||
# return queryset.select_related('study__order__patient').order_by('-created_at')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'studies': ImagingStudy.objects.filter(
|
||
# tenant=self.request.user.tenant
|
||
# ).select_related('order__patient').order_by('-study_datetime')[:50],
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class ImagingSeriesDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about an imaging series.
|
||
# """
|
||
# model = ImagingSeries
|
||
# template_name = 'radiology/imaging_series_detail.html'
|
||
# context_object_name = 'imaging_series'
|
||
#
|
||
# def get_queryset(self):
|
||
# return ImagingSeries.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# imaging_series = self.object
|
||
#
|
||
# # Get images for this series
|
||
# context['images'] = imaging_series.images.all().order_by('instance_number')
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class ImagingSeriesCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create a new imaging series.
|
||
# """
|
||
# model = ImagingSeries
|
||
# form_class = ImagingSeriesForm
|
||
# template_name = 'radiology/imaging_series_form.html'
|
||
# permission_required = 'radiology.add_imagingseries'
|
||
# success_url = reverse_lazy('radiology:imaging_series_list')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='IMAGING_SERIES_CREATED',
|
||
# model='ImagingSeries',
|
||
# object_id=str(self.object.series_id),
|
||
# details={
|
||
# 'series_description': self.object.series_description,
|
||
# 'study_id': str(self.object.study.study_id)
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'Imaging series created successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # DICOM IMAGE VIEWS (READ-ONLY - System Generated)
|
||
# # ============================================================================
|
||
#
|
||
# class DICOMImageListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all DICOM images with filtering and search.
|
||
# """
|
||
# model = DICOMImage
|
||
# template_name = 'radiology/dicom_image_list.html'
|
||
# context_object_name = 'dicom_images'
|
||
# paginate_by = 50
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = DICOMImage.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Filter by series
|
||
# series_id = self.request.GET.get('series')
|
||
# if series_id:
|
||
# queryset = queryset.filter(series_id=series_id)
|
||
#
|
||
# # Filter by study
|
||
# study_id = self.request.GET.get('study')
|
||
# if study_id:
|
||
# queryset = queryset.filter(series__study_id=study_id)
|
||
#
|
||
# return queryset.select_related('series__study__order__patient').order_by('instance_number')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'series': ImagingSeries.objects.filter(
|
||
# tenant=self.request.user.tenant
|
||
# ).select_related('study__order__patient').order_by('-created_at')[:50],
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class DICOMImageDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about a DICOM image.
|
||
# """
|
||
# model = DICOMImage
|
||
# template_name = 'radiology/dicom_image_detail.html'
|
||
# context_object_name = 'dicom_image'
|
||
#
|
||
# def get_queryset(self):
|
||
# return DICOMImage.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # RADIOLOGY REPORT VIEWS (APPEND-ONLY - Clinical Records)
|
||
# # ============================================================================
|
||
#
|
||
# class RadiologyReportListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all radiology reports with filtering and search.
|
||
# """
|
||
# model = RadiologyReport
|
||
# template_name = 'radiology/radiology_report_list.html'
|
||
# context_object_name = 'radiology_reports'
|
||
# paginate_by = 25
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = RadiologyReport.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Search functionality
|
||
# search = self.request.GET.get('search')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(study__order__patient__first_name__icontains=search) |
|
||
# Q(study__order__patient__last_name__icontains=search) |
|
||
# Q(study__order__patient__mrn__icontains=search) |
|
||
# Q(findings__icontains=search) |
|
||
# Q(impression__icontains=search)
|
||
# )
|
||
#
|
||
# # Filter by status
|
||
# status = self.request.GET.get('status')
|
||
# if status:
|
||
# queryset = queryset.filter(status=status)
|
||
#
|
||
# # Filter by critical findings
|
||
# critical_only = self.request.GET.get('critical_only')
|
||
# if critical_only:
|
||
# queryset = queryset.filter(has_critical_findings=True)
|
||
#
|
||
# # Filter by radiologist
|
||
# radiologist_id = self.request.GET.get('radiologist')
|
||
# if radiologist_id:
|
||
# queryset = queryset.filter(radiologist_id=radiologist_id)
|
||
#
|
||
# return queryset.select_related(
|
||
# 'study__order__patient', 'radiologist', 'template'
|
||
# ).order_by('-created_at')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'statuses': RadiologyReport._meta.get_field('status').choices,
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class RadiologyReportDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about a radiology report.
|
||
# """
|
||
# model = RadiologyReport
|
||
# template_name = 'radiology/radiology_report_detail.html'
|
||
# context_object_name = 'radiology_report'
|
||
#
|
||
# def get_queryset(self):
|
||
# return RadiologyReport.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
#
|
||
# class RadiologyReportCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create a new radiology report.
|
||
# """
|
||
# model = RadiologyReport
|
||
# form_class = RadiologyReportForm
|
||
# template_name = 'radiology/radiology_report_form.html'
|
||
# permission_required = 'radiology.add_radiologyreport'
|
||
# success_url = reverse_lazy('radiology:radiology_report_list')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.radiologist = self.request.user
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='RADIOLOGY_REPORT_CREATED',
|
||
# model='RadiologyReport',
|
||
# object_id=str(self.object.report_id),
|
||
# details={
|
||
# 'patient_name': f"{self.object.study.order.patient.first_name} {self.object.study.order.patient.last_name}",
|
||
# 'has_critical_findings': self.object.has_critical_findings
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'Radiology report created successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# # Note: No UpdateView or DeleteView for RadiologyReport - Append-only for clinical records
|
||
# # Reports can only be amended through addendum process after signing
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # HTMX VIEWS FOR REAL-TIME UPDATES
|
||
# # ============================================================================
|
||
#
|
||
# @login_required
|
||
# def radiology_stats(request):
|
||
# """
|
||
# HTMX endpoint for radiology statistics.
|
||
# """
|
||
# tenant = request.user.tenant
|
||
# today = timezone.now().date()
|
||
#
|
||
# stats = {
|
||
# 'pending_orders': ImagingOrder.objects.filter(
|
||
# tenant=tenant,
|
||
# status='PENDING'
|
||
# ).count(),
|
||
# 'scheduled_studies': ImagingStudy.objects.filter(
|
||
# tenant=tenant,
|
||
# status='SCHEDULED'
|
||
# ).count(),
|
||
# 'in_progress_studies': ImagingStudy.objects.filter(
|
||
# tenant=tenant,
|
||
# status='IN_PROGRESS'
|
||
# ).count(),
|
||
# 'reports_pending': RadiologyReport.objects.filter(
|
||
# tenant=tenant,
|
||
# status='DRAFT'
|
||
# ).count(),
|
||
# 'critical_findings': RadiologyReport.objects.filter(
|
||
# tenant=tenant,
|
||
# has_critical_findings=True,
|
||
# status='SIGNED',
|
||
# signed_datetime__date=today
|
||
# ).count(),
|
||
# }
|
||
#
|
||
# return render(request, 'radiology/partials/radiology_stats.html', {'stats': stats})
|
||
#
|
||
#
|
||
# @login_required
|
||
# def order_search(request):
|
||
# """
|
||
# HTMX endpoint for imaging order search.
|
||
# """
|
||
# search = request.GET.get('search', '')
|
||
# status = request.GET.get('status', '')
|
||
# modality = request.GET.get('modality', '')
|
||
#
|
||
# queryset = ImagingOrder.objects.filter(tenant=request.user.tenant)
|
||
#
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(patient__first_name__icontains=search) |
|
||
# Q(patient__last_name__icontains=search) |
|
||
# Q(patient__mrn__icontains=search) |
|
||
# Q(study_description__icontains=search)
|
||
# )
|
||
#
|
||
# if status:
|
||
# queryset = queryset.filter(status=status)
|
||
#
|
||
# if modality:
|
||
# queryset = queryset.filter(modality=modality)
|
||
#
|
||
# orders = queryset.select_related(
|
||
# 'patient', 'ordering_provider'
|
||
# ).order_by('-order_datetime')[:20]
|
||
#
|
||
# return render(request, 'radiology/partials/order_list.html', {'orders': orders})
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # ACTION VIEWS
|
||
# # ============================================================================
|
||
#
|
||
# @login_required
|
||
# def schedule_study(request, order_id):
|
||
# """
|
||
# Schedule an imaging study for an order.
|
||
# """
|
||
# if request.method == 'POST':
|
||
# order = get_object_or_404(
|
||
# ImagingOrder,
|
||
# id=order_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# scheduled_datetime = request.POST.get('scheduled_datetime')
|
||
# if scheduled_datetime:
|
||
# scheduled_datetime = timezone.datetime.fromisoformat(scheduled_datetime)
|
||
#
|
||
# # Create or update study
|
||
# study, created = ImagingStudy.objects.get_or_create(
|
||
# order=order,
|
||
# tenant=request.user.tenant,
|
||
# defaults={
|
||
# 'study_datetime': scheduled_datetime,
|
||
# 'status': 'SCHEDULED',
|
||
# 'technologist': request.user
|
||
# }
|
||
# )
|
||
#
|
||
# if not created:
|
||
# study.study_datetime = scheduled_datetime
|
||
# study.status = 'SCHEDULED'
|
||
# study.save()
|
||
#
|
||
# # Update order status
|
||
# order.status = 'SCHEDULED'
|
||
# order.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='STUDY_SCHEDULED',
|
||
# model='ImagingOrder',
|
||
# object_id=str(order.order_id),
|
||
# details={
|
||
# 'patient_name': f"{order.patient.first_name} {order.patient.last_name}",
|
||
# 'scheduled_time': scheduled_datetime.isoformat()
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(request, 'Study scheduled successfully.')
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# return render(request, 'radiology/partials/order_status.html', {'order': order})
|
||
#
|
||
# return redirect('radiology:imaging_order_detail', pk=order.pk)
|
||
#
|
||
# return JsonResponse({'success': False})
|
||
#
|
||
#
|
||
# @login_required
|
||
# def start_study(request, study_id):
|
||
# """
|
||
# Start an imaging study.
|
||
# """
|
||
# if request.method == 'POST':
|
||
# study = get_object_or_404(
|
||
# ImagingStudy,
|
||
# id=study_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# study.status = 'IN_PROGRESS'
|
||
# study.started_datetime = timezone.now()
|
||
# study.technologist = request.user
|
||
# study.save()
|
||
#
|
||
# # Update order status
|
||
# study.order.status = 'IN_PROGRESS'
|
||
# study.order.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='STUDY_STARTED',
|
||
# model='ImagingStudy',
|
||
# object_id=str(study.study_id),
|
||
# details={
|
||
# 'patient_name': f"{study.order.patient.first_name} {study.order.patient.last_name}",
|
||
# 'modality': study.order.modality
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(request, 'Study started successfully.')
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# return render(request, 'radiology/partials/study_status.html', {'study': study})
|
||
#
|
||
# return redirect('radiology:imaging_study_detail', pk=study.pk)
|
||
#
|
||
# return JsonResponse({'success': False})
|
||
#
|
||
#
|
||
# @login_required
|
||
# def complete_study(request, study_id):
|
||
# """
|
||
# Complete an imaging study.
|
||
# """
|
||
# if request.method == 'POST':
|
||
# study = get_object_or_404(
|
||
# ImagingStudy,
|
||
# id=study_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# study.status = 'COMPLETED'
|
||
# study.completed_datetime = timezone.now()
|
||
# study.save()
|
||
#
|
||
# # Update order status
|
||
# study.order.status = 'COMPLETED'
|
||
# study.order.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='STUDY_COMPLETED',
|
||
# model='ImagingStudy',
|
||
# object_id=str(study.study_id),
|
||
# details={
|
||
# 'patient_name': f"{study.order.patient.first_name} {study.order.patient.last_name}",
|
||
# 'modality': study.order.modality
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(request, 'Study completed successfully.')
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# return render(request, 'radiology/partials/study_status.html', {'study': study})
|
||
#
|
||
# return redirect('radiology:imaging_study_detail', pk=study.pk)
|
||
#
|
||
# return JsonResponse({'success': False})
|
||
#
|
||
#
|
||
# @login_required
|
||
# def sign_report(request, report_id):
|
||
# """
|
||
# Sign a radiology report.
|
||
# """
|
||
# if request.method == 'POST':
|
||
# report = get_object_or_404(
|
||
# RadiologyReport,
|
||
# id=report_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# # Only allow signing if report is in draft status
|
||
# if report.status != 'DRAFT':
|
||
# messages.error(request, 'Only draft reports can be signed.')
|
||
# return redirect('radiology:radiology_report_detail', pk=report.pk)
|
||
#
|
||
# report.status = 'SIGNED'
|
||
# report.signed_datetime = timezone.now()
|
||
# report.signed_by = request.user
|
||
# report.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='REPORT_SIGNED',
|
||
# model='RadiologyReport',
|
||
# object_id=str(report.report_id),
|
||
# details={
|
||
# 'patient_name': f"{report.study.order.patient.first_name} {report.study.order.patient.last_name}",
|
||
# 'has_critical_findings': report.has_critical_findings
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(request, 'Report signed successfully.')
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# return render(request, 'radiology/partials/report_status.html', {'report': report})
|
||
#
|
||
# return redirect('radiology:radiology_report_detail', pk=report.pk)
|
||
#
|
||
# return JsonResponse({'success': False})
|
||
#
|
||
#
|
||
# @login_required
|
||
# def dictate_report(request, study_id):
|
||
# """
|
||
# Start dictating a report for a study.
|
||
# """
|
||
# if request.method == 'POST':
|
||
# study = get_object_or_404(
|
||
# ImagingStudy,
|
||
# id=study_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# template_id = request.POST.get('template_id')
|
||
# template = None
|
||
# if template_id:
|
||
# template = get_object_or_404(
|
||
# ReportTemplate,
|
||
# id=template_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# # Create report if it doesn't exist
|
||
# report, created = RadiologyReport.objects.get_or_create(
|
||
# study=study,
|
||
# tenant=request.user.tenant,
|
||
# defaults={
|
||
# 'radiologist': request.user,
|
||
# 'template': template,
|
||
# 'status': 'DRAFT'
|
||
# }
|
||
# )
|
||
#
|
||
# if created:
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='REPORT_DICTATION_STARTED',
|
||
# model='RadiologyReport',
|
||
# object_id=str(report.report_id),
|
||
# details={
|
||
# 'patient_name': f"{study.order.patient.first_name} {study.order.patient.last_name}",
|
||
# 'template_used': template.template_name if template else None
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(request, 'Report dictation started.')
|
||
#
|
||
# return redirect('radiology:radiology_report_detail', pk=report.pk)
|
||
#
|
||
# return JsonResponse({'success': False})
|
||
#
|
||
#
|
||
#
|
||
#
|
||
# @login_required
|
||
# @require_POST
|
||
# def clock_in(request, employee_id):
|
||
# """
|
||
# Clock in an employee. If HTMX, return the updated controls partial (button -> time).
|
||
# Otherwise return JSON.
|
||
# """
|
||
# # Prefer the URL param; fall back to POST field for safety
|
||
# emp_id = employee_id or request.POST.get('employee_id')
|
||
#
|
||
# try:
|
||
# employee = Employee.objects.get(id=emp_id, tenant=request.user.tenant)
|
||
# except Employee.DoesNotExist:
|
||
# if request.headers.get('HX-Request'):
|
||
# return HttpResponse("Employee not found.", status=404)
|
||
# return JsonResponse({'success': False, 'message': 'Employee not found.'}, status=404)
|
||
#
|
||
# today = timezone.now().date()
|
||
# open_entry = TimeEntry.objects.filter(
|
||
# employee=employee,
|
||
# work_date=today,
|
||
# clock_out_time__isnull=True
|
||
# ).first()
|
||
#
|
||
# if open_entry:
|
||
# # Already clocked in — just re-render the controls so the UI shows the time
|
||
# if request.headers.get('HX-Request'):
|
||
# html = render_to_string(
|
||
# 'hr/partials/clock_controls.html',
|
||
# {'employee': employee, 'time_entry': open_entry},
|
||
# request=request
|
||
# )
|
||
# return HttpResponse(html)
|
||
# return JsonResponse({'success': False, 'message': 'Employee is already clocked in.'}, status=400)
|
||
#
|
||
# # Create new time entry
|
||
# time_entry = TimeEntry.objects.create(
|
||
# employee=employee,
|
||
# work_date=today,
|
||
# clock_in_time=timezone.now(),
|
||
# status='DRAFT'
|
||
# )
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# html = render_to_string(
|
||
# 'hr/partials/clock_controls.html',
|
||
# {'employee': employee, 'time_entry': time_entry},
|
||
# request=request
|
||
# )
|
||
# return HttpResponse(html)
|
||
#
|
||
# return JsonResponse({
|
||
# 'success': True,
|
||
# 'message': f'{employee.get_full_name()} clocked in successfully.',
|
||
# 'time_entry_id': time_entry.id
|
||
# })
|
||
#
|
||
#
|
||
# @login_required
|
||
# @require_POST
|
||
# def clock_out(request, employee_id):
|
||
# """
|
||
# Clock out an employee.
|
||
# If HTMX, return the updated controls partial (button -> times).
|
||
# Otherwise, return JSON.
|
||
# """
|
||
# emp_id = employee_id or request.POST.get('employee_id')
|
||
#
|
||
# # Find employee in same tenant
|
||
# try:
|
||
# employee = Employee.objects.get(id=emp_id, tenant=request.user.tenant)
|
||
# except Employee.DoesNotExist:
|
||
# if request.headers.get('HX-Request'):
|
||
# # Re-render controls without an active entry (will show "Clock In")
|
||
# html = render_to_string(
|
||
# 'hr/partials/clock_controls.html',
|
||
# {'employee': None, 'time_entry': None}, # or pass the real employee if you want the button still visible
|
||
# request=request
|
||
# )
|
||
# return HttpResponse(html, status=404)
|
||
# return JsonResponse({'success': False, 'message': 'Employee not found.'}, status=404)
|
||
#
|
||
# # Locate today's open entry
|
||
# today = timezone.now().date()
|
||
# time_entry = TimeEntry.objects.filter(
|
||
# employee=employee,
|
||
# work_date=today,
|
||
# clock_out_time__isnull=True
|
||
# ).first()
|
||
#
|
||
# if not time_entry:
|
||
# if request.headers.get('HX-Request'):
|
||
# # No open entry → re-render to show "Clock In"
|
||
# html = render_to_string(
|
||
# 'hr/partials/clock_controls.html',
|
||
# {'employee': employee, 'time_entry': None},
|
||
# request=request
|
||
# )
|
||
# return HttpResponse(html, status=400)
|
||
# return JsonResponse({'success': False, 'message': 'No active clock-in found for this employee.'}, status=400)
|
||
#
|
||
# # Close the entry
|
||
# time_entry.clock_out_time = timezone.now()
|
||
# time_entry.save() # if your model computes total_hours on save
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# html = render_to_string(
|
||
# 'hr/partials/clock_controls.html',
|
||
# {'employee': employee, 'time_entry': time_entry},
|
||
# request=request
|
||
# )
|
||
# return HttpResponse(html)
|
||
#
|
||
# return JsonResponse({
|
||
# 'success': True,
|
||
# 'message': f'{employee.get_full_name()} clocked out successfully.',
|
||
# 'time_entry_id': time_entry.id,
|
||
# # If total_hours is Decimal, this makes it JSON-safe:
|
||
# 'hours_worked': float(getattr(time_entry, 'total_hours', 0) or 0)
|
||
# })
|
||
# Query patterns to use if needed
|
||
# # All upcoming sessions for a tenant (next 30 days)
|
||
# TrainingSession.objects.filter(
|
||
# tenant=request.user.tenant, start_at__gte=timezone.now(),
|
||
# start_at__lte=timezone.now() + timedelta(days=30)
|
||
# ).select_related('program', 'instructor')
|
||
#
|
||
# # Employees due for renewal in 30 days
|
||
# TrainingCertificates.objects.filter(
|
||
# tenant=request.user.tenant,
|
||
# expiry_date__lte=date.today() + timedelta(days=30),
|
||
# expiry_date__gte=date.today()
|
||
# ).select_related('employee', 'program')
|
||
#
|
||
# # Enroll an employee, respecting capacity
|
||
# session = TrainingSession.objects.select_for_update().get(pk=session_pk, tenant=tenant)
|
||
# if session.capacity and session.enrollments.count() >= session.capacity:
|
||
# status = 'WAITLISTED'
|
||
# else:
|
||
# status = 'SCHEDULED'
|
||
# enrollment = TrainingRecord.objects.create(
|
||
# tenant=tenant, employee=emp, program=session.program, session=session,
|
||
# status=status, created_by=request.user
|
||
# )
|
||
#
|
||
# # Mark completion + pass (auto-certificate will fire via signal)
|
||
# enrollment.status = 'COMPLETED'
|
||
# enrollment.passed = True
|
||
# enrollment.completion_date = date.today()
|
||
# enrollment.credits_earned = enrollment.hours
|
||
# enrollment.save()
|
||
|
||
|
||
#
|
||
# from django.shortcuts import render, redirect, get_object_or_404
|
||
# from django.views.generic import (
|
||
# ListView, DetailView, CreateView, UpdateView, DeleteView
|
||
# )
|
||
# from django.urls import reverse, reverse_lazy
|
||
# from django.contrib.auth.decorators import login_required
|
||
# from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||
# from django.contrib import messages
|
||
# from django.utils import timezone
|
||
# from django.http import HttpResponse, JsonResponse
|
||
# from django.db.models import Q, Count, Sum, Avg, F, ExpressionWrapper, fields
|
||
# from django.template.loader import render_to_string
|
||
# from django.core.paginator import Paginator
|
||
#
|
||
# from .models import (
|
||
# Department, Employee, Schedule, ScheduleAssignment,
|
||
# TimeEntry, PerformanceReview, TrainingRecord
|
||
# )
|
||
# from .forms import (
|
||
# DepartmentForm, EmployeeForm, ScheduleForm, ScheduleAssignmentForm,
|
||
# TimeEntryForm, PerformanceReviewForm, TrainingRecordForm,
|
||
# HRSearchForm, TimeEntrySearchForm
|
||
# )
|
||
#
|
||
#
|
||
# class DashboardView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# HR dashboard view displaying summary statistics.
|
||
# """
|
||
# template_name = 'hr/dashboard.html'
|
||
# context_object_name = 'employees'
|
||
# paginate_by = 10
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Get recent employees as primary queryset.
|
||
# """
|
||
# return Employee.objects.filter(
|
||
# tenant=self.request.user.tenant,
|
||
# is_active=True
|
||
# ).order_by('-hire_date')[:10]
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# tenant = self.request.user.tenant
|
||
#
|
||
# # Employee statistics
|
||
# context['total_employees'] = Employee.objects.filter(
|
||
# tenant=tenant
|
||
# ).count()
|
||
#
|
||
# context['active_employees'] = Employee.objects.filter(
|
||
# tenant=tenant,
|
||
# is_active=True
|
||
# ).count()
|
||
#
|
||
# context['department_stats'] = Department.objects.filter(
|
||
# tenant=tenant
|
||
# ).annotate(
|
||
# employee_count=Count('employees', filter=Q(employees__is_active=True))
|
||
# ).order_by('-employee_count')[:5]
|
||
#
|
||
# # Recent hires
|
||
# context['recent_hires'] = Employee.objects.filter(
|
||
# tenant=tenant
|
||
# ).order_by('-hire_date')[:5]
|
||
#
|
||
# # Upcoming reviews
|
||
# context['upcoming_reviews'] = PerformanceReview.objects.filter(
|
||
# tenant=tenant,
|
||
# status='PENDING',
|
||
# scheduled_date__gte=timezone.now().date()
|
||
# ).order_by('scheduled_date')[:5]
|
||
#
|
||
# # Employees on leave
|
||
# context['employees_on_leave'] = Employee.objects.filter(
|
||
# tenant=tenant,
|
||
# employment_status='LEAVE'
|
||
# ).order_by('last_name')
|
||
#
|
||
# # Training expiring soon
|
||
# context['expiring_trainings'] = TrainingRecord.objects.filter(
|
||
# tenant=tenant,
|
||
# status='COMPLETED',
|
||
# expiry_date__gte=timezone.now().date(),
|
||
# expiry_date__lte=timezone.now().date() + timezone.timedelta(days=30)
|
||
# ).order_by('expiry_date')[:5]
|
||
#
|
||
# # Time entry approvals pending
|
||
# context['pending_time_approvals'] = TimeEntry.objects.filter(
|
||
# tenant=tenant,
|
||
# is_approved=False,
|
||
# entry_date__gte=timezone.now().date() - timezone.timedelta(days=14)
|
||
# ).count()
|
||
#
|
||
# # Search form
|
||
# context['search_form'] = HRSearchForm(tenant=tenant)
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class EmployeeListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||
# """
|
||
# List view for employees.
|
||
# """
|
||
# model = Employee
|
||
# template_name = 'hr/employee_list.html'
|
||
# context_object_name = 'employees'
|
||
# paginate_by = 20
|
||
# permission_required = 'hr.view_employee'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter employees by tenant and apply search filters.
|
||
# """
|
||
# queryset = Employee.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Apply search filters
|
||
# form = HRSearchForm(self.request.GET, tenant=self.request.user.tenant)
|
||
# if form.is_valid():
|
||
# data = form.cleaned_data
|
||
#
|
||
# # Search term
|
||
# if data.get('search'):
|
||
# search_term = data['search']
|
||
# queryset = queryset.filter(
|
||
# Q(first_name__icontains=search_term) |
|
||
# Q(last_name__icontains=search_term) |
|
||
# Q(employee_number__icontains=search_term) |
|
||
# Q(position__icontains=search_term) |
|
||
# Q(personal_email__icontains=search_term) |
|
||
# Q(work_email__icontains=search_term)
|
||
# )
|
||
#
|
||
# # Department filter
|
||
# if data.get('department'):
|
||
# queryset = queryset.filter(department=data['department'])
|
||
#
|
||
# # Employment status filter
|
||
# if data.get('employment_status'):
|
||
# queryset = queryset.filter(employment_status=data['employment_status'])
|
||
#
|
||
# # Employment type filter
|
||
# if data.get('employment_type'):
|
||
# queryset = queryset.filter(employment_type=data['employment_type'])
|
||
#
|
||
# # Position filter
|
||
# if data.get('position'):
|
||
# queryset = queryset.filter(position__icontains=data['position'])
|
||
#
|
||
# # Hire date range
|
||
# if data.get('hire_date_from'):
|
||
# queryset = queryset.filter(hire_date__gte=data['hire_date_from'])
|
||
#
|
||
# if data.get('hire_date_to'):
|
||
# queryset = queryset.filter(hire_date__lte=data['hire_date_to'])
|
||
#
|
||
# # Active status
|
||
# if data.get('is_active') == 'True':
|
||
# queryset = queryset.filter(is_active=True)
|
||
# elif data.get('is_active') == 'False':
|
||
# queryset = queryset.filter(is_active=False)
|
||
#
|
||
# return queryset.select_related('department', 'supervisor')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# """
|
||
# Add search form to context.
|
||
# """
|
||
# context = super().get_context_data(**kwargs)
|
||
# context['search_form'] = HRSearchForm(
|
||
# self.request.GET or None,
|
||
# tenant=self.request.user.tenant
|
||
# )
|
||
# return context
|
||
#
|
||
#
|
||
# class EmployeeDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||
# """
|
||
# Detail view for employees.
|
||
# """
|
||
# model = Employee
|
||
# template_name = 'hr/employee_detail.html'
|
||
# context_object_name = 'employee'
|
||
# permission_required = 'hr.view_employee'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter employee by tenant.
|
||
# """
|
||
# return Employee.objects.filter(
|
||
# tenant=self.request.user.tenant
|
||
# ).select_related(
|
||
# 'department', 'supervisor', 'user', 'created_by'
|
||
# )
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# """
|
||
# Add related objects to context.
|
||
# """
|
||
# context = super().get_context_data(**kwargs)
|
||
# employee = self.object
|
||
#
|
||
# # Current schedule
|
||
# context['current_schedule'] = Schedule.objects.filter(
|
||
# employee=employee,
|
||
# is_current=True,
|
||
# is_active=True
|
||
# ).first()
|
||
#
|
||
# # Recent time entries
|
||
# context['recent_time_entries'] = TimeEntry.objects.filter(
|
||
# employee=employee
|
||
# ).order_by('-entry_date', '-created_at')[:10]
|
||
#
|
||
# # Performance reviews
|
||
# context['performance_reviews'] = PerformanceReview.objects.filter(
|
||
# employee=employee
|
||
# ).order_by('-scheduled_date')[:5]
|
||
#
|
||
# # Training records
|
||
# context['training_records'] = TrainingRecord.objects.filter(
|
||
# employee=employee
|
||
# ).order_by('-start_date')[:10]
|
||
#
|
||
# # Subordinates
|
||
# context['subordinates'] = Employee.objects.filter(
|
||
# supervisor=employee,
|
||
# is_active=True
|
||
# ).order_by('last_name', 'first_name')
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class EmployeeCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create view for employees.
|
||
# """
|
||
# model = Employee
|
||
# form_class = EmployeeForm
|
||
# template_name = 'hr/employee_form.html'
|
||
# permission_required = 'hr.add_employee'
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Set tenant and user before saving.
|
||
# """
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.created_by = self.request.user
|
||
#
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Employee {form.instance.get_full_name()} created successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to employee detail view.
|
||
# """
|
||
# return reverse('hr:employee_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class EmployeeUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update view for employees.
|
||
# """
|
||
# model = Employee
|
||
# form_class = EmployeeForm
|
||
# template_name = 'hr/employee_form.html'
|
||
# permission_required = 'hr.change_employee'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter employee by tenant.
|
||
# """
|
||
# return Employee.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Handle successful form validation.
|
||
# """
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Employee {form.instance.get_full_name()} updated successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to employee detail view.
|
||
# """
|
||
# return reverse('hr:employee_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class EmployeeDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
||
# """
|
||
# Delete view for employees.
|
||
# """
|
||
# model = Employee
|
||
# template_name = 'hr/employee_confirm_delete.html'
|
||
# permission_required = 'hr.delete_employee'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter employee by tenant.
|
||
# """
|
||
# return Employee.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def delete(self, request, *args, **kwargs):
|
||
# """
|
||
# Override delete to show success message.
|
||
# """
|
||
# self.object = self.get_object()
|
||
# name = self.object.get_full_name()
|
||
#
|
||
# # Check if employee can be safely deleted
|
||
# if self.object.managed_departments.exists():
|
||
# messages.error(
|
||
# request,
|
||
# f"Cannot delete {name} because they are assigned as a department manager."
|
||
# )
|
||
# return redirect('hr:employee_detail', pk=self.object.pk)
|
||
#
|
||
# if self.object.subordinates.exists():
|
||
# messages.error(
|
||
# request,
|
||
# f"Cannot delete {name} because they are assigned as a supervisor."
|
||
# )
|
||
# return redirect('hr:employee_detail', pk=self.object.pk)
|
||
#
|
||
# success_url = self.get_success_url()
|
||
# self.object.delete()
|
||
#
|
||
# messages.success(request, f"Employee {name} deleted successfully.")
|
||
#
|
||
# return redirect(success_url)
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to employee list view.
|
||
# """
|
||
# return reverse_lazy('hr:employee_list')
|
||
#
|
||
#
|
||
# class DepartmentListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||
# """
|
||
# List view for departments.
|
||
# """
|
||
# model = Department
|
||
# template_name = 'hr/department_list.html'
|
||
# context_object_name = 'departments'
|
||
# paginate_by = 20
|
||
# permission_required = 'hr.view_department'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter departments by tenant.
|
||
# """
|
||
# queryset = Department.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Apply search filters
|
||
# search = self.request.GET.get('search', '')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(name__icontains=search) |
|
||
# Q(code__icontains=search) |
|
||
# Q(description__icontains=search)
|
||
# )
|
||
#
|
||
# department_type = self.request.GET.get('department_type', '')
|
||
# if department_type:
|
||
# queryset = queryset.filter(department_type=department_type)
|
||
#
|
||
# is_active = self.request.GET.get('is_active', '')
|
||
# if is_active == 'True':
|
||
# queryset = queryset.filter(is_active=True)
|
||
# elif is_active == 'False':
|
||
# queryset = queryset.filter(is_active=False)
|
||
#
|
||
# return queryset.select_related('manager', 'parent_department')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# """
|
||
# Add filter choices to context.
|
||
# """
|
||
# context = super().get_context_data(**kwargs)
|
||
# context['search'] = self.request.GET.get('search', '')
|
||
# context['department_types'] = Department.DEPARTMENT_TYPES
|
||
# context['selected_type'] = self.request.GET.get('department_type', '')
|
||
# context['selected_active'] = self.request.GET.get('is_active', '')
|
||
# return context
|
||
#
|
||
#
|
||
# class DepartmentDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||
# """
|
||
# Detail view for departments.
|
||
# """
|
||
# model = Department
|
||
# template_name = 'hr/department_detail.html'
|
||
# context_object_name = 'department'
|
||
# permission_required = 'hr.view_department'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter department by tenant.
|
||
# """
|
||
# return Department.objects.filter(
|
||
# tenant=self.request.user.tenant
|
||
# ).select_related(
|
||
# 'manager', 'parent_department', 'created_by'
|
||
# )
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# """
|
||
# Add related objects to context.
|
||
# """
|
||
# context = super().get_context_data(**kwargs)
|
||
# department = self.object
|
||
#
|
||
# # Employees in department
|
||
# context['employees'] = Employee.objects.filter(
|
||
# department=department,
|
||
# is_active=True
|
||
# ).order_by('last_name', 'first_name')
|
||
#
|
||
# # Sub-departments
|
||
# context['sub_departments'] = Department.objects.filter(
|
||
# parent_department=department,
|
||
# is_active=True
|
||
# ).order_by('name')
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class DepartmentCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create view for departments.
|
||
# """
|
||
# model = Department
|
||
# form_class = DepartmentForm
|
||
# template_name = 'hr/department_form.html'
|
||
# permission_required = 'hr.add_department'
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Set tenant and user before saving.
|
||
# """
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.created_by = self.request.user
|
||
#
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Department {form.instance.name} created successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to department detail view.
|
||
# """
|
||
# return reverse('hr:department_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class DepartmentUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update view for departments.
|
||
# """
|
||
# model = Department
|
||
# form_class = DepartmentForm
|
||
# template_name = 'hr/department_form.html'
|
||
# permission_required = 'hr.change_department'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter department by tenant.
|
||
# """
|
||
# return Department.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Handle successful form validation.
|
||
# """
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Department {form.instance.name} updated successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to department detail view.
|
||
# """
|
||
# return reverse('hr:department_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class DepartmentDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
||
# """
|
||
# Delete view for departments.
|
||
# """
|
||
# model = Department
|
||
# template_name = 'hr/department_confirm_delete.html'
|
||
# permission_required = 'hr.delete_department'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter department by tenant.
|
||
# """
|
||
# return Department.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def delete(self, request, *args, **kwargs):
|
||
# """
|
||
# Override delete to show success message.
|
||
# """
|
||
# self.object = self.get_object()
|
||
# name = self.object.name
|
||
#
|
||
# # Check if department can be safely deleted
|
||
# if self.object.employees.exists():
|
||
# messages.error(
|
||
# request,
|
||
# f"Cannot delete {name} because it has employees assigned to it."
|
||
# )
|
||
# return redirect('hr:department_detail', pk=self.object.pk)
|
||
#
|
||
# if self.object.sub_departments.exists():
|
||
# messages.error(
|
||
# request,
|
||
# f"Cannot delete {name} because it has sub-departments assigned to it."
|
||
# )
|
||
# return redirect('hr:department_detail', pk=self.object.pk)
|
||
#
|
||
# success_url = self.get_success_url()
|
||
# self.object.delete()
|
||
#
|
||
# messages.success(request, f"Department {name} deleted successfully.")
|
||
#
|
||
# return redirect(success_url)
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to department list view.
|
||
# """
|
||
# return reverse_lazy('hr:department_list')
|
||
#
|
||
#
|
||
# class ScheduleListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||
# """
|
||
# List view for schedules.
|
||
# """
|
||
# model = Schedule
|
||
# template_name = 'hr/schedule_list.html'
|
||
# context_object_name = 'schedules'
|
||
# paginate_by = 20
|
||
# permission_required = 'hr.view_schedule'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter schedules by tenant and apply search filters.
|
||
# """
|
||
# queryset = Schedule.objects.filter(
|
||
# employee__tenant=self.request.user.tenant
|
||
# )
|
||
#
|
||
# # Apply search filters
|
||
# search = self.request.GET.get('search', '')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(name__icontains=search) |
|
||
# Q(description__icontains=search) |
|
||
# Q(employee__first_name__icontains=search) |
|
||
# Q(employee__last_name__icontains=search) |
|
||
# Q(employee__employee_number__icontains=search)
|
||
# )
|
||
#
|
||
# employee_id = self.request.GET.get('employee', '')
|
||
# if employee_id:
|
||
# queryset = queryset.filter(employee_id=employee_id)
|
||
#
|
||
# schedule_type = self.request.GET.get('schedule_type', '')
|
||
# if schedule_type:
|
||
# queryset = queryset.filter(schedule_type=schedule_type)
|
||
#
|
||
# is_active = self.request.GET.get('is_active', '')
|
||
# if is_active == 'True':
|
||
# queryset = queryset.filter(is_active=True)
|
||
# elif is_active == 'False':
|
||
# queryset = queryset.filter(is_active=False)
|
||
#
|
||
# is_current = self.request.GET.get('is_current', '')
|
||
# if is_current == 'True':
|
||
# queryset = queryset.filter(is_current=True)
|
||
#
|
||
# return queryset.select_related('employee', 'approved_by')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# """
|
||
# Add filter choices to context.
|
||
# """
|
||
# context = super().get_context_data(**kwargs)
|
||
# tenant = self.request.user.tenant
|
||
#
|
||
# context['search'] = self.request.GET.get('search', '')
|
||
# context['employees'] = Employee.objects.filter(
|
||
# tenant=tenant,
|
||
# is_active=True
|
||
# ).order_by('last_name', 'first_name')
|
||
# context['schedule_types'] = Schedule.SCHEDULE_TYPES
|
||
# context['selected_employee'] = self.request.GET.get('employee', '')
|
||
# context['selected_type'] = self.request.GET.get('schedule_type', '')
|
||
# context['selected_active'] = self.request.GET.get('is_active', '')
|
||
# context['selected_current'] = self.request.GET.get('is_current', '')
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class ScheduleDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||
# """
|
||
# Detail view for schedules.
|
||
# """
|
||
# model = Schedule
|
||
# template_name = 'hr/schedule_detail.html'
|
||
# context_object_name = 'schedule'
|
||
# permission_required = 'hr.view_schedule'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter schedule by tenant.
|
||
# """
|
||
# return Schedule.objects.filter(
|
||
# employee__tenant=self.request.user.tenant
|
||
# ).select_related(
|
||
# 'employee', 'approved_by', 'created_by'
|
||
# )
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# """
|
||
# Add related objects to context.
|
||
# """
|
||
# context = super().get_context_data(**kwargs)
|
||
# schedule = self.object
|
||
#
|
||
# # Assignments for this schedule
|
||
# context['assignments'] = ScheduleAssignment.objects.filter(
|
||
# schedule=schedule
|
||
# ).order_by('assignment_date', 'start_time')
|
||
#
|
||
# # Group assignments by week
|
||
# assignments_by_week = {}
|
||
# for assignment in context['assignments']:
|
||
# # Get week start date (Monday)
|
||
# week_start = assignment.assignment_date - timezone.timedelta(
|
||
# days=assignment.assignment_date.weekday()
|
||
# )
|
||
# week_key = week_start.strftime('%Y-%m-%d')
|
||
#
|
||
# if week_key not in assignments_by_week:
|
||
# assignments_by_week[week_key] = []
|
||
#
|
||
# assignments_by_week[week_key].append(assignment)
|
||
#
|
||
# context['assignments_by_week'] = assignments_by_week
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class ScheduleCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create view for schedules.
|
||
# """
|
||
# model = Schedule
|
||
# form_class = ScheduleForm
|
||
# template_name = 'hr/schedule_form.html'
|
||
# permission_required = 'hr.add_schedule'
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
#
|
||
# # Pre-select employee if provided in GET parameters
|
||
# employee_id = self.request.GET.get('employee', None)
|
||
# if employee_id:
|
||
# kwargs['initial'] = kwargs.get('initial', {})
|
||
# kwargs['initial']['employee'] = employee_id
|
||
#
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Set tenant and user before saving.
|
||
# """
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.created_by = self.request.user
|
||
#
|
||
# # Set approval information
|
||
# form.instance.approved_by = self.request.user
|
||
# form.instance.approval_date = timezone.now()
|
||
#
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Schedule '{form.instance.name}' for {form.instance.employee.get_full_name()} created successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to schedule detail view.
|
||
# """
|
||
# return reverse('hr:schedule_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class ScheduleUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update view for schedules.
|
||
# """
|
||
# model = Schedule
|
||
# form_class = ScheduleForm
|
||
# template_name = 'hr/schedule_form.html'
|
||
# permission_required = 'hr.change_schedule'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter schedule by tenant.
|
||
# """
|
||
# return Schedule.objects.filter(
|
||
# employee__tenant=self.request.user.tenant
|
||
# )
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Handle successful form validation.
|
||
# """
|
||
# # If is_current status changed, update approval information
|
||
# if 'is_current' in form.changed_data and form.instance.is_current:
|
||
# form.instance.approved_by = self.request.user
|
||
# form.instance.approval_date = timezone.now()
|
||
#
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Schedule '{form.instance.name}' updated successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to schedule detail view.
|
||
# """
|
||
# return reverse('hr:schedule_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class ScheduleDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
||
# """
|
||
# Delete view for schedules.
|
||
# """
|
||
# model = Schedule
|
||
# template_name = 'hr/schedule_confirm_delete.html'
|
||
# permission_required = 'hr.delete_schedule'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter schedule by tenant.
|
||
# """
|
||
# return Schedule.objects.filter(
|
||
# employee__tenant=self.request.user.tenant
|
||
# )
|
||
#
|
||
# def delete(self, request, *args, **kwargs):
|
||
# """
|
||
# Override delete to show success message.
|
||
# """
|
||
# self.object = self.get_object()
|
||
# employee_name = self.object.employee.get_full_name()
|
||
# schedule_name = self.object.name
|
||
#
|
||
# # Check if schedule can be safely deleted
|
||
# if self.object.is_current:
|
||
# messages.error(
|
||
# request,
|
||
# f"Cannot delete schedule '{schedule_name}' because it is the current schedule for {employee_name}."
|
||
# )
|
||
# return redirect('hr:schedule_detail', pk=self.object.pk)
|
||
#
|
||
# success_url = self.get_success_url()
|
||
# self.object.delete()
|
||
#
|
||
# messages.success(
|
||
# request,
|
||
# f"Schedule '{schedule_name}' for {employee_name} deleted successfully."
|
||
# )
|
||
#
|
||
# return redirect(success_url)
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to schedule list view.
|
||
# """
|
||
# return reverse_lazy('hr:schedule_list')
|
||
#
|
||
#
|
||
# class TimeEntryListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||
# """
|
||
# List view for time entries.
|
||
# """
|
||
# model = TimeEntry
|
||
# template_name = 'hr/time_entry_list.html'
|
||
# context_object_name = 'time_entries'
|
||
# paginate_by = 20
|
||
# permission_required = 'hr.view_timeentry'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter time entries by tenant and apply search filters.
|
||
# """
|
||
# queryset = TimeEntry.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Apply search filters
|
||
# form = TimeEntrySearchForm(self.request.GET, tenant=self.request.user.tenant)
|
||
# if form.is_valid():
|
||
# data = form.cleaned_data
|
||
#
|
||
# # Search term
|
||
# if data.get('search'):
|
||
# search_term = data['search']
|
||
# queryset = queryset.filter(
|
||
# Q(employee__first_name__icontains=search_term) |
|
||
# Q(employee__last_name__icontains=search_term) |
|
||
# Q(employee__employee_number__icontains=search_term) |
|
||
# Q(description__icontains=search_term)
|
||
# )
|
||
#
|
||
# # Employee filter
|
||
# if data.get('employee'):
|
||
# queryset = queryset.filter(employee=data['employee'])
|
||
#
|
||
# # Department filter
|
||
# if data.get('department'):
|
||
# queryset = queryset.filter(department=data['department'])
|
||
#
|
||
# # Entry type filter
|
||
# if data.get('entry_type'):
|
||
# queryset = queryset.filter(entry_type=data['entry_type'])
|
||
#
|
||
# # Entry date range
|
||
# if data.get('entry_date_from'):
|
||
# queryset = queryset.filter(entry_date__gte=data['entry_date_from'])
|
||
#
|
||
# if data.get('entry_date_to'):
|
||
# queryset = queryset.filter(entry_date__lte=data['entry_date_to'])
|
||
#
|
||
# # Approval status
|
||
# if data.get('is_approved') == 'True':
|
||
# queryset = queryset.filter(is_approved=True)
|
||
# elif data.get('is_approved') == 'False':
|
||
# queryset = queryset.filter(is_approved=False)
|
||
#
|
||
# # Payment status
|
||
# if data.get('is_paid') == 'True':
|
||
# queryset = queryset.filter(is_paid=True)
|
||
# elif data.get('is_paid') == 'False':
|
||
# queryset = queryset.filter(is_paid=False)
|
||
#
|
||
# return queryset.select_related(
|
||
# 'employee', 'department', 'approved_by'
|
||
# ).order_by('-entry_date', '-updated_at')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# """
|
||
# Add search form to context.
|
||
# """
|
||
# context = super().get_context_data(**kwargs)
|
||
# context['search_form'] = TimeEntrySearchForm(
|
||
# self.request.GET or None,
|
||
# tenant=self.request.user.tenant
|
||
# )
|
||
#
|
||
# # Calculate totals
|
||
# entries = self.object_list
|
||
# context['total_hours'] = sum(entry.total_hours for entry in entries)
|
||
# context['total_approved'] = sum(1 for entry in entries if entry.is_approved)
|
||
# context['total_pending'] = sum(1 for entry in entries if not entry.is_approved)
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class TimeEntryDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||
# """
|
||
# Detail view for time entries.
|
||
# """
|
||
# model = TimeEntry
|
||
# template_name = 'hr/time_entry_detail.html'
|
||
# context_object_name = 'time_entry'
|
||
# permission_required = 'hr.view_timeentry'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter time entry by tenant.
|
||
# """
|
||
# return TimeEntry.objects.filter(
|
||
# tenant=self.request.user.tenant
|
||
# ).select_related(
|
||
# 'employee', 'department', 'approved_by', 'created_by'
|
||
# )
|
||
#
|
||
#
|
||
# class TimeEntryCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create view for time entries.
|
||
# """
|
||
# model = TimeEntry
|
||
# form_class = TimeEntryForm
|
||
# template_name = 'hr/time_entry_form.html'
|
||
# permission_required = 'hr.add_timeentry'
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
#
|
||
# # Pre-select employee and date if provided in GET parameters
|
||
# initial = {}
|
||
# employee_id = self.request.GET.get('employee', None)
|
||
# if employee_id:
|
||
# initial['employee'] = employee_id
|
||
#
|
||
# entry_date = self.request.GET.get('entry_date', None)
|
||
# if entry_date:
|
||
# try:
|
||
# initial['entry_date'] = timezone.datetime.strptime(
|
||
# entry_date, '%Y-%m-%d'
|
||
# ).date()
|
||
# except (ValueError, TypeError):
|
||
# pass
|
||
#
|
||
# if initial:
|
||
# kwargs['initial'] = initial
|
||
#
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Set tenant and user before saving.
|
||
# """
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.created_by = self.request.user
|
||
#
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Time entry for {form.instance.employee.get_full_name()} on {form.instance.entry_date} created successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to time entry list view or employee detail based on origin.
|
||
# """
|
||
# if 'employee' in self.request.GET:
|
||
# return reverse('hr:employee_detail', kwargs={'pk': self.request.GET['employee']})
|
||
# return reverse('hr:time_entry_list')
|
||
#
|
||
#
|
||
# class TimeEntryUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update view for time entries.
|
||
# """
|
||
# model = TimeEntry
|
||
# form_class = TimeEntryForm
|
||
# template_name = 'hr/time_entry_form.html'
|
||
# permission_required = 'hr.change_timeentry'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter time entry by tenant.
|
||
# """
|
||
# return TimeEntry.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Handle successful form validation.
|
||
# """
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Time entry for {form.instance.employee.get_full_name()} on {form.instance.entry_date} updated successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to time entry detail view.
|
||
# """
|
||
# return reverse('hr:time_entry_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# @login_required
|
||
# def approve_time_entry(request, pk):
|
||
# """
|
||
# Approve a time entry.
|
||
# """
|
||
# time_entry = get_object_or_404(
|
||
# TimeEntry,
|
||
# pk=pk,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# if request.method == 'POST':
|
||
# if not request.user.has_perm('hr.approve_timeentry'):
|
||
# messages.error(request, "You don't have permission to approve time entries.")
|
||
# return redirect('hr:time_entry_detail', pk=time_entry.pk)
|
||
#
|
||
# time_entry.is_approved = True
|
||
# time_entry.approved_by = request.user
|
||
# time_entry.approval_date = timezone.now()
|
||
# time_entry.save()
|
||
#
|
||
# messages.success(
|
||
# request,
|
||
# f"Time entry for {time_entry.employee.get_full_name()} on {time_entry.entry_date} approved."
|
||
# )
|
||
#
|
||
# redirect_url = request.POST.get('next', reverse('hr:time_entry_detail', kwargs={'pk': time_entry.pk}))
|
||
# return redirect(redirect_url)
|
||
#
|
||
# return redirect('hr:time_entry_detail', pk=time_entry.pk)
|
||
#
|
||
#
|
||
#
|
||
#
|
||
# class PerformanceReviewListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||
# """
|
||
# List view for performance reviews.
|
||
# """
|
||
# model = PerformanceReview
|
||
# template_name = 'hr/performance_review_list.html'
|
||
# context_object_name = 'reviews'
|
||
# paginate_by = 20
|
||
# permission_required = 'hr.view_performancereview'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter reviews by tenant and apply search filters.
|
||
# """
|
||
# queryset = PerformanceReview.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Apply search filters
|
||
# search = self.request.GET.get('search', '')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(employee__first_name__icontains=search) |
|
||
# Q(employee__last_name__icontains=search) |
|
||
# Q(employee__employee_number__icontains=search) |
|
||
# Q(strengths__icontains=search) |
|
||
# Q(areas_for_improvement__icontains=search)
|
||
# )
|
||
#
|
||
# employee_id = self.request.GET.get('employee', '')
|
||
# if employee_id:
|
||
# queryset = queryset.filter(employee_id=employee_id)
|
||
#
|
||
# review_type = self.request.GET.get('review_type', '')
|
||
# if review_type:
|
||
# queryset = queryset.filter(review_type=review_type)
|
||
#
|
||
# status = self.request.GET.get('status', '')
|
||
# if status:
|
||
# queryset = queryset.filter(status=status)
|
||
#
|
||
# # Date range filters
|
||
# scheduled_from = self.request.GET.get('scheduled_from', '')
|
||
# if scheduled_from:
|
||
# try:
|
||
# scheduled_from = timezone.datetime.strptime(scheduled_from, '%Y-%m-%d').date()
|
||
# queryset = queryset.filter(scheduled_date__gte=scheduled_from)
|
||
# except (ValueError, TypeError):
|
||
# pass
|
||
#
|
||
# scheduled_to = self.request.GET.get('scheduled_to', '')
|
||
# if scheduled_to:
|
||
# try:
|
||
# scheduled_to = timezone.datetime.strptime(scheduled_to, '%Y-%m-%d').date()
|
||
# queryset = queryset.filter(scheduled_date__lte=scheduled_to)
|
||
# except (ValueError, TypeError):
|
||
# pass
|
||
#
|
||
# # Rating filter
|
||
# rating = self.request.GET.get('rating', '')
|
||
# if rating and rating.isdigit():
|
||
# queryset = queryset.filter(performance_rating=int(rating))
|
||
#
|
||
# return queryset.select_related('employee', 'reviewer')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# """
|
||
# Add filter choices to context.
|
||
# """
|
||
# context = super().get_context_data(**kwargs)
|
||
# tenant = self.request.user.tenant
|
||
#
|
||
# context['search'] = self.request.GET.get('search', '')
|
||
# context['employees'] = Employee.objects.filter(
|
||
# tenant=tenant
|
||
# ).order_by('last_name', 'first_name')
|
||
# context['review_types'] = PerformanceReview.REVIEW_TYPES
|
||
# context['review_statuses'] = PerformanceReview.REVIEW_STATUS
|
||
# context['selected_employee'] = self.request.GET.get('employee', '')
|
||
# context['selected_type'] = self.request.GET.get('review_type', '')
|
||
# context['selected_status'] = self.request.GET.get('status', '')
|
||
# context['scheduled_from'] = self.request.GET.get('scheduled_from', '')
|
||
# context['scheduled_to'] = self.request.GET.get('scheduled_to', '')
|
||
# context['selected_rating'] = self.request.GET.get('rating', '')
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class PerformanceReviewDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||
# """
|
||
# Detail view for performance reviews.
|
||
# """
|
||
# model = PerformanceReview
|
||
# template_name = 'hr/performance_review_detail.html'
|
||
# context_object_name = 'review'
|
||
# permission_required = 'hr.view_performancereview'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter review by tenant.
|
||
# """
|
||
# return PerformanceReview.objects.filter(
|
||
# tenant=self.request.user.tenant
|
||
# ).select_related(
|
||
# 'employee', 'reviewer', 'created_by'
|
||
# )
|
||
#
|
||
#
|
||
# class PerformanceReviewCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create view for performance reviews.
|
||
# """
|
||
# model = PerformanceReview
|
||
# form_class = PerformanceReviewForm
|
||
# template_name = 'hr/performance_review_form.html'
|
||
# permission_required = 'hr.add_performancereview'
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
#
|
||
# # Pre-select employee if provided in GET parameters
|
||
# employee_id = self.request.GET.get('employee', None)
|
||
# if employee_id:
|
||
# kwargs['initial'] = kwargs.get('initial', {})
|
||
# kwargs['initial']['employee'] = employee_id
|
||
# kwargs['initial']['reviewer'] = self.request.user.id
|
||
#
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Set tenant and user before saving.
|
||
# """
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.created_by = self.request.user
|
||
#
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Performance review for {form.instance.employee.get_full_name()} created successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to review detail view.
|
||
# """
|
||
# return reverse('hr:performance_review_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class PerformanceReviewUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update view for performance reviews.
|
||
# """
|
||
# model = PerformanceReview
|
||
# form_class = PerformanceReviewForm
|
||
# template_name = 'hr/performance_review_form.html'
|
||
# permission_required = 'hr.change_performancereview'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter review by tenant.
|
||
# """
|
||
# return PerformanceReview.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Handle successful form validation.
|
||
# """
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Performance review for {form.instance.employee.get_full_name()} updated successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to review detail view.
|
||
# """
|
||
# return reverse('hr:performance_review_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class TrainingRecordListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||
# """
|
||
# List view for training records.
|
||
# """
|
||
# model = TrainingRecord
|
||
# template_name = 'hr/training_record_list.html'
|
||
# context_object_name = 'training_records'
|
||
# paginate_by = 20
|
||
# permission_required = 'hr.view_trainingrecord'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter training records by tenant and apply search filters.
|
||
# """
|
||
# queryset = TrainingRecord.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Apply search filters
|
||
# search = self.request.GET.get('search', '')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(employee__first_name__icontains=search) |
|
||
# Q(employee__last_name__icontains=search) |
|
||
# Q(employee__employee_number__icontains=search) |
|
||
# Q(training_title__icontains=search) |
|
||
# Q(training_description__icontains=search) |
|
||
# Q(provider__icontains=search) |
|
||
# Q(trainer__icontains=search)
|
||
# )
|
||
#
|
||
# employee_id = self.request.GET.get('employee', '')
|
||
# if employee_id:
|
||
# queryset = queryset.filter(employee_id=employee_id)
|
||
#
|
||
# training_type = self.request.GET.get('training_type', '')
|
||
# if training_type:
|
||
# queryset = queryset.filter(training_type=training_type)
|
||
#
|
||
# status = self.request.GET.get('status', '')
|
||
# if status:
|
||
# queryset = queryset.filter(status=status)
|
||
#
|
||
# is_mandatory = self.request.GET.get('is_mandatory', '')
|
||
# if is_mandatory == 'True':
|
||
# queryset = queryset.filter(is_mandatory=True)
|
||
# elif is_mandatory == 'False':
|
||
# queryset = queryset.filter(is_mandatory=False)
|
||
#
|
||
# # Date range filters
|
||
# start_from = self.request.GET.get('start_from', '')
|
||
# if start_from:
|
||
# try:
|
||
# start_from = timezone.datetime.strptime(start_from, '%Y-%m-%d').date()
|
||
# queryset = queryset.filter(start_date__gte=start_from)
|
||
# except (ValueError, TypeError):
|
||
# pass
|
||
#
|
||
# start_to = self.request.GET.get('start_to', '')
|
||
# if start_to:
|
||
# try:
|
||
# start_to = timezone.datetime.strptime(start_to, '%Y-%m-%d').date()
|
||
# queryset = queryset.filter(start_date__lte=start_to)
|
||
# except (ValueError, TypeError):
|
||
# pass
|
||
#
|
||
# # Expiry status filter
|
||
# expiry_status = self.request.GET.get('expiry_status', '')
|
||
# today = timezone.now().date()
|
||
# if expiry_status == 'valid':
|
||
# queryset = queryset.filter(
|
||
# status='COMPLETED',
|
||
# expiry_date__gt=today
|
||
# )
|
||
# elif expiry_status == 'expiring':
|
||
# queryset = queryset.filter(
|
||
# status='COMPLETED',
|
||
# expiry_date__gt=today,
|
||
# expiry_date__lte=today + timezone.timedelta(days=30)
|
||
# )
|
||
# elif expiry_status == 'expired':
|
||
# queryset = queryset.filter(
|
||
# Q(status='EXPIRED') |
|
||
# Q(status='COMPLETED', expiry_date__lt=today)
|
||
# )
|
||
#
|
||
# return queryset.select_related('employee')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# """
|
||
# Add filter choices to context.
|
||
# """
|
||
# context = super().get_context_data(**kwargs)
|
||
# tenant = self.request.user.tenant
|
||
#
|
||
# context['search'] = self.request.GET.get('search', '')
|
||
# context['employees'] = Employee.objects.filter(
|
||
# tenant=tenant
|
||
# ).order_by('last_name', 'first_name')
|
||
# context['training_statuses'] = TrainingRecord.TRAINING_STATUS
|
||
# context['selected_employee'] = self.request.GET.get('employee', '')
|
||
# context['selected_type'] = self.request.GET.get('training_type', '')
|
||
# context['selected_status'] = self.request.GET.get('status', '')
|
||
# context['selected_mandatory'] = self.request.GET.get('is_mandatory', '')
|
||
# context['start_from'] = self.request.GET.get('start_from', '')
|
||
# context['start_to'] = self.request.GET.get('start_to', '')
|
||
# context['selected_expiry'] = self.request.GET.get('expiry_status', '')
|
||
#
|
||
# # Collect unique training types from existing records
|
||
# context['training_types'] = TrainingRecord.objects.filter(
|
||
# tenant=tenant
|
||
# ).values_list('training_type', flat=True).distinct().order_by('training_type')
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class TrainingRecordDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
|
||
# """
|
||
# Detail view for training records.
|
||
# """
|
||
# model = TrainingRecord
|
||
# template_name = 'hr/training_record_detail.html'
|
||
# context_object_name = 'training_record'
|
||
# permission_required = 'hr.view_trainingrecord'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter training record by tenant.
|
||
# """
|
||
# return TrainingRecord.objects.filter(
|
||
# tenant=self.request.user.tenant
|
||
# ).select_related(
|
||
# 'employee', 'created_by'
|
||
# )
|
||
#
|
||
#
|
||
# class TrainingRecordCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create view for training records.
|
||
# """
|
||
# model = TrainingRecord
|
||
# form_class = TrainingRecordForm
|
||
# template_name = 'hr/training_record_form.html'
|
||
# permission_required = 'hr.add_trainingrecord'
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
#
|
||
# # Pre-select employee if provided in GET parameters
|
||
# employee_id = self.request.GET.get('employee', None)
|
||
# if employee_id:
|
||
# kwargs['initial'] = kwargs.get('initial', {})
|
||
# kwargs['initial']['employee'] = employee_id
|
||
#
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Set tenant and user before saving.
|
||
# """
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.created_by = self.request.user
|
||
#
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Training record '{form.instance.training_title}' for {form.instance.employee.get_full_name()} created successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to training record detail view.
|
||
# """
|
||
# return reverse('hr:training_record_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# class TrainingRecordUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update view for training records.
|
||
# """
|
||
# model = TrainingRecord
|
||
# form_class = TrainingRecordForm
|
||
# template_name = 'hr/training_record_form.html'
|
||
# permission_required = 'hr.change_trainingrecord'
|
||
#
|
||
# def get_queryset(self):
|
||
# """
|
||
# Filter training record by tenant.
|
||
# """
|
||
# return TrainingRecord.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_form_kwargs(self):
|
||
# """
|
||
# Pass tenant and user to form.
|
||
# """
|
||
# kwargs = super().get_form_kwargs()
|
||
# kwargs['tenant'] = self.request.user.tenant
|
||
# kwargs['user'] = self.request.user
|
||
# return kwargs
|
||
#
|
||
# def form_valid(self, form):
|
||
# """
|
||
# Handle successful form validation.
|
||
# """
|
||
# response = super().form_valid(form)
|
||
#
|
||
# messages.success(
|
||
# self.request,
|
||
# f"Training record '{form.instance.training_title}' updated successfully."
|
||
# )
|
||
#
|
||
# return response
|
||
#
|
||
# def get_success_url(self):
|
||
# """
|
||
# Redirect to training record detail view.
|
||
# """
|
||
# return reverse('hr:training_record_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
#
|
||
# @login_required
|
||
# def hr_stats(request):
|
||
# """
|
||
# Return HR statistics for dashboard updates.
|
||
# """
|
||
# context = {
|
||
# 'total_employees': Employee.objects.filter(tenant=request.user.tenant).count(),
|
||
# 'active_employees': Employee.objects.filter(
|
||
# tenant=request.user.tenant,
|
||
# employment_status='ACTIVE'
|
||
# ).count(),
|
||
# 'total_departments': Department.objects.filter(tenant=request.user.tenant).count(),
|
||
# 'pending_reviews': PerformanceReview.objects.filter(
|
||
# tenant=request.user.tenant,
|
||
# status='PENDING'
|
||
# ).count(),
|
||
# 'employees_clocked_in': TimeEntry.objects.filter(
|
||
# tenant=request.user.tenant,
|
||
# clock_in_time__date=timezone.now().date(),
|
||
# clock_out_time__isnull=True
|
||
# ).count(),
|
||
# }
|
||
#
|
||
# return render(request, 'hr/partials/hr_stats.html', context)
|
||
#
|
||
#
|
||
# @login_required
|
||
# def clock_in(request):
|
||
# """
|
||
# Clock in the current user.
|
||
# """
|
||
# # Check if user has an employee profile
|
||
# try:
|
||
# employee = request.user.employee_profile
|
||
# except (AttributeError, Employee.DoesNotExist):
|
||
# messages.error(request, "You don't have an employee profile. Please contact HR.")
|
||
# return redirect('hr:dashboard')
|
||
#
|
||
# # Check if already clocked in
|
||
# existing_entry = TimeEntry.objects.filter(
|
||
# tenant=request.user.tenant,
|
||
# employee=employee,
|
||
# entry_date=timezone.now().date(),
|
||
# clock_out_time__isnull=True
|
||
# ).first()
|
||
#
|
||
# if existing_entry:
|
||
# messages.warning(request, "You are already clocked in. Please clock out first.")
|
||
# return redirect('hr:dashboard')
|
||
#
|
||
# # Create new time entry
|
||
# time_entry = TimeEntry(
|
||
# tenant=request.user.tenant,
|
||
# employee=employee,
|
||
# entry_date=timezone.now().date(),
|
||
# entry_type='REGULAR',
|
||
# clock_in_time=timezone.now(),
|
||
# department=employee.department,
|
||
# created_by=request.user
|
||
# )
|
||
# time_entry.save()
|
||
#
|
||
# messages.success(request, f"Clocked in at {time_entry.clock_in_time.strftime('%H:%M:%S')}.")
|
||
# return redirect('hr:dashboard')
|
||
#
|
||
#
|
||
# @login_required
|
||
# def clock_out(request):
|
||
# """
|
||
# Clock out the current user.
|
||
# """
|
||
# # Check if user has an employee profile
|
||
# try:
|
||
# employee = request.user.employee_profile
|
||
# except (AttributeError, Employee.DoesNotExist):
|
||
# messages.error(request, "You don't have an employee profile. Please contact HR.")
|
||
# return redirect('hr:dashboard')
|
||
#
|
||
# # Find active time entry
|
||
# time_entry = TimeEntry.objects.filter(
|
||
# tenant=request.user.tenant,
|
||
# employee=employee,
|
||
# entry_date=timezone.now().date(),
|
||
# clock_out_time__isnull=True
|
||
# ).order_by('-clock_in_time').first()
|
||
#
|
||
# if not time_entry:
|
||
# messages.warning(request, "You are not clocked in. Please clock in first.")
|
||
# return redirect('hr:dashboard')
|
||
#
|
||
# # Update time entry
|
||
# time_entry.clock_out_time = timezone.now()
|
||
# time_entry.save() # This will calculate total_hours in the save method
|
||
#
|
||
# hours = time_entry.total_hours
|
||
# messages.success(
|
||
# request,
|
||
# f"Clocked out at {time_entry.clock_out_time.strftime('%H:%M:%S')}. "
|
||
# f"Total hours: {hours:.2f}"
|
||
# )
|
||
# return redirect('hr:dashboard')
|
||
#
|
||
#
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # ACTION VIEWS
|
||
# # ============================================================================
|
||
#
|
||
# @login_required
|
||
# def start_case(request, case_id):
|
||
# """
|
||
# Start a surgical case.
|
||
# """
|
||
# if request.method == 'POST':
|
||
# case = get_object_or_404(
|
||
# SurgicalCase,
|
||
# id=case_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# case.status = 'IN_PROGRESS'
|
||
# case.actual_start_time = timezone.now()
|
||
# case.save()
|
||
#
|
||
# # Update room status
|
||
# if case.operating_room:
|
||
# case.operating_room.status = 'IN_USE'
|
||
# case.operating_room.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='SURGICAL_CASE_STARTED',
|
||
# model='SurgicalCase',
|
||
# object_id=str(case.case_id),
|
||
# details={
|
||
# 'patient_name': f"{case.patient.first_name} {case.patient.last_name}",
|
||
# 'procedure_name': case.procedure_name
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(request, 'Surgical case started successfully.')
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# return render(request, 'operating_theatre/partials/case_status.html', {'case': case})
|
||
#
|
||
# return redirect('operating_theatre:surgical_case_detail', pk=case.pk)
|
||
#
|
||
# return JsonResponse({'success': False})
|
||
#
|
||
#
|
||
# @login_required
|
||
# def complete_case(request, case_id):
|
||
# """
|
||
# Complete a surgical case.
|
||
# """
|
||
# if request.method == 'POST':
|
||
# case = get_object_or_404(
|
||
# SurgicalCase,
|
||
# id=case_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# case.status = 'COMPLETED'
|
||
# case.actual_end_time = timezone.now()
|
||
# case.save()
|
||
#
|
||
# # Update room status
|
||
# if case.operating_room:
|
||
# case.operating_room.status = 'CLEANING'
|
||
# case.operating_room.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='SURGICAL_CASE_COMPLETED',
|
||
# model='SurgicalCase',
|
||
# object_id=str(case.case_id),
|
||
# details={
|
||
# 'patient_name': f"{case.patient.first_name} {case.patient.last_name}",
|
||
# 'procedure_name': case.procedure_name,
|
||
# 'duration': str(case.actual_end_time - case.actual_start_time) if case.actual_start_time else None
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(request, 'Surgical case completed successfully.')
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# return render(request, 'operating_theatre/partials/case_status.html', {'case': case})
|
||
#
|
||
# return redirect('operating_theatre:surgical_case_detail', pk=case.pk)
|
||
#
|
||
# return JsonResponse({'success': False})
|
||
# #
|
||
#
|
||
# @login_required
|
||
# def sign_note(request, note_id):
|
||
# """
|
||
# Sign a surgical note.
|
||
# """
|
||
# if request.method == 'POST':
|
||
# note = get_object_or_404(
|
||
# SurgicalNote,
|
||
# id=note_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# # Only allow signing if note is in draft status
|
||
# if note.status != 'DRAFT':
|
||
# messages.error(request, 'Only draft notes can be signed.')
|
||
# return redirect('operating_theatre:surgical_note_detail', pk=note.pk)
|
||
#
|
||
# note.status = 'SIGNED'
|
||
# note.signed_datetime = timezone.now()
|
||
# note.signed_by = request.user
|
||
# note.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='SURGICAL_NOTE_SIGNED',
|
||
# model='SurgicalNote',
|
||
# object_id=str(note.note_id),
|
||
# details={
|
||
# 'patient_name': f"{note.surgical_case.patient.first_name} {note.surgical_case.patient.last_name}",
|
||
# 'note_type': note.note_type
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(request, 'Surgical note signed successfully.')
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# return render(request, 'operating_theatre/partials/note_status.html', {'note': note})
|
||
#
|
||
# return redirect('operating_theatre:surgical_note_detail', pk=note.pk)
|
||
#
|
||
# return JsonResponse({'success': False})
|
||
#
|
||
#
|
||
# @login_required
|
||
# def update_room_status(request, room_id):
|
||
# """
|
||
# Update operating room status.
|
||
# """
|
||
# if request.method == 'POST':
|
||
# room = get_object_or_404(
|
||
# OperatingRoom,
|
||
# id=room_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# new_status = request.POST.get('status')
|
||
# if new_status in dict(OperatingRoom._meta.get_field('status').choices):
|
||
# old_status = room.status
|
||
# room.status = new_status
|
||
# room.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='OPERATING_ROOM_STATUS_UPDATED',
|
||
# model='OperatingRoom',
|
||
# object_id=str(room.id),
|
||
# details={
|
||
# 'room_number': room.room_number,
|
||
# 'old_status': old_status,
|
||
# 'new_status': new_status
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(request, f'Room {room.room_number} status updated to {room.get_status_display()}.')
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# return render(request, 'operating_theatre/partials/room_status.html', {'room': room})
|
||
#
|
||
# return redirect('operating_theatre:operating_room_detail', pk=room.pk)
|
||
#
|
||
# return JsonResponse({'success': False})
|
||
#
|
||
#
|
||
#
|
||
#
|
||
#
|
||
# """
|
||
# Operating Theatre app views with healthcare-focused CRUD operations.
|
||
# Implements appropriate access patterns for surgical and OR management workflows.
|
||
# """
|
||
#
|
||
# 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, PermissionRequiredMixin
|
||
# from django.views.generic import (
|
||
# ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView
|
||
# )
|
||
# from django.http import JsonResponse, HttpResponse
|
||
# from django.db.models import Q, Count, Avg, Sum, F
|
||
# from django.utils import timezone
|
||
# from django.contrib import messages
|
||
# from django.urls import reverse_lazy, reverse
|
||
# from django.core.paginator import Paginator
|
||
# from django.template.loader import render_to_string
|
||
# from datetime import datetime, timedelta, date
|
||
# import json
|
||
#
|
||
# from core.utils import AuditLogger
|
||
# from .models import (
|
||
# OperatingRoom, ORBlock, SurgicalCase, SurgicalNote,
|
||
# EquipmentUsage, SurgicalNoteTemplate
|
||
# )
|
||
# from .forms import (
|
||
# OperatingRoomForm, ORBlockForm, SurgicalCaseForm, SurgicalNoteForm,
|
||
# EquipmentUsageForm, SurgicalNoteTemplateForm
|
||
# )
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # DASHBOARD AND OVERVIEW VIEWS
|
||
# # ============================================================================
|
||
#
|
||
# class OperatingTheatreDashboardView(LoginRequiredMixin, TemplateView):
|
||
# """
|
||
# Main operating theatre dashboard with key metrics and recent activity.
|
||
# """
|
||
# template_name = 'operating_theatre/dashboard.html'
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# tenant = self.request.user.tenant
|
||
# today = timezone.now().date()
|
||
#
|
||
# # Dashboard statistics
|
||
# context.update({
|
||
# 'total_rooms': OperatingRoom.objects.filter(
|
||
# tenant=tenant,
|
||
# is_active=True
|
||
# ).count(),
|
||
# 'rooms_available': OperatingRoom.objects.filter(
|
||
# tenant=tenant,
|
||
# is_active=True,
|
||
# status='AVAILABLE'
|
||
# ).count(),
|
||
# 'rooms_in_use': OperatingRoom.objects.filter(
|
||
# tenant=tenant,
|
||
# is_active=True,
|
||
# status='IN_USE'
|
||
# ).count(),
|
||
# 'rooms_maintenance': OperatingRoom.objects.filter(
|
||
# tenant=tenant,
|
||
# is_active=True,
|
||
# status='MAINTENANCE'
|
||
# ).count(),
|
||
# 'cases_today': SurgicalCase.objects.filter(
|
||
# tenant=tenant,
|
||
# scheduled_start_time__date=today
|
||
# ).count(),
|
||
# 'cases_in_progress': SurgicalCase.objects.filter(
|
||
# tenant=tenant,
|
||
# status='IN_PROGRESS'
|
||
# ).count(),
|
||
# 'cases_completed_today': SurgicalCase.objects.filter(
|
||
# tenant=tenant,
|
||
# actual_end_time__date=today,
|
||
# status='COMPLETED'
|
||
# ).count(),
|
||
# 'emergency_cases_today': SurgicalCase.objects.filter(
|
||
# tenant=tenant,
|
||
# scheduled_start_time__date=today,
|
||
# priority='EMERGENCY'
|
||
# ).count(),
|
||
# 'blocks_today': ORBlock.objects.filter(
|
||
# tenant=tenant,
|
||
# date=today
|
||
# ).count(),
|
||
# 'equipment_in_use': EquipmentUsage.objects.filter(
|
||
# tenant=tenant,
|
||
# status='IN_USE'
|
||
# ).count(),
|
||
# 'notes_pending': SurgicalNote.objects.filter(
|
||
# tenant=tenant,
|
||
# status='DRAFT'
|
||
# ).count(),
|
||
# })
|
||
#
|
||
# # Recent surgical cases
|
||
# context['recent_cases'] = SurgicalCase.objects.filter(
|
||
# tenant=tenant
|
||
# ).select_related(
|
||
# 'patient', 'primary_surgeon', 'operating_room'
|
||
# ).order_by('-scheduled_start_time')[:10]
|
||
#
|
||
# # Today's schedule
|
||
# context['todays_schedule'] = SurgicalCase.objects.filter(
|
||
# tenant=tenant,
|
||
# scheduled_start_time__date=today
|
||
# ).select_related(
|
||
# 'patient', 'primary_surgeon', 'operating_room'
|
||
# ).order_by('scheduled_start_time')
|
||
#
|
||
# # Room utilization
|
||
# context['room_utilization'] = OperatingRoom.objects.filter(
|
||
# tenant=tenant,
|
||
# is_active=True
|
||
# ).annotate(
|
||
# cases_today=Count(
|
||
# 'surgical_cases',
|
||
# filter=Q(surgical_cases__scheduled_start_time__date=today)
|
||
# )
|
||
# ).order_by('room_number')
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # OPERATING ROOM VIEWS (FULL CRUD - Master Data)
|
||
# # ============================================================================
|
||
#
|
||
# class OperatingRoomListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all operating rooms with filtering and search.
|
||
# """
|
||
# model = OperatingRoom
|
||
# template_name = 'operating_theatre/operating_room_list.html'
|
||
# context_object_name = 'operating_rooms'
|
||
# paginate_by = 25
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Search functionality
|
||
# search = self.request.GET.get('search')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(room_number__icontains=search) |
|
||
# Q(room_name__icontains=search) |
|
||
# Q(location__icontains=search)
|
||
# )
|
||
#
|
||
# # Filter by room type
|
||
# room_type = self.request.GET.get('room_type')
|
||
# if room_type:
|
||
# queryset = queryset.filter(room_type=room_type)
|
||
#
|
||
# # Filter by status
|
||
# status = self.request.GET.get('status')
|
||
# if status:
|
||
# queryset = queryset.filter(status=status)
|
||
#
|
||
# # Filter by floor
|
||
# floor = self.request.GET.get('floor')
|
||
# if floor:
|
||
# queryset = queryset.filter(floor=floor)
|
||
#
|
||
# # Filter by active status
|
||
# active_only = self.request.GET.get('active_only')
|
||
# if active_only:
|
||
# queryset = queryset.filter(is_active=True)
|
||
#
|
||
# return queryset.order_by('room_number')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'room_types': OperatingRoom._meta.get_field('room_type').choices,
|
||
# 'statuses': OperatingRoom._meta.get_field('status').choices,
|
||
# 'search_query': self.request.GET.get('search', ''),
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class OperatingRoomDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about an operating room.
|
||
# """
|
||
# model = OperatingRoom
|
||
# template_name = 'operating_theatre/operating_room_detail.html'
|
||
# context_object_name = 'operating_room'
|
||
#
|
||
# def get_queryset(self):
|
||
# return OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# operating_room = self.object
|
||
# today = timezone.now().date()
|
||
#
|
||
# # Get today's cases for this room
|
||
# context['todays_cases'] = operating_room.surgical_cases.filter(
|
||
# scheduled_start_time__date=today
|
||
# ).select_related('patient', 'primary_surgeon').order_by('scheduled_start_time')
|
||
#
|
||
# # Get recent cases
|
||
# context['recent_cases'] = operating_room.surgical_cases.all().select_related(
|
||
# 'patient', 'primary_surgeon'
|
||
# ).order_by('-scheduled_start_time')[:10]
|
||
#
|
||
# # Get equipment usage
|
||
# context['equipment_usage'] = EquipmentUsage.objects.filter(
|
||
# operating_room=operating_room,
|
||
# tenant=self.request.user.tenant
|
||
# ).order_by('-start_time')[:10]
|
||
#
|
||
# # Room statistics
|
||
# context['room_stats'] = {
|
||
# 'total_cases': operating_room.surgical_cases.count(),
|
||
# 'cases_this_month': operating_room.surgical_cases.filter(
|
||
# scheduled_start_time__month=timezone.now().month,
|
||
# scheduled_start_time__year=timezone.now().year
|
||
# ).count(),
|
||
# 'average_case_duration': operating_room.surgical_cases.filter(
|
||
# actual_end_time__isnull=False
|
||
# ).aggregate(
|
||
# avg_duration=Avg(
|
||
# F('actual_end_time') - F('actual_start_time')
|
||
# )
|
||
# )['avg_duration'],
|
||
# }
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class OperatingRoomCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create a new operating room.
|
||
# """
|
||
# model = OperatingRoom
|
||
# form_class = OperatingRoomForm
|
||
# template_name = 'operating_theatre/operating_room_form.html'
|
||
# permission_required = 'operating_theatre.add_operatingroom'
|
||
# success_url = reverse_lazy('operating_theatre:operating_room_list')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='OPERATING_ROOM_CREATED',
|
||
# model='OperatingRoom',
|
||
# object_id=str(self.object.id),
|
||
# details={
|
||
# 'room_number': self.object.room_number,
|
||
# 'room_name': self.object.room_name,
|
||
# 'room_type': self.object.room_type
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, f'Operating room "{self.object.room_number}" created successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# class OperatingRoomUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update an existing operating room.
|
||
# """
|
||
# model = OperatingRoom
|
||
# form_class = OperatingRoomForm
|
||
# template_name = 'operating_theatre/operating_room_form.html'
|
||
# permission_required = 'operating_theatre.change_operatingroom'
|
||
#
|
||
# def get_queryset(self):
|
||
# return OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse('operating_theatre:operating_room_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
# def form_valid(self, form):
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='OPERATING_ROOM_UPDATED',
|
||
# model='OperatingRoom',
|
||
# object_id=str(self.object.id),
|
||
# details={
|
||
# 'room_number': self.object.room_number,
|
||
# 'changes': form.changed_data
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, f'Operating room "{self.object.room_number}" updated successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# class OperatingRoomDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
||
# """
|
||
# Delete an operating room (soft delete by deactivating).
|
||
# """
|
||
# model = OperatingRoom
|
||
# template_name = 'operating_theatre/operating_room_confirm_delete.html'
|
||
# permission_required = 'operating_theatre.delete_operatingroom'
|
||
# success_url = reverse_lazy('operating_theatre:operating_room_list')
|
||
#
|
||
# def get_queryset(self):
|
||
# return OperatingRoom.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def delete(self, request, *args, **kwargs):
|
||
# self.object = self.get_object()
|
||
#
|
||
# # Check if room has active cases
|
||
# active_cases = self.object.surgical_cases.filter(
|
||
# status__in=['SCHEDULED', 'IN_PROGRESS']
|
||
# ).count()
|
||
#
|
||
# if active_cases > 0:
|
||
# messages.error(
|
||
# request,
|
||
# f'Cannot deactivate room "{self.object.room_number}" - it has {active_cases} active cases.'
|
||
# )
|
||
# return redirect('operating_theatre:operating_room_detail', pk=self.object.pk)
|
||
#
|
||
# # Soft delete by deactivating
|
||
# self.object.is_active = False
|
||
# self.object.status = 'OUT_OF_SERVICE'
|
||
# self.object.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='OPERATING_ROOM_DEACTIVATED',
|
||
# model='OperatingRoom',
|
||
# object_id=str(self.object.id),
|
||
# details={'room_number': self.object.room_number}
|
||
# )
|
||
#
|
||
# messages.success(request, f'Operating room "{self.object.room_number}" deactivated successfully.')
|
||
# return redirect(self.success_url)
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # SURGICAL NOTE TEMPLATE VIEWS (FULL CRUD - Master Data)
|
||
# # ============================================================================
|
||
#
|
||
# class SurgicalNoteTemplateListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all surgical note templates with filtering and search.
|
||
# """
|
||
# model = SurgicalNoteTemplate
|
||
# template_name = 'operating_theatre/surgical_note_template_list.html'
|
||
# context_object_name = 'surgical_note_templates'
|
||
# paginate_by = 25
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Search functionality
|
||
# search = self.request.GET.get('search')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(template_name__icontains=search) |
|
||
# Q(procedure_type__icontains=search) |
|
||
# Q(specialty__icontains=search)
|
||
# )
|
||
#
|
||
# # Filter by specialty
|
||
# specialty = self.request.GET.get('specialty')
|
||
# if specialty:
|
||
# queryset = queryset.filter(specialty=specialty)
|
||
#
|
||
# # Filter by active status
|
||
# active_only = self.request.GET.get('active_only')
|
||
# if active_only:
|
||
# queryset = queryset.filter(is_active=True)
|
||
#
|
||
# return queryset.order_by('template_name')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'specialties': SurgicalNoteTemplate._meta.get_field('specialty').choices,
|
||
# 'search_query': self.request.GET.get('search', ''),
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class SurgicalNoteTemplateDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about a surgical note template.
|
||
# """
|
||
# model = SurgicalNoteTemplate
|
||
# template_name = 'operating_theatre/surgical_note_template_detail.html'
|
||
# context_object_name = 'surgical_note_template'
|
||
#
|
||
# def get_queryset(self):
|
||
# return SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# template = self.object
|
||
#
|
||
# # Get recent notes using this template
|
||
# context['recent_notes'] = SurgicalNote.objects.filter(
|
||
# template=template,
|
||
# tenant=self.request.user.tenant
|
||
# ).select_related('surgical_case__patient').order_by('-created_at')[:10]
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class SurgicalNoteTemplateCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create a new surgical note template.
|
||
# """
|
||
# model = SurgicalNoteTemplate
|
||
# form_class = SurgicalNoteTemplateForm
|
||
# template_name = 'operating_theatre/surgical_note_template_form.html'
|
||
# permission_required = 'operating_theatre.add_surgicalnotetemplate'
|
||
# success_url = reverse_lazy('operating_theatre:surgical_note_template_list')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.created_by = self.request.user
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='SURGICAL_NOTE_TEMPLATE_CREATED',
|
||
# model='SurgicalNoteTemplate',
|
||
# object_id=str(self.object.id),
|
||
# details={
|
||
# 'template_name': self.object.template_name,
|
||
# 'specialty': self.object.specialty
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, f'Surgical note template "{self.object.template_name}" created successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# class SurgicalNoteTemplateUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update an existing surgical note template.
|
||
# """
|
||
# model = SurgicalNoteTemplate
|
||
# form_class = SurgicalNoteTemplateForm
|
||
# template_name = 'operating_theatre/surgical_note_template_form.html'
|
||
# permission_required = 'operating_theatre.change_surgicalnotetemplate'
|
||
#
|
||
# def get_queryset(self):
|
||
# return SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse('operating_theatre:surgical_note_template_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
# def form_valid(self, form):
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='SURGICAL_NOTE_TEMPLATE_UPDATED',
|
||
# model='SurgicalNoteTemplate',
|
||
# object_id=str(self.object.id),
|
||
# details={
|
||
# 'template_name': self.object.template_name,
|
||
# 'changes': form.changed_data
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, f'Surgical note template "{self.object.template_name}" updated successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# class SurgicalNoteTemplateDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
|
||
# """
|
||
# Delete a surgical note template (soft delete by deactivating).
|
||
# """
|
||
# model = SurgicalNoteTemplate
|
||
# template_name = 'operating_theatre/surgical_note_template_confirm_delete.html'
|
||
# permission_required = 'operating_theatre.delete_surgicalnotetemplate'
|
||
# success_url = reverse_lazy('operating_theatre:surgical_note_template_list')
|
||
#
|
||
# def get_queryset(self):
|
||
# return SurgicalNoteTemplate.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def delete(self, request, *args, **kwargs):
|
||
# self.object = self.get_object()
|
||
#
|
||
# # Soft delete by deactivating
|
||
# self.object.is_active = False
|
||
# self.object.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='SURGICAL_NOTE_TEMPLATE_DEACTIVATED',
|
||
# model='SurgicalNoteTemplate',
|
||
# object_id=str(self.object.id),
|
||
# details={'template_name': self.object.template_name}
|
||
# )
|
||
#
|
||
# messages.success(request, f'Surgical note template "{self.object.template_name}" deactivated successfully.')
|
||
# return redirect(self.success_url)
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # OR BLOCK VIEWS (LIMITED CRUD - Operational Data)
|
||
# # ============================================================================
|
||
#
|
||
# class ORBlockListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all OR blocks with filtering and search.
|
||
# """
|
||
# model = ORBlock
|
||
# template_name = 'operating_theatre/or_block_list.html'
|
||
# context_object_name = 'or_blocks'
|
||
# paginate_by = 25
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = ORBlock.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Filter by date range
|
||
# date_from = self.request.GET.get('date_from')
|
||
# date_to = self.request.GET.get('date_to')
|
||
# if date_from:
|
||
# queryset = queryset.filter(date__gte=date_from)
|
||
# if date_to:
|
||
# queryset = queryset.filter(date__lte=date_to)
|
||
#
|
||
# # Filter by surgeon
|
||
# surgeon_id = self.request.GET.get('surgeon')
|
||
# if surgeon_id:
|
||
# queryset = queryset.filter(surgeon_id=surgeon_id)
|
||
#
|
||
# # Filter by operating room
|
||
# room_id = self.request.GET.get('room')
|
||
# if room_id:
|
||
# queryset = queryset.filter(operating_room_id=room_id)
|
||
#
|
||
# return queryset.select_related(
|
||
# 'operating_room', 'surgeon'
|
||
# ).order_by('-date', 'start_time')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'operating_rooms': OperatingRoom.objects.filter(
|
||
# tenant=self.request.user.tenant,
|
||
# is_active=True
|
||
# ).order_by('room_number'),
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class ORBlockDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about an OR block.
|
||
# """
|
||
# model = ORBlock
|
||
# template_name = 'operating_theatre/or_block_detail.html'
|
||
# context_object_name = 'or_block'
|
||
#
|
||
# def get_queryset(self):
|
||
# return ORBlock.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# or_block = self.object
|
||
#
|
||
# # Get cases scheduled in this block
|
||
# context['scheduled_cases'] = SurgicalCase.objects.filter(
|
||
# operating_room=or_block.operating_room,
|
||
# scheduled_start_time__date=or_block.date,
|
||
# scheduled_start_time__time__gte=or_block.start_time,
|
||
# scheduled_start_time__time__lt=or_block.end_time,
|
||
# tenant=self.request.user.tenant
|
||
# ).select_related('patient', 'primary_surgeon').order_by('scheduled_start_time')
|
||
#
|
||
# # Calculate utilization
|
||
# total_block_minutes = (
|
||
# timezone.datetime.combine(timezone.now().date(), or_block.end_time) -
|
||
# timezone.datetime.combine(timezone.now().date(), or_block.start_time)
|
||
# ).total_seconds() / 60
|
||
#
|
||
# used_minutes = 0
|
||
# for case in context['scheduled_cases']:
|
||
# if case.estimated_duration_minutes:
|
||
# used_minutes += case.estimated_duration_minutes
|
||
#
|
||
# context['utilization_percentage'] = (
|
||
# (used_minutes / total_block_minutes) * 100 if total_block_minutes > 0 else 0
|
||
# )
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class ORBlockCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create a new OR block.
|
||
# """
|
||
# model = ORBlock
|
||
# form_class = ORBlockForm
|
||
# template_name = 'operating_theatre/or_block_form.html'
|
||
# permission_required = 'operating_theatre.add_orblock'
|
||
# success_url = reverse_lazy('operating_theatre:or_block_list')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='OR_BLOCK_CREATED',
|
||
# model='ORBlock',
|
||
# object_id=str(self.object.id),
|
||
# details={
|
||
# 'date': str(self.object.date),
|
||
# 'operating_room': self.object.operating_room.room_number,
|
||
# 'surgeon': f"{self.object.surgeon.first_name} {self.object.surgeon.last_name}"
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'OR block created successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# class ORBlockUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update an OR block (limited to notes and time adjustments).
|
||
# """
|
||
# model = ORBlock
|
||
# fields = ['start_time', 'end_time', 'notes'] # Restricted fields
|
||
# template_name = 'operating_theatre/or_block_update_form.html'
|
||
# permission_required = 'operating_theatre.change_orblock'
|
||
#
|
||
# def get_queryset(self):
|
||
# return ORBlock.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse('operating_theatre:or_block_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
# def form_valid(self, form):
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='OR_BLOCK_UPDATED',
|
||
# model='ORBlock',
|
||
# object_id=str(self.object.id),
|
||
# details={
|
||
# 'date': str(self.object.date),
|
||
# 'changes': form.changed_data
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'OR block updated successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # SURGICAL CASE VIEWS (RESTRICTED CRUD - Clinical Data)
|
||
# # ============================================================================
|
||
#
|
||
# class SurgicalCaseListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all surgical cases with filtering and search.
|
||
# """
|
||
# model = SurgicalCase
|
||
# template_name = 'operating_theatre/surgical_case_list.html'
|
||
# context_object_name = 'surgical_cases'
|
||
# paginate_by = 25
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = SurgicalCase.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Search functionality
|
||
# search = self.request.GET.get('search')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(patient__first_name__icontains=search) |
|
||
# Q(patient__last_name__icontains=search) |
|
||
# Q(patient__mrn__icontains=search) |
|
||
# Q(procedure_name__icontains=search)
|
||
# )
|
||
#
|
||
# # Filter by status
|
||
# status = self.request.GET.get('status')
|
||
# if status:
|
||
# queryset = queryset.filter(status=status)
|
||
#
|
||
# # Filter by priority
|
||
# priority = self.request.GET.get('priority')
|
||
# if priority:
|
||
# queryset = queryset.filter(priority=priority)
|
||
#
|
||
# # Filter by surgeon
|
||
# surgeon_id = self.request.GET.get('surgeon')
|
||
# if surgeon_id:
|
||
# queryset = queryset.filter(primary_surgeon_id=surgeon_id)
|
||
#
|
||
# # Filter by operating room
|
||
# room_id = self.request.GET.get('room')
|
||
# if room_id:
|
||
# queryset = queryset.filter(operating_room_id=room_id)
|
||
#
|
||
# # Filter by date range
|
||
# date_from = self.request.GET.get('date_from')
|
||
# date_to = self.request.GET.get('date_to')
|
||
# if date_from:
|
||
# queryset = queryset.filter(scheduled_start_time__date__gte=date_from)
|
||
# if date_to:
|
||
# queryset = queryset.filter(scheduled_start_time__date__lte=date_to)
|
||
#
|
||
# return queryset.select_related(
|
||
# 'patient', 'primary_surgeon', 'operating_room'
|
||
# ).order_by('-scheduled_start_time')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'statuses': SurgicalCase._meta.get_field('status').choices,
|
||
# 'priorities': SurgicalCase._meta.get_field('priority').choices,
|
||
# 'operating_rooms': OperatingRoom.objects.filter(
|
||
# tenant=self.request.user.tenant,
|
||
# is_active=True
|
||
# ).order_by('room_number'),
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class SurgicalCaseDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about a surgical case.
|
||
# """
|
||
# model = SurgicalCase
|
||
# template_name = 'operating_theatre/surgical_case_detail.html'
|
||
# context_object_name = 'surgical_case'
|
||
#
|
||
# def get_queryset(self):
|
||
# return SurgicalCase.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# surgical_case = self.object
|
||
#
|
||
# # Get surgical notes for this case
|
||
# context['surgical_notes'] = surgical_case.surgical_notes.all().order_by('-created_at')
|
||
#
|
||
# # Get equipment usage for this case
|
||
# context['equipment_usage'] = EquipmentUsage.objects.filter(
|
||
# surgical_case=surgical_case,
|
||
# tenant=self.request.user.tenant
|
||
# ).order_by('-start_time')
|
||
#
|
||
# # Calculate actual duration if case is completed
|
||
# if surgical_case.actual_start_time and surgical_case.actual_end_time:
|
||
# context['actual_duration'] = surgical_case.actual_end_time - surgical_case.actual_start_time
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class SurgicalCaseCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create a new surgical case.
|
||
# """
|
||
# model = SurgicalCase
|
||
# form_class = SurgicalCaseForm
|
||
# template_name = 'operating_theatre/surgical_case_form.html'
|
||
# permission_required = 'operating_theatre.add_surgicalcase'
|
||
# success_url = reverse_lazy('operating_theatre:surgical_case_list')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='SURGICAL_CASE_CREATED',
|
||
# model='SurgicalCase',
|
||
# object_id=str(self.object.case_id),
|
||
# details={
|
||
# 'patient_name': f"{self.object.patient.first_name} {self.object.patient.last_name}",
|
||
# 'procedure_name': self.object.procedure_name,
|
||
# 'priority': self.object.priority
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'Surgical case created successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# class SurgicalCaseUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update surgical case (limited to status and notes after surgery starts).
|
||
# """
|
||
# model = SurgicalCase
|
||
# template_name = 'operating_theatre/surgical_case_update_form.html'
|
||
# permission_required = 'operating_theatre.change_surgicalcase'
|
||
#
|
||
# def get_queryset(self):
|
||
# return SurgicalCase.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_form_class(self):
|
||
# # Limit fields based on case status
|
||
# if self.object.status in ['IN_PROGRESS', 'COMPLETED']:
|
||
# # Limited fields for cases that have started
|
||
# class RestrictedSurgicalCaseForm(SurgicalCaseForm):
|
||
# class Meta(SurgicalCaseForm.Meta):
|
||
# fields = ['status', 'notes', 'complications']
|
||
#
|
||
# return RestrictedSurgicalCaseForm
|
||
# else:
|
||
# return SurgicalCaseForm
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse('operating_theatre:surgical_case_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
# def form_valid(self, form):
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='SURGICAL_CASE_UPDATED',
|
||
# model='SurgicalCase',
|
||
# object_id=str(self.object.case_id),
|
||
# details={
|
||
# 'patient_name': f"{self.object.patient.first_name} {self.object.patient.last_name}",
|
||
# 'changes': form.changed_data
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'Surgical case updated successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # SURGICAL NOTE VIEWS (APPEND-ONLY - Clinical Records)
|
||
# # ============================================================================
|
||
#
|
||
# class SurgicalNoteListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all surgical notes with filtering and search.
|
||
# """
|
||
# model = SurgicalNote
|
||
# template_name = 'operating_theatre/surgical_note_list.html'
|
||
# context_object_name = 'surgical_notes'
|
||
# paginate_by = 25
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = SurgicalNote.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Search functionality
|
||
# search = self.request.GET.get('search')
|
||
# if search:
|
||
# queryset = queryset.filter(
|
||
# Q(surgical_case__patient__first_name__icontains=search) |
|
||
# Q(surgical_case__patient__last_name__icontains=search) |
|
||
# Q(surgical_case__patient__mrn__icontains=search) |
|
||
# Q(surgical_case__procedure_name__icontains=search) |
|
||
# Q(note_content__icontains=search)
|
||
# )
|
||
#
|
||
# # Filter by note type
|
||
# note_type = self.request.GET.get('note_type')
|
||
# if note_type:
|
||
# queryset = queryset.filter(note_type=note_type)
|
||
#
|
||
# # Filter by status
|
||
# status = self.request.GET.get('status')
|
||
# if status:
|
||
# queryset = queryset.filter(status=status)
|
||
#
|
||
# # Filter by surgeon
|
||
# surgeon_id = self.request.GET.get('surgeon')
|
||
# if surgeon_id:
|
||
# queryset = queryset.filter(surgeon_id=surgeon_id)
|
||
#
|
||
# return queryset.select_related(
|
||
# 'surgical_case__patient', 'surgeon', 'template'
|
||
# ).order_by('-created_at')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'note_types': SurgicalNote._meta.get_field('note_type').choices,
|
||
# 'statuses': SurgicalNote._meta.get_field('status').choices,
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class SurgicalNoteDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about a surgical note.
|
||
# """
|
||
# model = SurgicalNote
|
||
# template_name = 'operating_theatre/surgical_note_detail.html'
|
||
# context_object_name = 'surgical_note'
|
||
#
|
||
# def get_queryset(self):
|
||
# return SurgicalNote.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
#
|
||
# class SurgicalNoteCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create a new surgical note.
|
||
# """
|
||
# model = SurgicalNote
|
||
# form_class = SurgicalNoteForm
|
||
# template_name = 'operating_theatre/surgical_note_form.html'
|
||
# permission_required = 'operating_theatre.add_surgicalnote'
|
||
# success_url = reverse_lazy('operating_theatre:surgical_note_list')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# form.instance.surgeon = self.request.user
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='SURGICAL_NOTE_CREATED',
|
||
# model='SurgicalNote',
|
||
# object_id=str(self.object.note_id),
|
||
# details={
|
||
# 'patient_name': f"{self.object.surgical_case.patient.first_name} {self.object.surgical_case.patient.last_name}",
|
||
# 'note_type': self.object.note_type
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'Surgical note created successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# # Note: No UpdateView or DeleteView for SurgicalNote - Append-only for clinical records
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # EQUIPMENT USAGE VIEWS (LIMITED CRUD - Operational Data)
|
||
# # ============================================================================
|
||
#
|
||
# class EquipmentUsageListView(LoginRequiredMixin, ListView):
|
||
# """
|
||
# List all equipment usage records with filtering and search.
|
||
# """
|
||
# model = EquipmentUsage
|
||
# template_name = 'operating_theatre/equipment_usage_list.html'
|
||
# context_object_name = 'equipment_usage_records'
|
||
# paginate_by = 25
|
||
#
|
||
# def get_queryset(self):
|
||
# queryset = EquipmentUsage.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# # Filter by equipment type
|
||
# equipment_type = self.request.GET.get('equipment_type')
|
||
# if equipment_type:
|
||
# queryset = queryset.filter(equipment_type=equipment_type)
|
||
#
|
||
# # Filter by status
|
||
# status = self.request.GET.get('status')
|
||
# if status:
|
||
# queryset = queryset.filter(status=status)
|
||
#
|
||
# # Filter by operating room
|
||
# room_id = self.request.GET.get('room')
|
||
# if room_id:
|
||
# queryset = queryset.filter(operating_room_id=room_id)
|
||
#
|
||
# # Filter by date range
|
||
# date_from = self.request.GET.get('date_from')
|
||
# date_to = self.request.GET.get('date_to')
|
||
# if date_from:
|
||
# queryset = queryset.filter(start_time__date__gte=date_from)
|
||
# if date_to:
|
||
# queryset = queryset.filter(start_time__date__lte=date_to)
|
||
#
|
||
# return queryset.select_related(
|
||
# 'operating_room', 'surgical_case__patient'
|
||
# ).order_by('-start_time')
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# context.update({
|
||
# 'equipment_types': EquipmentUsage._meta.get_field('equipment_type').choices,
|
||
# 'statuses': EquipmentUsage._meta.get_field('status').choices,
|
||
# 'operating_rooms': OperatingRoom.objects.filter(
|
||
# tenant=self.request.user.tenant,
|
||
# is_active=True
|
||
# ).order_by('room_number'),
|
||
# })
|
||
# return context
|
||
#
|
||
#
|
||
# class EquipmentUsageDetailView(LoginRequiredMixin, DetailView):
|
||
# """
|
||
# Display detailed information about equipment usage.
|
||
# """
|
||
# model = EquipmentUsage
|
||
# template_name = 'operating_theatre/equipment_usage_detail.html'
|
||
# context_object_name = 'equipment_usage'
|
||
#
|
||
# def get_queryset(self):
|
||
# return EquipmentUsage.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_context_data(self, **kwargs):
|
||
# context = super().get_context_data(**kwargs)
|
||
# equipment_usage = self.object
|
||
#
|
||
# # Calculate usage duration if ended
|
||
# if equipment_usage.end_time:
|
||
# context['usage_duration'] = equipment_usage.end_time - equipment_usage.start_time
|
||
#
|
||
# return context
|
||
#
|
||
#
|
||
# class EquipmentUsageCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
|
||
# """
|
||
# Create a new equipment usage record.
|
||
# """
|
||
# model = EquipmentUsage
|
||
# form_class = EquipmentUsageForm
|
||
# template_name = 'operating_theatre/equipment_usage_form.html'
|
||
# permission_required = 'operating_theatre.add_equipmentusage'
|
||
# success_url = reverse_lazy('operating_theatre:equipment_usage_list')
|
||
#
|
||
# def form_valid(self, form):
|
||
# form.instance.tenant = self.request.user.tenant
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='EQUIPMENT_USAGE_CREATED',
|
||
# model='EquipmentUsage',
|
||
# object_id=str(self.object.id),
|
||
# details={
|
||
# 'equipment_name': self.object.equipment_name,
|
||
# 'equipment_type': self.object.equipment_type,
|
||
# 'operating_room': self.object.operating_room.room_number
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'Equipment usage record created successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# class EquipmentUsageUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
|
||
# """
|
||
# Update equipment usage record (limited to status and end time).
|
||
# """
|
||
# model = EquipmentUsage
|
||
# fields = ['status', 'end_time', 'notes'] # Restricted fields
|
||
# template_name = 'operating_theatre/equipment_usage_update_form.html'
|
||
# permission_required = 'operating_theatre.change_equipmentusage'
|
||
#
|
||
# def get_queryset(self):
|
||
# return EquipmentUsage.objects.filter(tenant=self.request.user.tenant)
|
||
#
|
||
# def get_success_url(self):
|
||
# return reverse('operating_theatre:equipment_usage_detail', kwargs={'pk': self.object.pk})
|
||
#
|
||
# def form_valid(self, form):
|
||
# response = super().form_valid(form)
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=self.request.user,
|
||
# action='EQUIPMENT_USAGE_UPDATED',
|
||
# model='EquipmentUsage',
|
||
# object_id=str(self.object.id),
|
||
# details={
|
||
# 'equipment_name': self.object.equipment_name,
|
||
# 'changes': form.changed_data
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(self.request, 'Equipment usage record updated successfully.')
|
||
# return response
|
||
#
|
||
#
|
||
# # ============================================================================
|
||
# # HTMX VIEWS FOR REAL-TIME UPDATES
|
||
# # ============================================================================
|
||
#
|
||
# @login_required
|
||
# def operating_theatre_stats(request):
|
||
# """
|
||
# HTMX endpoint for operating theatre statistics.
|
||
# """
|
||
# tenant = request.user.tenant
|
||
# today = timezone.now().date()
|
||
#
|
||
# stats = {
|
||
# 'rooms_available': OperatingRoom.objects.filter(
|
||
# tenant=tenant,
|
||
# is_active=True,
|
||
# status='AVAILABLE'
|
||
# ).count(),
|
||
# 'rooms_in_use': OperatingRoom.objects.filter(
|
||
# tenant=tenant,
|
||
# is_active=True,
|
||
# status='IN_USE'
|
||
# ).count(),
|
||
# 'cases_in_progress': SurgicalCase.objects.filter(
|
||
# tenant=tenant,
|
||
# status='IN_PROGRESS'
|
||
# ).count(),
|
||
# 'cases_completed_today': SurgicalCase.objects.filter(
|
||
# tenant=tenant,
|
||
# actual_end_time__date=today,
|
||
# status='COMPLETED'
|
||
# ).count(),
|
||
# 'emergency_cases_today': SurgicalCase.objects.filter(
|
||
# tenant=tenant,
|
||
# scheduled_start_time__date=today,
|
||
# priority='EMERGENCY'
|
||
# ).count(),
|
||
# }
|
||
#
|
||
# return render(request, 'operating_theatre/partials/or_stats.html', {'stats': stats})
|
||
#
|
||
#
|
||
# @login_required
|
||
# def start_case(request, case_id):
|
||
# """
|
||
# Start a surgical case.
|
||
# """
|
||
# if request.method == 'POST':
|
||
# case = get_object_or_404(
|
||
# SurgicalCase,
|
||
# id=case_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# case.status = 'IN_PROGRESS'
|
||
# case.actual_start_time = timezone.now()
|
||
# case.save()
|
||
#
|
||
# # Update room status
|
||
# if case.operating_room:
|
||
# case.operating_room.status = 'IN_USE'
|
||
# case.operating_room.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='SURGICAL_CASE_STARTED',
|
||
# model='SurgicalCase',
|
||
# object_id=str(case.case_id),
|
||
# details={
|
||
# 'patient_name': f"{case.patient.first_name} {case.patient.last_name}",
|
||
# 'procedure_name': case.procedure_name
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(request, 'Surgical case started successfully.')
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# return render(request, 'operating_theatre/partials/case_status.html', {'case': case})
|
||
#
|
||
# return redirect('operating_theatre:surgical_case_detail', pk=case.pk)
|
||
#
|
||
# return JsonResponse({'success': False})
|
||
#
|
||
#
|
||
# @login_required
|
||
# def complete_case(request, case_id):
|
||
# """
|
||
# Complete a surgical case.
|
||
# """
|
||
# if request.method == 'POST':
|
||
# case = get_object_or_404(
|
||
# SurgicalCase,
|
||
# id=case_id,
|
||
# tenant=request.user.tenant
|
||
# )
|
||
#
|
||
# case.status = 'COMPLETED'
|
||
# case.actual_end_time = timezone.now()
|
||
# case.save()
|
||
#
|
||
# # Update room status
|
||
# if case.operating_room:
|
||
# case.operating_room.status = 'CLEANING'
|
||
# case.operating_room.save()
|
||
#
|
||
# # Log the action
|
||
# AuditLogger.log_action(
|
||
# user=request.user,
|
||
# action='SURGICAL_CASE_COMPLETED',
|
||
# model='SurgicalCase',
|
||
# object_id=str(case.case_id),
|
||
# details={
|
||
# 'patient_name': f"{case.patient.first_name} {case.patient.last_name}",
|
||
# 'procedure_name': case.procedure_name,
|
||
# 'duration': str(case.actual_end_time - case.actual_start_time) if case.actual_start_time else None
|
||
# }
|
||
# )
|
||
#
|
||
# messages.success(request, 'Surgical case completed successfully.')
|
||
#
|
||
# if request.headers.get('HX-Request'):
|
||
# return render(request, 'operating_theatre/partials/case_status.html', {'case': case})
|
||
#
|
||
# return redirect('operating_theatre:surgical_case_detail', pk=case.pk)
|
||
#
|
||
# return JsonResponse({'success': False}) |