2658 lines
89 KiB
Plaintext
2658 lines
89 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})
|
||
#
|
||
#
|
||
#
|
||
# |