190 lines
6.0 KiB
Python
190 lines
6.0 KiB
Python
"""
|
|
Constants for radiology app.
|
|
Centralized choices and validators for consistency across models.
|
|
"""
|
|
|
|
from django.db import models
|
|
from django.core.validators import RegexValidator
|
|
|
|
|
|
class RadiologyChoices:
|
|
"""Centralized choices for radiology models."""
|
|
|
|
class Modality(models.TextChoices):
|
|
CR = 'CR', 'Computed Radiography'
|
|
CT = 'CT', 'Computed Tomography'
|
|
MR = 'MR', 'Magnetic Resonance'
|
|
US = 'US', 'Ultrasound'
|
|
XA = 'XA', 'X-Ray Angiography'
|
|
RF = 'RF', 'Radiofluoroscopy'
|
|
DX = 'DX', 'Digital Radiography'
|
|
MG = 'MG', 'Mammography'
|
|
NM = 'NM', 'Nuclear Medicine'
|
|
PT = 'PT', 'Positron Emission Tomography'
|
|
OT = 'OT', 'Other'
|
|
|
|
class BodyPart(models.TextChoices):
|
|
HEAD = 'HEAD', 'Head'
|
|
NECK = 'NECK', 'Neck'
|
|
CHEST = 'CHEST', 'Chest'
|
|
ABDOMEN = 'ABDOMEN', 'Abdomen'
|
|
PELVIS = 'PELVIS', 'Pelvis'
|
|
SPINE = 'SPINE', 'Spine'
|
|
EXTREMITY = 'EXTREMITY', 'Extremity'
|
|
BREAST = 'BREAST', 'Breast'
|
|
HEART = 'HEART', 'Heart'
|
|
BRAIN = 'BRAIN', 'Brain'
|
|
WHOLE_BODY = 'WHOLE_BODY', 'Whole Body'
|
|
OTHER = 'OTHER', 'Other'
|
|
|
|
class Priority(models.TextChoices):
|
|
ROUTINE = 'ROUTINE', 'Routine'
|
|
URGENT = 'URGENT', 'Urgent'
|
|
STAT = 'STAT', 'STAT'
|
|
EMERGENCY = 'EMERGENCY', 'Emergency'
|
|
|
|
class ImageQuality(models.TextChoices):
|
|
EXCELLENT = 'EXCELLENT', 'Excellent'
|
|
GOOD = 'GOOD', 'Good'
|
|
ADEQUATE = 'ADEQUATE', 'Adequate'
|
|
POOR = 'POOR', 'Poor'
|
|
UNACCEPTABLE = 'UNACCEPTABLE', 'Unacceptable'
|
|
|
|
class StudyStatus(models.TextChoices):
|
|
SCHEDULED = 'SCHEDULED', 'Scheduled'
|
|
ARRIVED = 'ARRIVED', 'Arrived'
|
|
IN_PROGRESS = 'IN_PROGRESS', 'In Progress'
|
|
COMPLETED = 'COMPLETED', 'Completed'
|
|
INTERPRETED = 'INTERPRETED', 'Interpreted'
|
|
FINALIZED = 'FINALIZED', 'Finalized'
|
|
CANCELLED = 'CANCELLED', 'Cancelled'
|
|
|
|
class OrderStatus(models.TextChoices):
|
|
PENDING = 'PENDING', 'Pending'
|
|
SCHEDULED = 'SCHEDULED', 'Scheduled'
|
|
IN_PROGRESS = 'IN_PROGRESS', 'In Progress'
|
|
COMPLETED = 'COMPLETED', 'Completed'
|
|
CANCELLED = 'CANCELLED', 'Cancelled'
|
|
ON_HOLD = 'ON_HOLD', 'On Hold'
|
|
|
|
class ReportStatus(models.TextChoices):
|
|
DRAFT = 'DRAFT', 'Draft'
|
|
PRELIMINARY = 'PRELIMINARY', 'Preliminary'
|
|
FINAL = 'FINAL', 'Final'
|
|
AMENDED = 'AMENDED', 'Amended'
|
|
CORRECTED = 'CORRECTED', 'Corrected'
|
|
|
|
class CompletionStatus(models.TextChoices):
|
|
COMPLETE = 'COMPLETE', 'Complete'
|
|
PARTIAL = 'PARTIAL', 'Partial'
|
|
INCOMPLETE = 'INCOMPLETE', 'Incomplete'
|
|
|
|
class PatientPosition(models.TextChoices):
|
|
HFP = 'HFP', 'Head First-Prone'
|
|
HFS = 'HFS', 'Head First-Supine'
|
|
HFDR = 'HFDR', 'Head First-Decubitus Right'
|
|
HFDL = 'HFDL', 'Head First-Decubitus Left'
|
|
FFP = 'FFP', 'Feet First-Prone'
|
|
FFS = 'FFS', 'Feet First-Supine'
|
|
FFDR = 'FFDR', 'Feet First-Decubitus Right'
|
|
FFDL = 'FFDL', 'Feet First-Decubitus Left'
|
|
|
|
class ContrastRoute(models.TextChoices):
|
|
IV = 'IV', 'Intravenous'
|
|
ORAL = 'ORAL', 'Oral'
|
|
RECTAL = 'RECTAL', 'Rectal'
|
|
INTRATHECAL = 'INTRATHECAL', 'Intrathecal'
|
|
INTRA_ARTICULAR = 'INTRA_ARTICULAR', 'Intra-articular'
|
|
OTHER = 'OTHER', 'Other'
|
|
|
|
|
|
class RadiologyValidators:
|
|
"""Validators for radiology-specific fields."""
|
|
|
|
# DICOM UID validator - must be numeric with dots
|
|
dicom_uid_validator = RegexValidator(
|
|
regex=r'^[0-9]+(\.[0-9]+)*$',
|
|
message='Invalid DICOM UID format. Must contain only numbers and dots.'
|
|
)
|
|
|
|
# Accession number validator
|
|
accession_number_validator = RegexValidator(
|
|
regex=r'^[A-Z]{2,4}-\d{8}-\d{4}$',
|
|
message='Invalid accession number format. Expected format: RAD-YYYYMMDD-NNNN'
|
|
)
|
|
|
|
# Order number validator
|
|
order_number_validator = RegexValidator(
|
|
regex=r'^[A-Z]{3}-\d+-\d{6}$',
|
|
message='Invalid order number format. Expected format: IMG-TENANT-NNNNNN'
|
|
)
|
|
|
|
|
|
# DICOM Standard Constants
|
|
class DICOMConstants:
|
|
"""DICOM standard constants and mappings."""
|
|
|
|
# Common SOP Class UIDs
|
|
SOP_CLASS_UIDS = {
|
|
'CT_IMAGE': '1.2.840.10008.5.1.4.1.1.2',
|
|
'MR_IMAGE': '1.2.840.10008.5.1.4.1.1.4',
|
|
'US_IMAGE': '1.2.840.10008.5.1.4.1.1.6.1',
|
|
'CR_IMAGE': '1.2.840.10008.5.1.4.1.1.1',
|
|
'DX_IMAGE': '1.2.840.10008.5.1.4.1.1.1.1',
|
|
}
|
|
|
|
# Transfer Syntax UIDs
|
|
TRANSFER_SYNTAX_UIDS = {
|
|
'IMPLICIT_VR_LITTLE_ENDIAN': '1.2.840.10008.1.2',
|
|
'EXPLICIT_VR_LITTLE_ENDIAN': '1.2.840.10008.1.2.1',
|
|
'EXPLICIT_VR_BIG_ENDIAN': '1.2.840.10008.1.2.2',
|
|
'JPEG_BASELINE': '1.2.840.10008.1.2.4.50',
|
|
'JPEG_LOSSLESS': '1.2.840.10008.1.2.4.70',
|
|
}
|
|
|
|
# Standard image types
|
|
IMAGE_TYPES = {
|
|
'ORIGINAL': 'Original image',
|
|
'DERIVED': 'Derived image',
|
|
'SECONDARY': 'Secondary capture',
|
|
}
|
|
|
|
|
|
# Business Rules Constants
|
|
class RadiologyBusinessRules:
|
|
"""Business rules and constraints for radiology workflow."""
|
|
|
|
# Status transition rules
|
|
VALID_STATUS_TRANSITIONS = {
|
|
'SCHEDULED': ['ARRIVED', 'CANCELLED'],
|
|
'ARRIVED': ['IN_PROGRESS', 'CANCELLED'],
|
|
'IN_PROGRESS': ['COMPLETED', 'CANCELLED'],
|
|
'COMPLETED': ['INTERPRETED'],
|
|
'INTERPRETED': ['FINALIZED'],
|
|
'FINALIZED': ['AMENDED'],
|
|
'CANCELLED': [], # Terminal state
|
|
}
|
|
|
|
# Priority escalation rules
|
|
PRIORITY_ESCALATION_HOURS = {
|
|
'ROUTINE': 24,
|
|
'URGENT': 4,
|
|
'STAT': 1,
|
|
'EMERGENCY': 0.5,
|
|
}
|
|
|
|
# Critical finding communication requirements
|
|
CRITICAL_COMMUNICATION_TIMEFRAMES = {
|
|
'EMERGENCY': 15, # minutes
|
|
'STAT': 30, # minutes
|
|
'URGENT': 60, # minutes
|
|
'ROUTINE': 120, # minutes
|
|
}
|
|
|
|
# Quality thresholds
|
|
QUALITY_THRESHOLDS = {
|
|
'MIN_ACCEPTABLE': 'ADEQUATE',
|
|
'PREFERRED': 'GOOD',
|
|
'EXCELLENT': 'EXCELLENT',
|
|
}
|