Marwan Alwali ab2c4a36c5 update
2025-10-02 10:13:03 +03:00

2658 lines
89 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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})
#
#
#
#