130 lines
5.1 KiB
Python
130 lines
5.1 KiB
Python
from django.db import models
|
|
from django.contrib.auth import get_user_model
|
|
from django.utils.translation import gettext_lazy as _
|
|
from core.models import Patient
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class DocumentTemplate(models.Model):
|
|
"""
|
|
Reusable document templates for clinical notes and other documents.
|
|
"""
|
|
CATEGORY_CHOICES = [
|
|
('medical', _('Medical')),
|
|
('nursing', _('Nursing')),
|
|
('aba', _('ABA')),
|
|
('ot', _('Occupational Therapy')),
|
|
('slp', _('Speech-Language Pathology')),
|
|
('assessment', _('Assessment')),
|
|
('progress', _('Progress Report')),
|
|
('discharge', _('Discharge Summary')),
|
|
('other', _('Other')),
|
|
]
|
|
|
|
name = models.CharField(_('Template Name'), max_length=200)
|
|
category = models.CharField(_('Category'), max_length=50, choices=CATEGORY_CHOICES)
|
|
description = models.TextField(_('Description'), blank=True)
|
|
content = models.TextField(_('Template Content'), help_text=_('Use {{variable_name}} for dynamic fields'))
|
|
is_active = models.BooleanField(_('Active'), default=True)
|
|
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='created_templates')
|
|
created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
|
|
updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
|
|
|
|
class Meta:
|
|
verbose_name = _('Document Template')
|
|
verbose_name_plural = _('Document Templates')
|
|
ordering = ['category', 'name']
|
|
|
|
def __str__(self):
|
|
return f"{self.name} ({self.get_category_display()})"
|
|
|
|
|
|
class ClinicalNote(models.Model):
|
|
"""
|
|
Clinical notes and documentation for patient care.
|
|
"""
|
|
STATUS_CHOICES = [
|
|
('draft', _('Draft')),
|
|
('final', _('Final')),
|
|
('amended', _('Amended')),
|
|
('deleted', _('Deleted')),
|
|
]
|
|
|
|
patient = models.ForeignKey(Patient, on_delete=models.CASCADE, related_name='clinical_notes')
|
|
template = models.ForeignKey(DocumentTemplate, on_delete=models.SET_NULL, null=True, blank=True, related_name='notes')
|
|
title = models.CharField(_('Title'), max_length=200)
|
|
content = models.TextField(_('Content'))
|
|
status = models.CharField(_('Status'), max_length=20, choices=STATUS_CHOICES, default='draft')
|
|
|
|
# Metadata
|
|
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='authored_notes')
|
|
created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
|
|
updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
|
|
finalized_at = models.DateTimeField(_('Finalized At'), null=True, blank=True)
|
|
finalized_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='finalized_notes')
|
|
|
|
# Versioning
|
|
version = models.IntegerField(_('Version'), default=1)
|
|
parent_note = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='amendments')
|
|
|
|
class Meta:
|
|
verbose_name = _('Clinical Note')
|
|
verbose_name_plural = _('Clinical Notes')
|
|
ordering = ['-created_at']
|
|
permissions = [
|
|
('can_finalize_note', 'Can finalize clinical notes'),
|
|
('can_amend_note', 'Can amend finalized notes'),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"{self.title} - {self.patient.get_full_name()} ({self.created_at.strftime('%Y-%m-%d')})"
|
|
|
|
|
|
class NoteAddendum(models.Model):
|
|
"""
|
|
Addendums to clinical notes for corrections or additional information.
|
|
"""
|
|
note = models.ForeignKey(ClinicalNote, on_delete=models.CASCADE, related_name='addendums')
|
|
content = models.TextField(_('Addendum Content'))
|
|
reason = models.CharField(_('Reason'), max_length=200)
|
|
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
|
|
created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
|
|
|
|
class Meta:
|
|
verbose_name = _('Note Addendum')
|
|
verbose_name_plural = _('Note Addendums')
|
|
ordering = ['-created_at']
|
|
|
|
def __str__(self):
|
|
return f"Addendum to {self.note.title} - {self.created_at.strftime('%Y-%m-%d %H:%M')}"
|
|
|
|
|
|
class NoteAuditLog(models.Model):
|
|
"""
|
|
Audit trail for clinical note changes.
|
|
"""
|
|
ACTION_CHOICES = [
|
|
('created', _('Created')),
|
|
('updated', _('Updated')),
|
|
('finalized', _('Finalized')),
|
|
('amended', _('Amended')),
|
|
('deleted', _('Deleted')),
|
|
('viewed', _('Viewed')),
|
|
]
|
|
|
|
note = models.ForeignKey(ClinicalNote, on_delete=models.CASCADE, related_name='audit_logs')
|
|
action = models.CharField(_('Action'), max_length=20, choices=ACTION_CHOICES)
|
|
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
|
|
timestamp = models.DateTimeField(_('Timestamp'), auto_now_add=True)
|
|
ip_address = models.GenericIPAddressField(_('IP Address'), null=True, blank=True)
|
|
changes = models.JSONField(_('Changes'), null=True, blank=True)
|
|
|
|
class Meta:
|
|
verbose_name = _('Note Audit Log')
|
|
verbose_name_plural = _('Note Audit Logs')
|
|
ordering = ['-timestamp']
|
|
|
|
def __str__(self):
|
|
return f"{self.get_action_display()} - {self.note.title} by {self.user} at {self.timestamp}"
|