hospital-management/tools/core_models_extended.py
Marwan Alwali 263292f6be update
2025-11-04 00:50:06 +03:00

299 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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