299 lines
14 KiB
Python
299 lines
14 KiB
Python
# # core/models.py (all-in-one, flat style)
|
||
# from django.conf import settings
|
||
# from django.db import models
|
||
# from django.contrib.contenttypes.fields import GenericForeignKey
|
||
# from django.contrib.contenttypes.models import ContentType
|
||
# import uuid
|
||
#
|
||
# # =============================
|
||
# # Mixins & Tenant manager
|
||
# # =============================
|
||
# class TimeStampedModel(models.Model):
|
||
# created_at = models.DateTimeField(auto_now_add=True, db_index=True)
|
||
# updated_at = models.DateTimeField(auto_now=True, db_index=True)
|
||
# class Meta:
|
||
# abstract = True
|
||
#
|
||
# class UserStampedModel(models.Model):
|
||
# created_by = models.ForeignKey(
|
||
# settings.AUTH_USER_MODEL, null=True, blank=True,
|
||
# on_delete=models.SET_NULL, related_name="%(class)s_created"
|
||
# )
|
||
# updated_by = models.ForeignKey(
|
||
# settings.AUTH_USER_MODEL, null=True, blank=True,
|
||
# on_delete=models.SET_NULL, related_name="%(class)s_updated"
|
||
# )
|
||
# class Meta:
|
||
# abstract = True
|
||
#
|
||
# class ActiveModel(models.Model):
|
||
# is_active = models.BooleanField(default=True, db_index=True)
|
||
# class Meta:
|
||
# abstract = True
|
||
#
|
||
# class TenantManager(models.Manager):
|
||
# def for_tenant(self, tenant):
|
||
# return super().get_queryset().filter(tenant=tenant)
|
||
#
|
||
# class TenantScopedModel(TimeStampedModel, UserStampedModel, ActiveModel):
|
||
# tenant = models.ForeignKey("core.Tenant", on_delete=models.CASCADE, db_index=True)
|
||
# objects = TenantManager()
|
||
# class Meta:
|
||
# abstract = True
|
||
#
|
||
# # =============================
|
||
# # Core primitives
|
||
# # =============================
|
||
# class Tenant(TimeStampedModel, ActiveModel):
|
||
# code = models.SlugField(max_length=64, unique=True)
|
||
# name = models.CharField(max_length=255)
|
||
# status = models.CharField(max_length=32, default="active") # active|suspended|closed
|
||
# def __str__(self):
|
||
# return f"{self.code} – {self.name}"
|
||
#
|
||
# class Facility(TenantScopedModel):
|
||
# code = models.SlugField(max_length=64)
|
||
# name = models.CharField(max_length=255)
|
||
# facility_type = models.CharField(max_length=64, blank=True, default="") # hospital|clinic|lab|...
|
||
# address_line1 = models.CharField(max_length=255, blank=True, default="")
|
||
# address_line2 = models.CharField(max_length=255, blank=True, default="")
|
||
# city = models.CharField(max_length=120, blank=True, default="")
|
||
# state = models.CharField(max_length=120, blank=True, default="")
|
||
# postal_code = models.CharField(max_length=30, blank=True, default="")
|
||
# country = models.CharField(max_length=2, blank=True, default="") # ISO-3166-1 alpha-2
|
||
#
|
||
# class Meta:
|
||
# unique_together = [("tenant", "code")]
|
||
# indexes = [
|
||
# models.Index(fields=["tenant", "name"]),
|
||
# models.Index(fields=["tenant", "facility_type"]),
|
||
# ]
|
||
#
|
||
# def __str__(self):
|
||
# return f"{self.code} – {self.name}"
|
||
#
|
||
# class Patient(TenantScopedModel):
|
||
# uuid = models.UUIDField(unique=True, editable=False, null=True, blank=True, default=uuid.uuid4)
|
||
# given_name = models.CharField(max_length=120)
|
||
# family_name = models.CharField(max_length=120, blank=True, default="")
|
||
# birth_date = models.DateField(null=True, blank=True)
|
||
# sex = models.CharField(max_length=16, blank=True, default="") # male|female|other|unknown
|
||
# primary_facility = models.ForeignKey("core.Facility", null=True, blank=True, on_delete=models.SET_NULL)
|
||
#
|
||
# class Meta:
|
||
# indexes = [
|
||
# models.Index(fields=["tenant", "family_name", "given_name"]),
|
||
# models.Index(fields=["tenant", "birth_date"]),
|
||
# ]
|
||
#
|
||
# def __str__(self):
|
||
# return f"{self.family_name}, {self.given_name}".strip(", ")
|
||
#
|
||
# class IdentifierType(TenantScopedModel):
|
||
# code = models.SlugField(max_length=64)
|
||
# name = models.CharField(max_length=120)
|
||
# issuer = models.CharField(max_length=120, blank=True, default="") # e.g., MOH, facility code
|
||
#
|
||
# class Meta:
|
||
# unique_together = [("tenant", "code")]
|
||
#
|
||
# def __str__(self):
|
||
# return self.name
|
||
#
|
||
# class Identifier(TenantScopedModel):
|
||
# patient = models.ForeignKey("core.Patient", on_delete=models.CASCADE, related_name="identifiers")
|
||
# id_type = models.ForeignKey("core.IdentifierType", on_delete=models.PROTECT)
|
||
# value = models.CharField(max_length=128)
|
||
# is_primary = models.BooleanField(default=False)
|
||
# period_start = models.DateField(null=True, blank=True)
|
||
# period_end = models.DateField(null=True, blank=True)
|
||
#
|
||
# class Meta:
|
||
# unique_together = [("tenant", "id_type", "value")]
|
||
# indexes = [
|
||
# models.Index(fields=["tenant", "value"]),
|
||
# models.Index(fields=["tenant", "patient"]),
|
||
# ]
|
||
#
|
||
# def __str__(self):
|
||
# return f"{self.id_type.code}:{self.value}"
|
||
#
|
||
# class Practitioner(TenantScopedModel):
|
||
# """Clinical identity; keep HR employment in hr.Employee and auth in accounts.User."""
|
||
# user = models.ForeignKey("accounts.User", null=True, blank=True, on_delete=models.SET_NULL)
|
||
# full_name = models.CharField(max_length=255)
|
||
# license_id = models.CharField(max_length=80, blank=True, default="")
|
||
# specialty = models.CharField(max_length=120, blank=True, default="")
|
||
# npi = models.CharField(max_length=20, blank=True, default="") # if relevant in your locale
|
||
# active_from = models.DateField(null=True, blank=True)
|
||
# active_to = models.DateField(null=True, blank=True)
|
||
#
|
||
# class Meta:
|
||
# indexes = [
|
||
# models.Index(fields=["tenant", "full_name"]),
|
||
# ]
|
||
# unique_together = [] # add ("tenant","license_id") if licenses are unique
|
||
#
|
||
# def __str__(self):
|
||
# return self.full_name
|
||
#
|
||
# class Department(TenantScopedModel):
|
||
# facility = models.ForeignKey("core.Facility", on_delete=models.PROTECT)
|
||
# code = models.SlugField(max_length=64)
|
||
# name = models.CharField(max_length=255)
|
||
# class Meta:
|
||
# unique_together = [("tenant", "facility", "code")]
|
||
# indexes = [models.Index(fields=["tenant", "facility", "name"])]
|
||
# def __str__(self):
|
||
# return f"{self.facility.code}:{self.code}"
|
||
#
|
||
# class LocationNode(TenantScopedModel):
|
||
# """Generic clinical location tree (building/floor/room/chair/etc.)."""
|
||
# facility = models.ForeignKey("core.Facility", on_delete=models.PROTECT)
|
||
# parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE, related_name="children")
|
||
# code = models.SlugField(max_length=64)
|
||
# name = models.CharField(max_length=255)
|
||
# kind = models.CharField(max_length=32, default="room") # building|floor|zone|room|chair|desk...
|
||
# class Meta:
|
||
# unique_together = [("tenant", "facility", "code")]
|
||
# indexes = [models.Index(fields=["tenant", "facility", "kind"])]
|
||
# def __str__(self):
|
||
# return f"{self.facility.code}:{self.code}"
|
||
#
|
||
# class Encounter(TenantScopedModel):
|
||
# class EncounterClass(models.TextChoices):
|
||
# INPATIENT = "INPATIENT", "Inpatient"
|
||
# OUTPATIENT = "OUTPATIENT", "Outpatient"
|
||
# EMERGENCY = "EMERGENCY", "Emergency"
|
||
# VIRTUAL = "VIRTUAL", "Virtual"
|
||
#
|
||
# class EncounterStatus(models.TextChoices):
|
||
# PLANNED = "PLANNED", "Planned"
|
||
# IN_PROGRESS = "IN_PROGRESS", "In Progress"
|
||
# FINISHED = "FINISHED", "Finished"
|
||
# CANCELLED = "CANCELLED", "Cancelled"
|
||
#
|
||
# patient = models.ForeignKey("core.Patient", on_delete=models.PROTECT, related_name="encounters")
|
||
# facility = models.ForeignKey("core.Facility", on_delete=models.PROTECT, related_name="encounters")
|
||
# encounter_class = models.CharField(max_length=20, choices=EncounterClass.choices)
|
||
# status = models.CharField(max_length=20, choices=EncounterStatus.choices, default=EncounterStatus.PLANNED)
|
||
# start_at = models.DateTimeField()
|
||
# end_at = models.DateTimeField(null=True, blank=True)
|
||
# attending = models.ForeignKey("core.Practitioner", null=True, blank=True, on_delete=models.SET_NULL, related_name="encounters_attending")
|
||
# department = models.ForeignKey("core.Department", null=True, blank=True, on_delete=models.SET_NULL)
|
||
# location_node = models.ForeignKey("core.LocationNode", null=True, blank=True, on_delete=models.SET_NULL)
|
||
#
|
||
# class Meta:
|
||
# indexes = [
|
||
# models.Index(fields=["tenant", "patient", "start_at"]),
|
||
# models.Index(fields=["tenant", "facility", "start_at"]),
|
||
# models.Index(fields=["tenant", "encounter_class"]),
|
||
# models.Index(fields=["tenant", "status"]),
|
||
# ]
|
||
#
|
||
# def __str__(self):
|
||
# return f"{self.patient_id} @ {self.facility_id} ({self.encounter_class})"
|
||
#
|
||
# class Address(TenantScopedModel):
|
||
# line1 = models.CharField(max_length=255, blank=True, default="")
|
||
# line2 = models.CharField(max_length=255, blank=True, default="")
|
||
# city = models.CharField(max_length=120, blank=True, default="")
|
||
# state = models.CharField(max_length=120, blank=True, default="")
|
||
# postal_code = models.CharField(max_length=30, blank=True, default="")
|
||
# country = models.CharField(max_length=2, blank=True, default="")
|
||
# def __str__(self):
|
||
# return ", ".join(x for x in [self.line1, self.city, self.state] if x)
|
||
#
|
||
# class ContactPoint(TenantScopedModel):
|
||
# system = models.CharField(max_length=20) # phone|email
|
||
# value = models.CharField(max_length=120)
|
||
# use = models.CharField(max_length=20, blank=True, default="") # home|work|mobile
|
||
# preferred = models.BooleanField(default=False)
|
||
# class Meta:
|
||
# indexes = [models.Index(fields=["tenant", "system", "value"])]
|
||
# def __str__(self):
|
||
# return f"{self.system}:{self.value}"
|
||
#
|
||
# class PatientContact(TenantScopedModel):
|
||
# patient = models.ForeignKey("core.Patient", on_delete=models.CASCADE, related_name="contacts")
|
||
# name = models.CharField(max_length=255, blank=True, default="")
|
||
# relationship = models.CharField(max_length=40, blank=True, default="") # guardian|next-of-kin
|
||
# address = models.ForeignKey("core.Address", null=True, blank=True, on_delete=models.SET_NULL)
|
||
# phone = models.ForeignKey("core.ContactPoint", null=True, blank=True, on_delete=models.SET_NULL, related_name="pc_phone")
|
||
# email = models.ForeignKey("core.ContactPoint", null=True, blank=True, on_delete=models.SET_NULL, related_name="pc_email")
|
||
#
|
||
# class Consent(TenantScopedModel):
|
||
# patient = models.ForeignKey("core.Patient", on_delete=models.CASCADE, related_name="consents")
|
||
# kind = models.CharField(max_length=40) # treatment|surgery|disclosure|photo
|
||
# given_at = models.DateTimeField()
|
||
# revoked_at = models.DateTimeField(null=True, blank=True)
|
||
# details = models.JSONField(null=True, blank=True) # form id, signer, ip, scope, etc.
|
||
# class Meta:
|
||
# indexes = [models.Index(fields=["tenant", "patient", "kind", "given_at"])]
|
||
#
|
||
# class PatientMergeEvent(TenantScopedModel):
|
||
# surviving = models.ForeignKey("core.Patient", on_delete=models.PROTECT, related_name="merges_surviving")
|
||
# merged = models.ForeignKey("core.Patient", on_delete=models.PROTECT, related_name="merges_merged")
|
||
# reason = models.CharField(max_length=120, blank=True, default="")
|
||
# happened_at = models.DateTimeField(auto_now_add=True)
|
||
#
|
||
# class Coverage(TenantScopedModel):
|
||
# """Patient's active insurance coverage (neutral, referenced by billing & insurance_approvals)."""
|
||
# patient = models.ForeignKey("core.Patient", on_delete=models.CASCADE, related_name="coverages")
|
||
# payer = models.ForeignKey("billing.Payer", on_delete=models.PROTECT) # keep payer master in billing
|
||
# member_id = models.CharField(max_length=64)
|
||
# plan_name = models.CharField(max_length=120, blank=True, default="")
|
||
# period_start = models.DateField(null=True, blank=True)
|
||
# period_end = models.DateField(null=True, blank=True)
|
||
# is_primary = models.BooleanField(default=True)
|
||
# class Meta:
|
||
# indexes = [models.Index(fields=["tenant", "patient", "is_primary"])]
|
||
# unique_together = [] # consider ("tenant","patient","payer","member_id")
|
||
#
|
||
# class CareTeam(TenantScopedModel):
|
||
# patient = models.ForeignKey("core.Patient", on_delete=models.CASCADE)
|
||
# encounter = models.ForeignKey("core.Encounter", null=True, blank=True, on_delete=models.SET_NULL)
|
||
#
|
||
# class CareTeamMember(TenantScopedModel):
|
||
# careteam = models.ForeignKey("core.CareTeam", on_delete=models.CASCADE, related_name="members")
|
||
# practitioner = models.ForeignKey("core.Practitioner", on_delete=models.PROTECT)
|
||
# role = models.CharField(max_length=40, blank=True, default="") # attending|consultant|nurse
|
||
# class Meta:
|
||
# indexes = [models.Index(fields=["tenant", "role"])]]
|
||
#
|
||
# class Attachment(TenantScopedModel):
|
||
# file = models.FileField(upload_to="attachments/%Y/%m/")
|
||
# title = models.CharField(max_length=255, blank=True, default="")
|
||
# content_type = models.CharField(max_length=120, blank=True, default="")
|
||
# size_bytes = models.PositiveIntegerField(default=0)
|
||
# patient = models.ForeignKey("core.Patient", null=True, blank=True, on_delete=models.SET_NULL, related_name="attachments")
|
||
# encounter = models.ForeignKey("core.Encounter", null=True, blank=True, on_delete=models.SET_NULL, related_name="attachments")
|
||
#
|
||
# class Meta:
|
||
# indexes = [
|
||
# models.Index(fields=["tenant", "patient"]),
|
||
# models.Index(fields=["tenant", "encounter"]),
|
||
# ]
|
||
#
|
||
# def __str__(self):
|
||
# return self.title or self.file.name
|
||
#
|
||
# class AuditEvent(TenantScopedModel):
|
||
# action = models.CharField(max_length=32) # create|update|delete|read|login|export|import|custom
|
||
# actor = models.ForeignKey("accounts.User", null=True, blank=True, on_delete=models.SET_NULL, related_name="audit_events")
|
||
# target_content_type = models.ForeignKey(ContentType, on_delete=models.SET_NULL, null=True, blank=True)
|
||
# target_object_id = models.CharField(max_length=64, blank=True, default="")
|
||
# target = GenericForeignKey("target_content_type", "target_object_id")
|
||
# summary = models.CharField(max_length=255, blank=True, default="")
|
||
# payload = models.JSONField(null=True, blank=True) # optional diff/extra data
|
||
# occurred_at = models.DateTimeField(auto_now_add=True)
|
||
#
|
||
# class Meta:
|
||
# indexes = [
|
||
# models.Index(fields=["tenant", "action", "occurred_at"]),
|
||
# models.Index(fields=["tenant", "target_content_type", "target_object_id"]),
|
||
# ]
|
||
#
|
||
# def __str__(self):
|
||
# return f"[{self.action}] {self.summary or self.id}"
|