1
This commit is contained in:
parent
fef9abdd4f
commit
7d56370811
@ -31,7 +31,7 @@ class StageStatus(BaseChoices):
|
|||||||
class PatientJourneyTemplate(UUIDModel, TimeStampedModel):
|
class PatientJourneyTemplate(UUIDModel, TimeStampedModel):
|
||||||
"""
|
"""
|
||||||
Journey template defines the stages for a patient journey type.
|
Journey template defines the stages for a patient journey type.
|
||||||
|
|
||||||
Example: OPD journey with stages:
|
Example: OPD journey with stages:
|
||||||
1. MD Consultation (trigger: OPD_VISIT_COMPLETED)
|
1. MD Consultation (trigger: OPD_VISIT_COMPLETED)
|
||||||
2. Lab (trigger: LAB_ORDER_COMPLETED)
|
2. Lab (trigger: LAB_ORDER_COMPLETED)
|
||||||
@ -46,7 +46,7 @@ class PatientJourneyTemplate(UUIDModel, TimeStampedModel):
|
|||||||
db_index=True
|
db_index=True
|
||||||
)
|
)
|
||||||
description = models.TextField(blank=True)
|
description = models.TextField(blank=True)
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
hospital = models.ForeignKey(
|
hospital = models.ForeignKey(
|
||||||
'organizations.Hospital',
|
'organizations.Hospital',
|
||||||
@ -59,14 +59,14 @@ class PatientJourneyTemplate(UUIDModel, TimeStampedModel):
|
|||||||
default=False,
|
default=False,
|
||||||
help_text="Default template for this journey type in this hospital"
|
help_text="Default template for this journey type in this hospital"
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['hospital', 'journey_type', 'name']
|
ordering = ['hospital', 'journey_type', 'name']
|
||||||
unique_together = [['hospital', 'journey_type', 'name']]
|
unique_together = [['hospital', 'journey_type', 'name']]
|
||||||
indexes = [
|
indexes = [
|
||||||
models.Index(fields=['hospital', 'journey_type', 'is_active']),
|
models.Index(fields=['hospital', 'journey_type', 'is_active']),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.hospital.name} - {self.get_journey_type_display()} - {self.name}"
|
return f"{self.hospital.name} - {self.get_journey_type_display()} - {self.name}"
|
||||||
|
|
||||||
@ -74,12 +74,12 @@ class PatientJourneyTemplate(UUIDModel, TimeStampedModel):
|
|||||||
class PatientJourneyStageTemplate(UUIDModel, TimeStampedModel):
|
class PatientJourneyStageTemplate(UUIDModel, TimeStampedModel):
|
||||||
"""
|
"""
|
||||||
Stage template defines a stage within a journey.
|
Stage template defines a stage within a journey.
|
||||||
|
|
||||||
Each stage:
|
Each stage:
|
||||||
- Has a trigger_event_code that maps to integration events
|
- Has a trigger_event_code that maps to integration events
|
||||||
- Can optionally send a survey when completed
|
- Can optionally send a survey when completed
|
||||||
- Has an order for sequencing
|
- Has an order for sequencing
|
||||||
|
|
||||||
Example: "MD Consultation" stage
|
Example: "MD Consultation" stage
|
||||||
- trigger_event_code: "OPD_VISIT_COMPLETED"
|
- trigger_event_code: "OPD_VISIT_COMPLETED"
|
||||||
- survey_template: MD Consultation Survey
|
- survey_template: MD Consultation Survey
|
||||||
@ -90,7 +90,7 @@ class PatientJourneyStageTemplate(UUIDModel, TimeStampedModel):
|
|||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='stages'
|
related_name='stages'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Stage identification
|
# Stage identification
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
name_ar = models.CharField(max_length=200, blank=True)
|
name_ar = models.CharField(max_length=200, blank=True)
|
||||||
@ -102,14 +102,14 @@ class PatientJourneyStageTemplate(UUIDModel, TimeStampedModel):
|
|||||||
default=0,
|
default=0,
|
||||||
help_text="Order of this stage in the journey"
|
help_text="Order of this stage in the journey"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Event trigger configuration
|
# Event trigger configuration
|
||||||
trigger_event_code = models.CharField(
|
trigger_event_code = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
help_text="Event code that triggers completion of this stage (e.g., OPD_VISIT_COMPLETED)",
|
help_text="Event code that triggers completion of this stage (e.g., OPD_VISIT_COMPLETED)",
|
||||||
db_index=True
|
db_index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Survey configuration
|
# Survey configuration
|
||||||
survey_template = models.ForeignKey(
|
survey_template = models.ForeignKey(
|
||||||
'surveys.SurveyTemplate',
|
'surveys.SurveyTemplate',
|
||||||
@ -127,7 +127,7 @@ class PatientJourneyStageTemplate(UUIDModel, TimeStampedModel):
|
|||||||
default=0,
|
default=0,
|
||||||
help_text="Hours to wait before sending survey (0 = immediate)"
|
help_text="Hours to wait before sending survey (0 = immediate)"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Requirements
|
# Requirements
|
||||||
requires_physician = models.BooleanField(
|
requires_physician = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
@ -137,16 +137,16 @@ class PatientJourneyStageTemplate(UUIDModel, TimeStampedModel):
|
|||||||
default=False,
|
default=False,
|
||||||
help_text="Does this stage require department information?"
|
help_text="Does this stage require department information?"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
is_optional = models.BooleanField(
|
is_optional = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
help_text="Can this stage be skipped?"
|
help_text="Can this stage be skipped?"
|
||||||
)
|
)
|
||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
|
|
||||||
description = models.TextField(blank=True)
|
description = models.TextField(blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['journey_template', 'order']
|
ordering = ['journey_template', 'order']
|
||||||
unique_together = [['journey_template', 'code']]
|
unique_together = [['journey_template', 'code']]
|
||||||
@ -154,7 +154,7 @@ class PatientJourneyStageTemplate(UUIDModel, TimeStampedModel):
|
|||||||
models.Index(fields=['journey_template', 'order']),
|
models.Index(fields=['journey_template', 'order']),
|
||||||
models.Index(fields=['trigger_event_code']),
|
models.Index(fields=['trigger_event_code']),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.journey_template.name} - {self.name} (Order: {self.order})"
|
return f"{self.journey_template.name} - {self.name} (Order: {self.order})"
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ class PatientJourneyStageTemplate(UUIDModel, TimeStampedModel):
|
|||||||
class PatientJourneyInstance(UUIDModel, TimeStampedModel):
|
class PatientJourneyInstance(UUIDModel, TimeStampedModel):
|
||||||
"""
|
"""
|
||||||
Journey instance tracks an actual patient's journey through a pathway.
|
Journey instance tracks an actual patient's journey through a pathway.
|
||||||
|
|
||||||
Linked to:
|
Linked to:
|
||||||
- Journey template (defines the stages)
|
- Journey template (defines the stages)
|
||||||
- Patient (who is going through the journey)
|
- Patient (who is going through the journey)
|
||||||
@ -173,7 +173,7 @@ class PatientJourneyInstance(UUIDModel, TimeStampedModel):
|
|||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='instances'
|
related_name='instances'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Patient and encounter information
|
# Patient and encounter information
|
||||||
patient = models.ForeignKey(
|
patient = models.ForeignKey(
|
||||||
'organizations.Patient',
|
'organizations.Patient',
|
||||||
@ -186,7 +186,7 @@ class PatientJourneyInstance(UUIDModel, TimeStampedModel):
|
|||||||
db_index=True,
|
db_index=True,
|
||||||
help_text="Unique encounter ID from HIS system"
|
help_text="Unique encounter ID from HIS system"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Journey metadata
|
# Journey metadata
|
||||||
hospital = models.ForeignKey(
|
hospital = models.ForeignKey(
|
||||||
'organizations.Hospital',
|
'organizations.Hospital',
|
||||||
@ -200,7 +200,7 @@ class PatientJourneyInstance(UUIDModel, TimeStampedModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
related_name='journey_instances'
|
related_name='journey_instances'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Status tracking
|
# Status tracking
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
@ -208,18 +208,18 @@ class PatientJourneyInstance(UUIDModel, TimeStampedModel):
|
|||||||
default=StatusChoices.ACTIVE,
|
default=StatusChoices.ACTIVE,
|
||||||
db_index=True
|
db_index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Timestamps
|
# Timestamps
|
||||||
started_at = models.DateTimeField(auto_now_add=True)
|
started_at = models.DateTimeField(auto_now_add=True)
|
||||||
completed_at = models.DateTimeField(null=True, blank=True)
|
completed_at = models.DateTimeField(null=True, blank=True)
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
metadata = models.JSONField(
|
metadata = models.JSONField(
|
||||||
default=dict,
|
default=dict,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Additional metadata from HIS system"
|
help_text="Additional metadata from HIS system"
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['-started_at']
|
ordering = ['-started_at']
|
||||||
indexes = [
|
indexes = [
|
||||||
@ -227,10 +227,10 @@ class PatientJourneyInstance(UUIDModel, TimeStampedModel):
|
|||||||
models.Index(fields=['patient', '-started_at']),
|
models.Index(fields=['patient', '-started_at']),
|
||||||
models.Index(fields=['hospital', 'status', '-started_at']),
|
models.Index(fields=['hospital', 'status', '-started_at']),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.patient.get_full_name()} - {self.journey_template.name} ({self.encounter_id})"
|
return f"{self.patient.get_full_name()} - {self.journey_template.name} ({self.encounter_id})"
|
||||||
|
|
||||||
def get_completion_percentage(self):
|
def get_completion_percentage(self):
|
||||||
"""Calculate journey completion percentage"""
|
"""Calculate journey completion percentage"""
|
||||||
total_stages = self.stage_instances.count()
|
total_stages = self.stage_instances.count()
|
||||||
@ -238,7 +238,7 @@ class PatientJourneyInstance(UUIDModel, TimeStampedModel):
|
|||||||
return 0
|
return 0
|
||||||
completed_stages = self.stage_instances.filter(status=StageStatus.COMPLETED).count()
|
completed_stages = self.stage_instances.filter(status=StageStatus.COMPLETED).count()
|
||||||
return int((completed_stages / total_stages) * 100)
|
return int((completed_stages / total_stages) * 100)
|
||||||
|
|
||||||
def is_complete(self):
|
def is_complete(self):
|
||||||
"""Check if all required stages are completed"""
|
"""Check if all required stages are completed"""
|
||||||
required_stages = self.stage_instances.filter(
|
required_stages = self.stage_instances.filter(
|
||||||
@ -250,14 +250,14 @@ class PatientJourneyInstance(UUIDModel, TimeStampedModel):
|
|||||||
class PatientJourneyStageInstance(UUIDModel, TimeStampedModel):
|
class PatientJourneyStageInstance(UUIDModel, TimeStampedModel):
|
||||||
"""
|
"""
|
||||||
Stage instance tracks completion of a specific stage in a patient's journey.
|
Stage instance tracks completion of a specific stage in a patient's journey.
|
||||||
|
|
||||||
Status flow:
|
Status flow:
|
||||||
1. PENDING - Stage created but not started
|
1. PENDING - Stage created but not started
|
||||||
2. IN_PROGRESS - Event received, processing
|
2. IN_PROGRESS - Event received, processing
|
||||||
3. COMPLETED - Stage completed successfully
|
3. COMPLETED - Stage completed successfully
|
||||||
4. SKIPPED - Stage skipped (optional stages only)
|
4. SKIPPED - Stage skipped (optional stages only)
|
||||||
5. CANCELLED - Journey cancelled
|
5. CANCELLED - Journey cancelled
|
||||||
|
|
||||||
When completed:
|
When completed:
|
||||||
- If auto_send_survey=True, create SurveyInstance
|
- If auto_send_survey=True, create SurveyInstance
|
||||||
- Record completion timestamp
|
- Record completion timestamp
|
||||||
@ -273,7 +273,7 @@ class PatientJourneyStageInstance(UUIDModel, TimeStampedModel):
|
|||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
related_name='instances'
|
related_name='instances'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
@ -281,7 +281,7 @@ class PatientJourneyStageInstance(UUIDModel, TimeStampedModel):
|
|||||||
default=StageStatus.PENDING,
|
default=StageStatus.PENDING,
|
||||||
db_index=True
|
db_index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Completion details
|
# Completion details
|
||||||
completed_at = models.DateTimeField(null=True, blank=True, db_index=True)
|
completed_at = models.DateTimeField(null=True, blank=True, db_index=True)
|
||||||
completed_by_event = models.ForeignKey(
|
completed_by_event = models.ForeignKey(
|
||||||
@ -292,7 +292,7 @@ class PatientJourneyStageInstance(UUIDModel, TimeStampedModel):
|
|||||||
related_name='completed_stages',
|
related_name='completed_stages',
|
||||||
help_text="Integration event that completed this stage"
|
help_text="Integration event that completed this stage"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Context from event
|
# Context from event
|
||||||
physician = models.ForeignKey(
|
physician = models.ForeignKey(
|
||||||
'organizations.Physician',
|
'organizations.Physician',
|
||||||
@ -310,7 +310,7 @@ class PatientJourneyStageInstance(UUIDModel, TimeStampedModel):
|
|||||||
related_name='journey_stages',
|
related_name='journey_stages',
|
||||||
help_text="Department where this stage occurred"
|
help_text="Department where this stage occurred"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Survey tracking
|
# Survey tracking
|
||||||
survey_instance = models.ForeignKey(
|
survey_instance = models.ForeignKey(
|
||||||
'surveys.SurveyInstance',
|
'surveys.SurveyInstance',
|
||||||
@ -321,14 +321,14 @@ class PatientJourneyStageInstance(UUIDModel, TimeStampedModel):
|
|||||||
help_text="Survey instance created for this stage"
|
help_text="Survey instance created for this stage"
|
||||||
)
|
)
|
||||||
survey_sent_at = models.DateTimeField(null=True, blank=True)
|
survey_sent_at = models.DateTimeField(null=True, blank=True)
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
metadata = models.JSONField(
|
metadata = models.JSONField(
|
||||||
default=dict,
|
default=dict,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Additional data from integration event"
|
help_text="Additional data from integration event"
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['journey_instance', 'stage_template__order']
|
ordering = ['journey_instance', 'stage_template__order']
|
||||||
unique_together = [['journey_instance', 'stage_template']]
|
unique_together = [['journey_instance', 'stage_template']]
|
||||||
@ -336,18 +336,18 @@ class PatientJourneyStageInstance(UUIDModel, TimeStampedModel):
|
|||||||
models.Index(fields=['journey_instance', 'status']),
|
models.Index(fields=['journey_instance', 'status']),
|
||||||
models.Index(fields=['status', 'completed_at']),
|
models.Index(fields=['status', 'completed_at']),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.journey_instance.encounter_id} - {self.stage_template.name} ({self.status})"
|
return f"{self.journey_instance.encounter_id} - {self.stage_template.name} ({self.status})"
|
||||||
|
|
||||||
def can_complete(self):
|
def can_complete(self):
|
||||||
"""Check if this stage can be completed"""
|
"""Check if this stage can be completed"""
|
||||||
return self.status in [StageStatus.PENDING, StageStatus.IN_PROGRESS]
|
return self.status in [StageStatus.PENDING, StageStatus.IN_PROGRESS]
|
||||||
|
|
||||||
def complete(self, event=None, physician=None, department=None, metadata=None):
|
def complete(self, event=None, physician=None, department=None, metadata=None):
|
||||||
"""
|
"""
|
||||||
Mark stage as completed.
|
Mark stage as completed.
|
||||||
|
|
||||||
This method should be called by the event processing task.
|
This method should be called by the event processing task.
|
||||||
It will:
|
It will:
|
||||||
1. Update status to COMPLETED
|
1. Update status to COMPLETED
|
||||||
@ -356,27 +356,27 @@ class PatientJourneyStageInstance(UUIDModel, TimeStampedModel):
|
|||||||
4. Trigger survey creation if configured
|
4. Trigger survey creation if configured
|
||||||
"""
|
"""
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
if not self.can_complete():
|
if not self.can_complete():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.status = StageStatus.COMPLETED
|
self.status = StageStatus.COMPLETED
|
||||||
self.completed_at = timezone.now()
|
self.completed_at = timezone.now()
|
||||||
self.completed_by_event = event
|
self.completed_by_event = event
|
||||||
|
|
||||||
if physician:
|
if physician:
|
||||||
self.physician = physician
|
self.physician = physician
|
||||||
if department:
|
if department:
|
||||||
self.department = department
|
self.department = department
|
||||||
if metadata:
|
if metadata:
|
||||||
self.metadata.update(metadata)
|
self.metadata.update(metadata)
|
||||||
|
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
# Check if journey is complete
|
# Check if journey is complete
|
||||||
if self.journey_instance.is_complete():
|
if self.journey_instance.is_complete():
|
||||||
self.journey_instance.status = StatusChoices.COMPLETED
|
self.journey_instance.status = StatusChoices.COMPLETED
|
||||||
self.journey_instance.completed_at = timezone.now()
|
self.journey_instance.completed_at = timezone.now()
|
||||||
self.journey_instance.save()
|
self.journey_instance.save()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
@ -11,13 +11,13 @@ class Hospital(UUIDModel, TimeStampedModel):
|
|||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
name_ar = models.CharField(max_length=200, blank=True, verbose_name="Name (Arabic)")
|
name_ar = models.CharField(max_length=200, blank=True, verbose_name="Name (Arabic)")
|
||||||
code = models.CharField(max_length=50, unique=True, db_index=True)
|
code = models.CharField(max_length=50, unique=True, db_index=True)
|
||||||
|
|
||||||
# Contact information
|
# Contact information
|
||||||
address = models.TextField(blank=True)
|
address = models.TextField(blank=True)
|
||||||
city = models.CharField(max_length=100, blank=True)
|
city = models.CharField(max_length=100, blank=True)
|
||||||
phone = models.CharField(max_length=20, blank=True)
|
phone = models.CharField(max_length=20, blank=True)
|
||||||
email = models.EmailField(blank=True)
|
email = models.EmailField(blank=True)
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
@ -25,28 +25,30 @@ class Hospital(UUIDModel, TimeStampedModel):
|
|||||||
default=StatusChoices.ACTIVE,
|
default=StatusChoices.ACTIVE,
|
||||||
db_index=True
|
db_index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
license_number = models.CharField(max_length=100, blank=True)
|
license_number = models.CharField(max_length=100, blank=True)
|
||||||
capacity = models.IntegerField(null=True, blank=True, help_text="Bed capacity")
|
capacity = models.IntegerField(null=True, blank=True, help_text="Bed capacity")
|
||||||
|
|
||||||
metadata = models.JSONField(default=dict, blank=True)
|
metadata = models.JSONField(default=dict, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
verbose_name_plural = 'Hospitals'
|
verbose_name_plural = 'Hospitals'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
# TODO: Add branch
|
||||||
|
|
||||||
class Department(UUIDModel, TimeStampedModel):
|
class Department(UUIDModel, TimeStampedModel):
|
||||||
"""Department within a hospital"""
|
"""Department within a hospital"""
|
||||||
hospital = models.ForeignKey(Hospital, on_delete=models.CASCADE, related_name='departments')
|
hospital = models.ForeignKey(Hospital, on_delete=models.CASCADE, related_name='departments')
|
||||||
|
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
name_ar = models.CharField(max_length=200, blank=True, verbose_name="Name (Arabic)")
|
name_ar = models.CharField(max_length=200, blank=True, verbose_name="Name (Arabic)")
|
||||||
code = models.CharField(max_length=50, db_index=True)
|
code = models.CharField(max_length=50, db_index=True)
|
||||||
|
|
||||||
# Hierarchy
|
# Hierarchy
|
||||||
parent = models.ForeignKey(
|
parent = models.ForeignKey(
|
||||||
'self',
|
'self',
|
||||||
@ -55,7 +57,7 @@ class Department(UUIDModel, TimeStampedModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
related_name='sub_departments'
|
related_name='sub_departments'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Manager
|
# Manager
|
||||||
manager = models.ForeignKey(
|
manager = models.ForeignKey(
|
||||||
'accounts.User',
|
'accounts.User',
|
||||||
@ -64,12 +66,12 @@ class Department(UUIDModel, TimeStampedModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
related_name='managed_departments'
|
related_name='managed_departments'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Contact
|
# Contact
|
||||||
phone = models.CharField(max_length=20, blank=True)
|
phone = models.CharField(max_length=20, blank=True)
|
||||||
email = models.EmailField(blank=True)
|
email = models.EmailField(blank=True)
|
||||||
location = models.CharField(max_length=200, blank=True, help_text="Building/Floor/Room")
|
location = models.CharField(max_length=200, blank=True, help_text="Building/Floor/Room")
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
@ -77,11 +79,11 @@ class Department(UUIDModel, TimeStampedModel):
|
|||||||
default=StatusChoices.ACTIVE,
|
default=StatusChoices.ACTIVE,
|
||||||
db_index=True
|
db_index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['hospital', 'name']
|
ordering = ['hospital', 'name']
|
||||||
unique_together = [['hospital', 'code']]
|
unique_together = [['hospital', 'code']]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.hospital.name} - {self.name}"
|
return f"{self.hospital.name} - {self.name}"
|
||||||
|
|
||||||
@ -96,17 +98,17 @@ class Physician(UUIDModel, TimeStampedModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
related_name='physician_profile'
|
related_name='physician_profile'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Basic information
|
# Basic information
|
||||||
first_name = models.CharField(max_length=100)
|
first_name = models.CharField(max_length=100)
|
||||||
last_name = models.CharField(max_length=100)
|
last_name = models.CharField(max_length=100)
|
||||||
first_name_ar = models.CharField(max_length=100, blank=True)
|
first_name_ar = models.CharField(max_length=100, blank=True)
|
||||||
last_name_ar = models.CharField(max_length=100, blank=True)
|
last_name_ar = models.CharField(max_length=100, blank=True)
|
||||||
|
|
||||||
# Professional information
|
# Professional information
|
||||||
license_number = models.CharField(max_length=100, unique=True, db_index=True)
|
license_number = models.CharField(max_length=100, unique=True, db_index=True)
|
||||||
specialization = models.CharField(max_length=200)
|
specialization = models.CharField(max_length=200)
|
||||||
|
|
||||||
# Organization
|
# Organization
|
||||||
hospital = models.ForeignKey(Hospital, on_delete=models.CASCADE, related_name='physicians')
|
hospital = models.ForeignKey(Hospital, on_delete=models.CASCADE, related_name='physicians')
|
||||||
department = models.ForeignKey(
|
department = models.ForeignKey(
|
||||||
@ -116,11 +118,11 @@ class Physician(UUIDModel, TimeStampedModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
related_name='physicians'
|
related_name='physicians'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Contact
|
# Contact
|
||||||
phone = models.CharField(max_length=20, blank=True)
|
phone = models.CharField(max_length=20, blank=True)
|
||||||
email = models.EmailField(blank=True)
|
email = models.EmailField(blank=True)
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
@ -128,13 +130,13 @@ class Physician(UUIDModel, TimeStampedModel):
|
|||||||
default=StatusChoices.ACTIVE,
|
default=StatusChoices.ACTIVE,
|
||||||
db_index=True
|
db_index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['last_name', 'first_name']
|
ordering = ['last_name', 'first_name']
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Dr. {self.first_name} {self.last_name}"
|
return f"Dr. {self.first_name} {self.last_name}"
|
||||||
|
|
||||||
def get_full_name(self):
|
def get_full_name(self):
|
||||||
return f"{self.first_name} {self.last_name}"
|
return f"{self.first_name} {self.last_name}"
|
||||||
|
|
||||||
@ -146,7 +148,7 @@ class Employee(UUIDModel, TimeStampedModel):
|
|||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='employee_profile'
|
related_name='employee_profile'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Organization
|
# Organization
|
||||||
hospital = models.ForeignKey(Hospital, on_delete=models.CASCADE, related_name='employees')
|
hospital = models.ForeignKey(Hospital, on_delete=models.CASCADE, related_name='employees')
|
||||||
department = models.ForeignKey(
|
department = models.ForeignKey(
|
||||||
@ -156,12 +158,12 @@ class Employee(UUIDModel, TimeStampedModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
related_name='employees'
|
related_name='employees'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Job information
|
# Job information
|
||||||
employee_id = models.CharField(max_length=50, unique=True, db_index=True)
|
employee_id = models.CharField(max_length=50, unique=True, db_index=True)
|
||||||
job_title = models.CharField(max_length=200)
|
job_title = models.CharField(max_length=200)
|
||||||
hire_date = models.DateField(null=True, blank=True)
|
hire_date = models.DateField(null=True, blank=True)
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
@ -169,10 +171,10 @@ class Employee(UUIDModel, TimeStampedModel):
|
|||||||
default=StatusChoices.ACTIVE,
|
default=StatusChoices.ACTIVE,
|
||||||
db_index=True
|
db_index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['user__last_name', 'user__first_name']
|
ordering = ['user__last_name', 'user__first_name']
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.user.get_full_name()} - {self.job_title}"
|
return f"{self.user.get_full_name()} - {self.job_title}"
|
||||||
|
|
||||||
@ -182,12 +184,12 @@ class Patient(UUIDModel, TimeStampedModel):
|
|||||||
# Basic information
|
# Basic information
|
||||||
mrn = models.CharField(max_length=50, unique=True, db_index=True, verbose_name="Medical Record Number")
|
mrn = models.CharField(max_length=50, unique=True, db_index=True, verbose_name="Medical Record Number")
|
||||||
national_id = models.CharField(max_length=50, blank=True, db_index=True)
|
national_id = models.CharField(max_length=50, blank=True, db_index=True)
|
||||||
|
|
||||||
first_name = models.CharField(max_length=100)
|
first_name = models.CharField(max_length=100)
|
||||||
last_name = models.CharField(max_length=100)
|
last_name = models.CharField(max_length=100)
|
||||||
first_name_ar = models.CharField(max_length=100, blank=True)
|
first_name_ar = models.CharField(max_length=100, blank=True)
|
||||||
last_name_ar = models.CharField(max_length=100, blank=True)
|
last_name_ar = models.CharField(max_length=100, blank=True)
|
||||||
|
|
||||||
# Demographics
|
# Demographics
|
||||||
date_of_birth = models.DateField(null=True, blank=True)
|
date_of_birth = models.DateField(null=True, blank=True)
|
||||||
gender = models.CharField(
|
gender = models.CharField(
|
||||||
@ -195,13 +197,13 @@ class Patient(UUIDModel, TimeStampedModel):
|
|||||||
choices=[('male', 'Male'), ('female', 'Female'), ('other', 'Other')],
|
choices=[('male', 'Male'), ('female', 'Female'), ('other', 'Other')],
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Contact
|
# Contact
|
||||||
phone = models.CharField(max_length=20, blank=True)
|
phone = models.CharField(max_length=20, blank=True)
|
||||||
email = models.EmailField(blank=True)
|
email = models.EmailField(blank=True)
|
||||||
address = models.TextField(blank=True)
|
address = models.TextField(blank=True)
|
||||||
city = models.CharField(max_length=100, blank=True)
|
city = models.CharField(max_length=100, blank=True)
|
||||||
|
|
||||||
# Primary hospital
|
# Primary hospital
|
||||||
primary_hospital = models.ForeignKey(
|
primary_hospital = models.ForeignKey(
|
||||||
Hospital,
|
Hospital,
|
||||||
@ -210,7 +212,7 @@ class Patient(UUIDModel, TimeStampedModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
related_name='patients'
|
related_name='patients'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
@ -218,12 +220,12 @@ class Patient(UUIDModel, TimeStampedModel):
|
|||||||
default=StatusChoices.ACTIVE,
|
default=StatusChoices.ACTIVE,
|
||||||
db_index=True
|
db_index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['last_name', 'first_name']
|
ordering = ['last_name', 'first_name']
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.first_name} {self.last_name} (MRN: {self.mrn})"
|
return f"{self.first_name} {self.last_name} (MRN: {self.mrn})"
|
||||||
|
|
||||||
def get_full_name(self):
|
def get_full_name(self):
|
||||||
return f"{self.first_name} {self.last_name}"
|
return f"{self.first_name} {self.last_name}"
|
||||||
|
|||||||
@ -43,4 +43,4 @@ LOGGING['loggers']['apps']['level'] = 'DEBUG' # noqa
|
|||||||
# Disable some security features for development
|
# Disable some security features for development
|
||||||
SECURE_SSL_REDIRECT = False
|
SECURE_SSL_REDIRECT = False
|
||||||
SESSION_COOKIE_SECURE = False
|
SESSION_COOKIE_SECURE = False
|
||||||
CSRF_COOKIE_SECURE = False
|
CSRF_COOKIE_SECURE = False
|
||||||
Loading…
x
Reference in New Issue
Block a user