166 lines
5.6 KiB
Python
166 lines
5.6 KiB
Python
"""
|
|
Standards Section Models - Track compliance standards (CBAHI, MOH, CHI, etc.)
|
|
"""
|
|
from django.db import models
|
|
from django.core.validators import FileExtensionValidator
|
|
|
|
from apps.core.models import TimeStampedModel, UUIDModel, StatusChoices
|
|
|
|
|
|
class StandardSource(UUIDModel, TimeStampedModel):
|
|
"""Standard sources like CBAHI, MOH, CHI, JCI, etc."""
|
|
name = models.CharField(max_length=100)
|
|
name_ar = models.CharField(max_length=100, blank=True, verbose_name="Name (Arabic)")
|
|
code = models.CharField(max_length=50, unique=True, db_index=True)
|
|
description = models.TextField(blank=True)
|
|
website = models.URLField(blank=True)
|
|
is_active = models.BooleanField(default=True, db_index=True)
|
|
|
|
class Meta:
|
|
ordering = ['name']
|
|
verbose_name = 'Standard Source'
|
|
verbose_name_plural = 'Standard Sources'
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class StandardCategory(UUIDModel, TimeStampedModel):
|
|
"""Group standards by category (Patient Safety, Quality Management, etc.)"""
|
|
name = models.CharField(max_length=100)
|
|
name_ar = models.CharField(max_length=100, blank=True, verbose_name="Name (Arabic)")
|
|
description = models.TextField(blank=True)
|
|
order = models.PositiveIntegerField(default=0, help_text="Display order")
|
|
is_active = models.BooleanField(default=True, db_index=True)
|
|
|
|
class Meta:
|
|
ordering = ['order', 'name']
|
|
verbose_name = 'Standard Category'
|
|
verbose_name_plural = 'Standard Categories'
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class Standard(UUIDModel, TimeStampedModel):
|
|
"""Actual standard requirements"""
|
|
class ComplianceStatus(models.TextChoices):
|
|
NOT_ASSESSED = 'not_assessed', 'Not Assessed'
|
|
MET = 'met', 'Met'
|
|
PARTIALLY_MET = 'partially_met', 'Partially Met'
|
|
NOT_MET = 'not_met', 'Not Met'
|
|
|
|
code = models.CharField(max_length=50, db_index=True, help_text="e.g., CBAHI-PS-01")
|
|
title = models.CharField(max_length=300)
|
|
title_ar = models.CharField(max_length=300, blank=True, verbose_name="Title (Arabic)")
|
|
description = models.TextField(help_text="Full description of the standard")
|
|
|
|
# Relationships
|
|
source = models.ForeignKey(
|
|
StandardSource,
|
|
on_delete=models.PROTECT,
|
|
related_name='standards'
|
|
)
|
|
category = models.ForeignKey(
|
|
StandardCategory,
|
|
on_delete=models.PROTECT,
|
|
related_name='standards'
|
|
)
|
|
department = models.ForeignKey(
|
|
'organizations.Department',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='standards',
|
|
help_text="Department-specific standard (null if applicable to all)"
|
|
)
|
|
|
|
# Dates
|
|
effective_date = models.DateField(null=True, blank=True, help_text="When standard becomes effective")
|
|
review_date = models.DateField(null=True, blank=True, help_text="Next review date")
|
|
|
|
is_active = models.BooleanField(default=True, db_index=True)
|
|
|
|
class Meta:
|
|
ordering = ['source', 'category', 'code']
|
|
verbose_name = 'Standard'
|
|
verbose_name_plural = 'Standards'
|
|
|
|
def __str__(self):
|
|
return f"{self.code}: {self.title}"
|
|
|
|
|
|
class StandardCompliance(UUIDModel, TimeStampedModel):
|
|
"""Track compliance status per department"""
|
|
class ComplianceStatus(models.TextChoices):
|
|
NOT_ASSESSED = 'not_assessed', 'Not Assessed'
|
|
MET = 'met', 'Met'
|
|
PARTIALLY_MET = 'partially_met', 'Partially Met'
|
|
NOT_MET = 'not_met', 'Not Met'
|
|
|
|
department = models.ForeignKey(
|
|
'organizations.Department',
|
|
on_delete=models.CASCADE,
|
|
related_name='compliance_records'
|
|
)
|
|
standard = models.ForeignKey(
|
|
Standard,
|
|
on_delete=models.CASCADE,
|
|
related_name='compliance_records'
|
|
)
|
|
status = models.CharField(
|
|
max_length=20,
|
|
choices=ComplianceStatus.choices,
|
|
default=ComplianceStatus.NOT_ASSESSED,
|
|
db_index=True
|
|
)
|
|
last_assessed_date = models.DateField(null=True, blank=True, help_text="Date of last assessment")
|
|
assessor = models.ForeignKey(
|
|
'accounts.User',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='assessments'
|
|
)
|
|
notes = models.TextField(blank=True, help_text="Assessment notes")
|
|
evidence_summary = models.TextField(blank=True, help_text="Summary of evidence")
|
|
|
|
class Meta:
|
|
ordering = ['-created_at']
|
|
verbose_name = 'Standard Compliance'
|
|
verbose_name_plural = 'Standard Compliance'
|
|
unique_together = [['department', 'standard']]
|
|
|
|
def __str__(self):
|
|
return f"{self.department.name} - {self.standard.code} - {self.status}"
|
|
|
|
|
|
class StandardAttachment(UUIDModel, TimeStampedModel):
|
|
"""Proof of compliance - evidence documents"""
|
|
compliance = models.ForeignKey(
|
|
StandardCompliance,
|
|
on_delete=models.CASCADE,
|
|
related_name='attachments'
|
|
)
|
|
file = models.FileField(
|
|
upload_to='standards/attachments/%Y/%m/',
|
|
validators=[FileExtensionValidator(allowed_extensions=['pdf', 'doc', 'docx', 'xls', 'xlsx', 'jpg', 'jpeg', 'png', 'zip'])]
|
|
)
|
|
filename = models.CharField(max_length=255, help_text="Original filename")
|
|
description = models.TextField(blank=True, help_text="Attachment description")
|
|
uploaded_by = models.ForeignKey(
|
|
'accounts.User',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='uploaded_standards_attachments'
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ['-created_at']
|
|
verbose_name = 'Standard Attachment'
|
|
verbose_name_plural = 'Standard Attachments'
|
|
|
|
def __str__(self):
|
|
return f"{self.compliance} - {self.filename}"
|