2025-08-12 13:33:25 +03:00

1238 lines
33 KiB
Python

"""
Patients app models for hospital management system.
Provides patient demographics, profiles, and consent management functionality.
"""
import uuid
from django.db import models
from django.core.validators import RegexValidator
from django.utils import timezone
from django.conf import settings
class PatientProfile(models.Model):
"""
Patient profile with comprehensive demographics and healthcare information.
"""
# Basic Identifiers
patient_id = models.UUIDField(
default=uuid.uuid4,
unique=True,
editable=False,
help_text='Unique patient identifier'
)
# Tenant relationship
tenant = models.ForeignKey(
'core.Tenant',
on_delete=models.CASCADE,
related_name='patients',
help_text='Organization tenant'
)
# Medical Record Number
mrn = models.CharField(
max_length=50,
unique=True,
help_text='Medical Record Number'
)
# Personal Information
first_name = models.CharField(
max_length=150,
help_text='First name'
)
last_name = models.CharField(
max_length=150,
help_text='Last name'
)
middle_name = models.CharField(
max_length=150,
blank=True,
null=True,
help_text='Middle name'
)
preferred_name = models.CharField(
max_length=150,
blank=True,
null=True,
help_text='Preferred name'
)
suffix = models.CharField(
max_length=20,
blank=True,
null=True,
help_text='Name suffix (Jr., Sr., III, etc.)'
)
# Demographics
date_of_birth = models.DateField(
help_text='Date of birth'
)
gender = models.CharField(
max_length=20,
choices=[
('MALE', 'Male'),
('FEMALE', 'Female'),
('OTHER', 'Other'),
('UNKNOWN', 'Unknown'),
('PREFER_NOT_TO_SAY', 'Prefer not to say'),
],
help_text='Gender'
)
sex_assigned_at_birth = models.CharField(
max_length=20,
choices=[
('MALE', 'Male'),
('FEMALE', 'Female'),
('INTERSEX', 'Intersex'),
('UNKNOWN', 'Unknown'),
],
blank=True,
null=True,
help_text='Sex assigned at birth'
)
# Race and Ethnicity
race = models.CharField(
max_length=50,
choices=[
('AMERICAN_INDIAN', 'American Indian or Alaska Native'),
('ASIAN', 'Asian'),
('BLACK', 'Black or African American'),
('PACIFIC_ISLANDER', 'Native Hawaiian or Other Pacific Islander'),
('WHITE', 'White'),
('OTHER', 'Other'),
('UNKNOWN', 'Unknown'),
('DECLINED', 'Patient Declined'),
],
blank=True,
null=True,
help_text='Race'
)
ethnicity = models.CharField(
max_length=50,
choices=[
('HISPANIC', 'Hispanic or Latino'),
('NON_HISPANIC', 'Not Hispanic or Latino'),
('UNKNOWN', 'Unknown'),
('DECLINED', 'Patient Declined'),
],
blank=True,
null=True,
help_text='Ethnicity'
)
# Contact Information
email = models.EmailField(
blank=True,
null=True,
help_text='Email address'
)
phone_number = models.CharField(
max_length=20,
blank=True,
null=True,
validators=[RegexValidator(
regex=r'^\+?1?\d{9,15}$',
message='Phone number must be entered in the format: "+999999999". Up to 15 digits allowed.'
)],
help_text='Primary phone number'
)
mobile_number = models.CharField(
max_length=20,
blank=True,
null=True,
validators=[RegexValidator(
regex=r'^\+?1?\d{9,15}$',
message='Phone number must be entered in the format: "+999999999". Up to 15 digits allowed.'
)],
help_text='Mobile phone number'
)
# Address Information
address_line_1 = models.CharField(
max_length=255,
blank=True,
null=True,
help_text='Address line 1'
)
address_line_2 = models.CharField(
max_length=255,
blank=True,
null=True,
help_text='Address line 2'
)
city = models.CharField(
max_length=100,
blank=True,
null=True,
help_text='City'
)
state = models.CharField(
max_length=50,
blank=True,
null=True,
help_text='State/Province'
)
zip_code = models.CharField(
max_length=20,
blank=True,
null=True,
help_text='ZIP/Postal code'
)
country = models.CharField(
max_length=100,
default='United States',
help_text='Country'
)
# Social Security and Identification
ssn = models.CharField(
max_length=11,
blank=True,
null=True,
validators=[RegexValidator(
regex=r'^\d{3}-\d{2}-\d{4}$',
message='SSN must be in format: 123-45-6789'
)],
help_text='Social Security Number'
)
drivers_license = models.CharField(
max_length=50,
blank=True,
null=True,
help_text='Driver\'s license number'
)
drivers_license_state = models.CharField(
max_length=50,
blank=True,
null=True,
help_text='Driver\'s license state'
)
# Marital Status and Family
marital_status = models.CharField(
max_length=20,
choices=[
('SINGLE', 'Single'),
('MARRIED', 'Married'),
('DIVORCED', 'Divorced'),
('WIDOWED', 'Widowed'),
('SEPARATED', 'Separated'),
('DOMESTIC_PARTNER', 'Domestic Partner'),
('OTHER', 'Other'),
('UNKNOWN', 'Unknown'),
],
blank=True,
null=True,
help_text='Marital status'
)
# Language and Communication
primary_language = models.CharField(
max_length=50,
default='English',
help_text='Primary language'
)
interpreter_needed = models.BooleanField(
default=False,
help_text='Interpreter services needed'
)
communication_preference = models.CharField(
max_length=20,
choices=[
('PHONE', 'Phone'),
('EMAIL', 'Email'),
('SMS', 'SMS'),
('MAIL', 'Mail'),
('PORTAL', 'Patient Portal'),
],
default='PHONE',
help_text='Preferred communication method'
)
# Employment and Financial
employer = models.CharField(
max_length=200,
blank=True,
null=True,
help_text='Employer'
)
occupation = models.CharField(
max_length=100,
blank=True,
null=True,
help_text='Occupation'
)
# Healthcare Information
primary_care_physician = models.CharField(
max_length=200,
blank=True,
null=True,
help_text='Primary care physician'
)
referring_physician = models.CharField(
max_length=200,
blank=True,
null=True,
help_text='Referring physician'
)
# Allergies and Medical Alerts
allergies = models.TextField(
blank=True,
null=True,
help_text='Known allergies'
)
medical_alerts = models.TextField(
blank=True,
null=True,
help_text='Medical alerts and warnings'
)
# Advance Directives
has_advance_directive = models.BooleanField(
default=False,
help_text='Has advance directive on file'
)
advance_directive_type = models.CharField(
max_length=50,
choices=[
('LIVING_WILL', 'Living Will'),
('HEALTHCARE_PROXY', 'Healthcare Proxy'),
('DNR', 'Do Not Resuscitate'),
('POLST', 'POLST'),
('OTHER', 'Other'),
],
blank=True,
null=True,
help_text='Type of advance directive'
)
# Status and Flags
is_active = models.BooleanField(
default=True,
help_text='Patient is active'
)
is_deceased = models.BooleanField(
default=False,
help_text='Patient is deceased'
)
date_of_death = models.DateField(
blank=True,
null=True,
help_text='Date of death'
)
# VIP and Special Handling
is_vip = models.BooleanField(
default=False,
help_text='VIP patient requiring special handling'
)
confidential_patient = models.BooleanField(
default=False,
help_text='Confidential patient with restricted access'
)
# Registration Information
registration_date = models.DateTimeField(
auto_now_add=True,
help_text='Initial registration date'
)
registered_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='registered_patients',
help_text='User who registered the patient'
)
# Photo
photo = models.ImageField(
upload_to='patient_photos/',
blank=True,
null=True,
help_text='Patient photo'
)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
last_visit_date = models.DateTimeField(
blank=True,
null=True,
help_text='Last visit date'
)
class Meta:
db_table = 'patients_patient_profile'
verbose_name = 'Patient Profile'
verbose_name_plural = 'Patient Profiles'
ordering = ['last_name', 'first_name']
indexes = [
models.Index(fields=['tenant', 'mrn']),
models.Index(fields=['last_name', 'first_name']),
models.Index(fields=['date_of_birth']),
models.Index(fields=['ssn']),
models.Index(fields=['phone_number']),
models.Index(fields=['email']),
]
unique_together = ['tenant', 'mrn']
def __str__(self):
return f"{self.get_full_name()} (MRN: {self.mrn})"
def get_full_name(self):
"""
Return the full name for the patient.
"""
if self.preferred_name:
name = f"{self.preferred_name} {self.last_name}"
else:
name = f"{self.first_name} {self.last_name}"
if self.suffix:
name += f" {self.suffix}"
return name
def get_display_name(self):
"""
Return the display name for the patient.
"""
return self.get_full_name()
@property
def age(self):
"""
Calculate patient age.
"""
if self.date_of_birth:
today = timezone.now().date()
return today.year - self.date_of_birth.year - (
(today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day)
)
return None
@property
def full_address(self):
"""
Return formatted full address.
"""
address_parts = []
if self.address_line_1:
address_parts.append(self.address_line_1)
if self.address_line_2:
address_parts.append(self.address_line_2)
if self.city:
city_state_zip = self.city
if self.state:
city_state_zip += f", {self.state}"
if self.zip_code:
city_state_zip += f" {self.zip_code}"
address_parts.append(city_state_zip)
if self.country and self.country != 'United States':
address_parts.append(self.country)
return '\n'.join(address_parts) if address_parts else None
class EmergencyContact(models.Model):
"""
Emergency contact information for patients.
"""
# Patient relationship
patient = models.ForeignKey(
PatientProfile,
on_delete=models.CASCADE,
related_name='emergency_contacts'
)
# Contact Information
first_name = models.CharField(
max_length=150,
help_text='First name'
)
last_name = models.CharField(
max_length=150,
help_text='Last name'
)
relationship = models.CharField(
max_length=50,
choices=[
('SPOUSE', 'Spouse'),
('PARENT', 'Parent'),
('CHILD', 'Child'),
('SIBLING', 'Sibling'),
('GRANDPARENT', 'Grandparent'),
('GRANDCHILD', 'Grandchild'),
('AUNT_UNCLE', 'Aunt/Uncle'),
('COUSIN', 'Cousin'),
('FRIEND', 'Friend'),
('NEIGHBOR', 'Neighbor'),
('CAREGIVER', 'Caregiver'),
('GUARDIAN', 'Guardian'),
('OTHER', 'Other'),
],
help_text='Relationship to patient'
)
# Contact Details
phone_number = models.CharField(
max_length=20,
validators=[RegexValidator(
regex=r'^\+?1?\d{9,15}$',
message='Phone number must be entered in the format: "+999999999". Up to 15 digits allowed.'
)],
help_text='Primary phone number'
)
mobile_number = models.CharField(
max_length=20,
blank=True,
null=True,
validators=[RegexValidator(
regex=r'^\+?1?\d{9,15}$',
message='Phone number must be entered in the format: "+999999999". Up to 15 digits allowed.'
)],
help_text='Mobile phone number'
)
email = models.EmailField(
blank=True,
null=True,
help_text='Email address'
)
# Address
address_line_1 = models.CharField(
max_length=255,
blank=True,
null=True,
help_text='Address line 1'
)
address_line_2 = models.CharField(
max_length=255,
blank=True,
null=True,
help_text='Address line 2'
)
city = models.CharField(
max_length=100,
blank=True,
null=True,
help_text='City'
)
state = models.CharField(
max_length=50,
blank=True,
null=True,
help_text='State/Province'
)
zip_code = models.CharField(
max_length=20,
blank=True,
null=True,
help_text='ZIP/Postal code'
)
# Priority and Authorization
priority = models.PositiveIntegerField(
default=1,
help_text='Contact priority (1 = highest)'
)
is_authorized_for_medical_decisions = models.BooleanField(
default=False,
help_text='Authorized to make medical decisions'
)
is_authorized_for_financial_decisions = models.BooleanField(
default=False,
help_text='Authorized to make financial decisions'
)
is_authorized_for_information = models.BooleanField(
default=True,
help_text='Authorized to receive medical information'
)
is_primary = models.BooleanField(
default=False,
help_text='Primary emergency contact'
)
authorization_number = models.CharField(
max_length=100,
blank=True,
null=True,
help_text='Authorization number'
)
# Status
is_active = models.BooleanField(
default=True,
help_text='Contact is active'
)
# Notes
notes = models.TextField(
blank=True,
null=True,
help_text='Additional notes about this contact'
)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'patients_emergency_contact'
verbose_name = 'Emergency Contact'
verbose_name_plural = 'Emergency Contacts'
ordering = ['priority', 'last_name', 'first_name']
indexes = [
models.Index(fields=['patient', 'priority']),
models.Index(fields=['phone_number']),
]
def __str__(self):
return f"{self.first_name} {self.last_name} ({self.relationship}) - {self.patient.get_full_name()}"
def get_full_name(self):
"""
Return the full name for the contact.
"""
return f"{self.first_name} {self.last_name}"
class InsuranceInfo(models.Model):
"""
Insurance information for patients.
"""
# Patient relationship
patient = models.ForeignKey(
PatientProfile,
on_delete=models.CASCADE,
related_name='insurance_info'
)
# Insurance Details
insurance_type = models.CharField(
max_length=20,
choices=[
('PRIMARY', 'Primary'),
('SECONDARY', 'Secondary'),
('TERTIARY', 'Tertiary'),
],
default='PRIMARY',
help_text='Insurance type'
)
# Insurance Company
insurance_company = models.CharField(
max_length=200,
help_text='Insurance company name'
)
plan_name = models.CharField(
max_length=200,
blank=True,
null=True,
help_text='Insurance plan name'
)
plan_type = models.CharField(
max_length=50,
choices=[
('HMO', 'Health Maintenance Organization'),
('PPO', 'Preferred Provider Organization'),
('EPO', 'Exclusive Provider Organization'),
('POS', 'Point of Service'),
('HDHP', 'High Deductible Health Plan'),
('MEDICARE', 'Medicare'),
('MEDICAID', 'Medicaid'),
('TRICARE', 'TRICARE'),
('WORKERS_COMP', 'Workers Compensation'),
('AUTO', 'Auto Insurance'),
('OTHER', 'Other'),
],
blank=True,
null=True,
help_text='Plan type'
)
# Policy Information
policy_number = models.CharField(
max_length=100,
help_text='Policy/Member ID number'
)
group_number = models.CharField(
max_length=100,
blank=True,
null=True,
help_text='Group number'
)
# Subscriber Information
subscriber_name = models.CharField(
max_length=200,
help_text='Subscriber name'
)
subscriber_relationship = models.CharField(
max_length=20,
choices=[
('SELF', 'Self'),
('SPOUSE', 'Spouse'),
('CHILD', 'Child'),
('PARENT', 'Parent'),
('OTHER', 'Other'),
],
default='SELF',
help_text='Relationship to subscriber'
)
subscriber_dob = models.DateField(
blank=True,
null=True,
help_text='Subscriber date of birth'
)
subscriber_ssn = models.CharField(
max_length=11,
blank=True,
null=True,
validators=[RegexValidator(
regex=r'^\d{3}-\d{2}-\d{4}$',
message='SSN must be in format: 123-45-6789'
)],
help_text='Subscriber Social Security Number'
)
# Coverage Information
effective_date = models.DateField(
help_text='Coverage effective date'
)
termination_date = models.DateField(
blank=True,
null=True,
help_text='Coverage termination date'
)
# Financial Information
copay_amount = models.DecimalField(
max_digits=10,
decimal_places=2,
blank=True,
null=True,
help_text='Copay amount'
)
deductible_amount = models.DecimalField(
max_digits=10,
decimal_places=2,
blank=True,
null=True,
help_text='Deductible amount'
)
out_of_pocket_max = models.DecimalField(
max_digits=10,
decimal_places=2,
blank=True,
null=True,
help_text='Out of pocket maximum'
)
# Verification
is_verified = models.BooleanField(
default=False,
help_text='Insurance has been verified'
)
verification_date = models.DateTimeField(
blank=True,
null=True,
help_text='Date insurance was verified'
)
verified_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='verified_insurance',
help_text='User who verified insurance'
)
# Authorization
requires_authorization = models.BooleanField(
default=False,
help_text='Requires prior authorization'
)
authorization_number = models.CharField(
max_length=100,
blank=True,
null=True,
help_text='Authorization number'
)
authorization_expiry = models.DateField(
blank=True,
null=True,
help_text='Authorization expiry date'
)
# Status
is_active = models.BooleanField(
default=True,
help_text='Insurance is active'
)
# Notes
notes = models.TextField(
blank=True,
null=True,
help_text='Additional notes about this insurance'
)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'patients_insurance_info'
verbose_name = 'Insurance Information'
verbose_name_plural = 'Insurance Information'
ordering = ['insurance_type', 'insurance_company']
indexes = [
models.Index(fields=['patient', 'insurance_type']),
models.Index(fields=['policy_number']),
models.Index(fields=['is_verified']),
]
def __str__(self):
return f"{self.insurance_company} ({self.insurance_type}) - {self.patient.get_full_name()}"
@property
def is_coverage_active(self):
"""
Check if coverage is currently active.
"""
today = timezone.now().date()
if self.termination_date and today > self.termination_date:
return False
return today >= self.effective_date and self.is_active
class ConsentTemplate(models.Model):
"""
Templates for consent forms.
"""
# Tenant relationship
tenant = models.ForeignKey(
'core.Tenant',
on_delete=models.CASCADE,
related_name='consent_templates'
)
# Template Information
name = models.CharField(
max_length=200,
help_text='Template name'
)
description = models.TextField(
blank=True,
null=True,
help_text='Template description'
)
category = models.CharField(
max_length=50,
choices=[
('TREATMENT', 'Treatment Consent'),
('PROCEDURE', 'Procedure Consent'),
('SURGERY', 'Surgical Consent'),
('ANESTHESIA', 'Anesthesia Consent'),
('RESEARCH', 'Research Consent'),
('PRIVACY', 'Privacy Consent'),
('FINANCIAL', 'Financial Consent'),
('DISCHARGE', 'Discharge Consent'),
('OTHER', 'Other'),
],
help_text='Consent category'
)
# Content
content = models.TextField(
help_text='Consent form content'
)
# Requirements
requires_signature = models.BooleanField(
default=True,
help_text='Requires patient signature'
)
requires_witness = models.BooleanField(
default=False,
help_text='Requires witness signature'
)
requires_guardian = models.BooleanField(
default=False,
help_text='Requires guardian signature for minors'
)
# Validity
is_active = models.BooleanField(
default=True,
help_text='Template is active'
)
version = models.CharField(
max_length=20,
default='1.0',
help_text='Template version'
)
effective_date = models.DateField(
default=timezone.now,
help_text='Template effective date'
)
expiry_date = models.DateField(
blank=True,
null=True,
help_text='Template expiry date'
)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='created_consent_templates',
help_text='User who created the template'
)
class Meta:
db_table = 'patients_consent_template'
verbose_name = 'Consent Template'
verbose_name_plural = 'Consent Templates'
ordering = ['category', 'name']
indexes = [
models.Index(fields=['tenant', 'category']),
models.Index(fields=['is_active']),
]
def __str__(self):
return f"{self.name} (v{self.version})"
class ConsentForm(models.Model):
"""
Patient consent forms.
"""
# Patient relationship
patient = models.ForeignKey(
PatientProfile,
on_delete=models.CASCADE,
related_name='consent_forms'
)
# Template relationship
template = models.ForeignKey(
ConsentTemplate,
on_delete=models.CASCADE,
related_name='consent_forms'
)
# Consent Information
consent_id = models.UUIDField(
default=uuid.uuid4,
unique=True,
editable=False,
help_text='Unique consent identifier'
)
# Status
status = models.CharField(
max_length=20,
choices=[
('PENDING', 'Pending'),
('SIGNED', 'Signed'),
('DECLINED', 'Declined'),
('EXPIRED', 'Expired'),
('REVOKED', 'Revoked'),
],
default='PENDING',
help_text='Consent status'
)
# Signatures
patient_signature = models.TextField(
blank=True,
null=True,
help_text='Patient digital signature'
)
patient_signed_at = models.DateTimeField(
blank=True,
null=True,
help_text='Patient signature timestamp'
)
patient_ip_address = models.GenericIPAddressField(
blank=True,
null=True,
help_text='Patient IP address when signed'
)
guardian_signature = models.TextField(
blank=True,
null=True,
help_text='Guardian digital signature'
)
guardian_signed_at = models.DateTimeField(
blank=True,
null=True,
help_text='Guardian signature timestamp'
)
guardian_name = models.CharField(
max_length=200,
blank=True,
null=True,
help_text='Guardian name'
)
guardian_relationship = models.CharField(
max_length=50,
blank=True,
null=True,
help_text='Guardian relationship to patient'
)
witness_signature = models.TextField(
blank=True,
null=True,
help_text='Witness digital signature'
)
witness_signed_at = models.DateTimeField(
blank=True,
null=True,
help_text='Witness signature timestamp'
)
witness_name = models.CharField(
max_length=200,
blank=True,
null=True,
help_text='Witness name'
)
witness_title = models.CharField(
max_length=100,
blank=True,
null=True,
help_text='Witness title'
)
# Provider Information
provider_name = models.CharField(
max_length=200,
blank=True,
null=True,
help_text='Provider name'
)
provider_signature = models.TextField(
blank=True,
null=True,
help_text='Provider digital signature'
)
provider_signed_at = models.DateTimeField(
blank=True,
null=True,
help_text='Provider signature timestamp'
)
# Validity
effective_date = models.DateTimeField(
default=timezone.now,
help_text='Consent effective date'
)
expiry_date = models.DateTimeField(
blank=True,
null=True,
help_text='Consent expiry date'
)
# Revocation
revoked_at = models.DateTimeField(
blank=True,
null=True,
help_text='Consent revocation timestamp'
)
revoked_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='revoked_consents',
help_text='User who revoked the consent'
)
revocation_reason = models.TextField(
blank=True,
null=True,
help_text='Reason for revocation'
)
# Notes
notes = models.TextField(
blank=True,
null=True,
help_text='Additional notes about this consent'
)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='created_consent_forms',
help_text='User who created the consent form'
)
class Meta:
db_table = 'patients_consent_form'
verbose_name = 'Consent Form'
verbose_name_plural = 'Consent Forms'
ordering = ['-created_at']
indexes = [
models.Index(fields=['patient', 'status']),
models.Index(fields=['template']),
models.Index(fields=['consent_id']),
]
def __str__(self):
return f"{self.template.name} - {self.patient.get_full_name()} ({self.status})"
@property
def is_valid(self):
"""
Check if consent is currently valid.
"""
now = timezone.now()
if self.status != 'SIGNED':
return False
if self.expiry_date and now > self.expiry_date:
return False
if self.revoked_at:
return False
return now >= self.effective_date
@property
def is_fully_signed(self):
"""
Check if all required signatures are present.
"""
if self.template.requires_signature and not self.patient_signature:
return False
if self.template.requires_witness and not self.witness_signature:
return False
if self.template.requires_guardian and self.patient.age and self.patient.age < 18:
if not self.guardian_signature:
return False
return True
class PatientNote(models.Model):
"""
General notes and comments about patients.
"""
# Patient relationship
patient = models.ForeignKey(
PatientProfile,
on_delete=models.CASCADE,
related_name='patient_notes'
)
# Note Information
note_id = models.UUIDField(
default=uuid.uuid4,
unique=True,
editable=False,
help_text='Unique note identifier'
)
# Content
title = models.CharField(
max_length=200,
help_text='Note title'
)
content = models.TextField(
help_text='Note content'
)
# Category
category = models.CharField(
max_length=50,
choices=[
('GENERAL', 'General'),
('ADMINISTRATIVE', 'Administrative'),
('CLINICAL', 'Clinical'),
('BILLING', 'Billing'),
('INSURANCE', 'Insurance'),
('SOCIAL', 'Social'),
('DISCHARGE', 'Discharge Planning'),
('FOLLOW_UP', 'Follow-up'),
('ALERT', 'Alert'),
('OTHER', 'Other'),
],
default='GENERAL',
help_text='Note category'
)
# Priority
priority = models.CharField(
max_length=20,
choices=[
('LOW', 'Low'),
('NORMAL', 'Normal'),
('HIGH', 'High'),
('URGENT', 'Urgent'),
],
default='NORMAL',
help_text='Note priority'
)
# Visibility
is_confidential = models.BooleanField(
default=False,
help_text='Note is confidential'
)
is_alert = models.BooleanField(
default=False,
help_text='Note is an alert'
)
# Status
is_active = models.BooleanField(
default=True,
help_text='Note is active'
)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='created_patient_notes',
help_text='User who created the note'
)
class Meta:
db_table = 'patients_patient_note'
verbose_name = 'Patient Note'
verbose_name_plural = 'Patient Notes'
ordering = ['-created_at']
indexes = [
models.Index(fields=['patient', 'category']),
models.Index(fields=['priority']),
models.Index(fields=['is_alert']),
]
def __str__(self):
return f"{self.title} - {self.patient.get_full_name()}"