Skip to content

Commit

Permalink
Changes create_rfh_patient_from_hospital_number to created MergedMRN
Browse files Browse the repository at this point in the history
create_rfh_patient_from_hospital_number now looks through the upstream database for the assosicated MRNs.
Creates an MRN for whichever is the active MRN.
Creates MergedMRNs for any inactive MRNs.

Returns the patient.

It also changes utils.get_or_create patient to not check upstream merged
mrns as this is no longer necessary.
  • Loading branch information
fredkingham committed Dec 7, 2022
1 parent 820d795 commit e126261
Show file tree
Hide file tree
Showing 7 changed files with 316 additions and 86 deletions.
3 changes: 3 additions & 0 deletions elcid/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class MergedMRN(models.Model):
e.g. if MRN 77456 was merged into patient 123
Patient 123 would have a patient merge object with MRN 77456
"""
ACTIVE = "ACTIVE"
INACTIVE = "INACTIVE"

patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
mrn = models.CharField(max_length=256)
merge_comments = models.TextField(blank=True, null=True, default="")
Expand Down
72 changes: 43 additions & 29 deletions elcid/test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from unittest.mock import patch
from opal.core.test import OpalTestCase
from elcid import utils
from elcid import episode_categories
from plugins.tb import episode_categories as tb_episode_categories


class ModelMethodLoggingTestCase(OpalTestCase):
Expand Down Expand Up @@ -35,49 +37,61 @@ def some_method(self):
result, "some_var"
)

class GetPatientTestCase(OpalTestCase):

class GetOrCreatePatientTestCase(OpalTestCase):
def setUp(self):
self.patient, _ = self.new_patient_and_episode_please()

def test_get_existing_patient(self):
self.patient.demographics_set.update(hospital_number="123")
patient = utils.get_patient('123')
self.assertEqual(self.patient, patient)

def test_get_existing_merged_patient(self):
self.patient.demographics_set.update(hospital_number="123")
self.patient.mergedmrn_set.create(
mrn="234"
self.patient.episode_set.update(
category_name=episode_categories.InfectionService.display_name
)
patient, created = utils.get_or_create_patient(
'123', episode_categories.InfectionService
)
patient = utils.get_patient('234')
self.assertEqual(self.patient, patient)
episode = self.patient.episode_set.get()
self.assertEqual(
episode.category_name,
episode_categories.InfectionService.display_name
)
self.assertFalse(created)

class GetOrCreatePatientTestCase(OpalTestCase):
def setUp(self):
self.patient, _ = self.new_patient_and_episode_please()

def test_get_existing_patient(self):
def test_create_new_episode_on_existing_patient(self):
self.patient.demographics_set.update(hospital_number="123")
patient, created = utils.get_or_create_patient('123')
patient, created = utils.get_or_create_patient(
'123', tb_episode_categories.TbEpisode
)
self.assertEqual(self.patient, patient)
self.assertTrue(self.patient.episode_set.filter(
category_name=tb_episode_categories.TbEpisode.display_name
).exists())
self.assertFalse(created)

@patch('intrahospital_api.update_demographics.upstream_merged_mrn')
@patch('intrahospital_api.loader.create_rfh_patient_from_hospital_number')
def test_create_new_patient(self, create_rfh_patient, upstream_merged_mrn):
create_rfh_patient.return_value = self.patient
upstream_merged_mrn.return_value = None
patient, created = utils.get_or_create_patient('123')
create_rfh_patient.assert_called_once_with('123', run_async=None)
self.assertEqual(self.patient, patient)
self.assertTrue(created)
def test_get_merged_patient(self, create_rfh_patient_from_hospital_number):
self.patient.demographics_set.update(hospital_number="234")
self.patient.mergedmrn_set.create(mrn="123")
patient, created = utils.get_or_create_patient(
'123', episode_categories.InfectionService
)
self.assertEqual(
patient.id, self.patient.id
)
self.assertFalse(create_rfh_patient_from_hospital_number.called)
self.assertFalse(created)

@patch('intrahospital_api.update_demographics.upstream_merged_mrn')
@patch('intrahospital_api.loader.create_rfh_patient_from_hospital_number')
def test_create_merged_patient(self, create_rfh_patient, upstream_merged_mrn):
create_rfh_patient.return_value = self.patient
upstream_merged_mrn.return_value = "234"
patient, created = utils.get_or_create_patient('123')
create_rfh_patient.assert_called_once_with('234', run_async=None)
def test_create_new_patient(self, create_rfh_patient_from_hospital_number):
create_rfh_patient_from_hospital_number.return_value = self.patient
patient, created = utils.get_or_create_patient(
'123', episode_categories.InfectionService
)
create_rfh_patient_from_hospital_number.assert_called_once_with(
'123',
episode_categories.InfectionService,
run_async=None
)
self.assertEqual(self.patient, patient)
self.assertTrue(created)
26 changes: 8 additions & 18 deletions elcid/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,30 +81,20 @@ def natural_keys(text):
return [ atoi(c) for c in re.split(r'(\d+)', text) ]


def get_patient(mrn):
def get_or_create_patient(mrn, episode_category, run_async=None):
from intrahospital_api import loader
patient = opal_models.Patient.objects.filter(
demographics__hospital_number=mrn
).first()
if patient:
return patient
return opal_models.Patient.objects.filter(
if not patient:
patient = opal_models.Patient.objects.filter(
mergedmrn__mrn=mrn
).first()


def get_or_create_patient(mrn, run_async=None):
from intrahospital_api import loader
from intrahospital_api import update_demographics
patient = get_patient(mrn)
if patient:
return (patient, False)
merged_mrn = update_demographics.upstream_merged_mrn(mrn)
if merged_mrn:
patient = loader.create_rfh_patient_from_hospital_number(
merged_mrn, run_async=run_async
)
else:
patient = loader.create_rfh_patient_from_hospital_number(
mrn, run_async=run_async
patient.episode_set.get_or_create(
category_name=episode_category.display_name
)
return (patient, False)
patient = loader.create_rfh_patient_from_hospital_number(mrn, episode_category, run_async=run_async)
return patient, True
56 changes: 44 additions & 12 deletions intrahospital_api/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from opal.models import Patient

from elcid import models as emodels
from elcid import episode_categories as elcid_episode_categories
from elcid.utils import timing
from plugins.admissions.loader import load_encounters, load_transfer_history_for_patient
from plugins.appointments.loader import load_appointments
Expand Down Expand Up @@ -103,21 +104,52 @@ def create_rfh_patient_from_hospital_number(hospital_number, episode_category, r
if emodels.MergedMRN.objects.filter(mrn=hospital_number):
raise ValueError('MRN has already been merged into another MRN')

patient = Patient.objects.create()
rows = update_demographics.get_related_rows_for_mrn(hospital_number)

demographics = patient.demographics()
demographics.hospital_number = hospital_number
demographics.save()
if not rows:
patient = Patient.objects.create()
patient.demographics_set.update(
hospital_number=hospital_number
)
patient.episode_set.create(
category_name=episode_category.display_name
)
load_patient(patient, run_async=run_async)
return patient

actives = [row for row in rows if row["ACTIVE_INACTIVE"] == emodels.MergedMRN.ACTIVE]
if len(actives) > 1 or len(actives) == 0:
# If there are multiple active rows, we can't create merges, so just create the patient
# If there are no active patients, we just create the patient
patient = Patient.objects.create()
patient.demographics_set.update(
hospital_number=hospital_number
)
patient.episode_set.create(
category_name=episode_category.display_name
)
load_patient(patient, run_async=run_async)
return patient

patient.create_episode(
category_name=episode_category.display_name,
start=datetime.date.today()
active_row = actives[0]
patient = Patient.objects.create()
patient.demographics_set.update(
hospital_number=active_row["PATIENT_NUMBER"]
)

if run_async is None:
load_patient(patient, run_async=run_async)
else:
load_patient(patient)
merged_mrns = []
for row in rows:
if row["ACTIVE_INACTIVE"] == emodels.MergedMRN.INACTIVE:
_, merge_dt = update_demographics.get_mrn_and_date_from_merge_comment(
row["MERGE_COMMENTS"]
)[0]
merged_mrns.append(emodels.MergedMRN(
patient = patient,
mrn=row["PATIENT_NUMBER"],
upstream_merge_datetime=merge_dt,
merge_comments=row["MERGE_COMMENTS"]
))
emodels.MergedMRN.objects.bulk_create(merged_mrns)
load_patient(patient, run_async=run_async)
return patient


Expand Down
137 changes: 134 additions & 3 deletions intrahospital_api/test/test_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -682,8 +682,14 @@ def test_synch_patient(
)


@mock.patch('intrahospital_api.loader.update_demographics.get_related_rows_for_mrn')
class CreateRfhPatientFromHospitalNumberTestCase(OpalTestCase):
def test_creates_patient_and_episode(self):
def test_creates_patient_and_episode(self, get_related_rows_for_mrn):
"""
A patient has no merged MRNs. Create the patient with
the episode category and return it.
"""
get_related_rows_for_mrn.return_value = None
patient = loader.create_rfh_patient_from_hospital_number(
'111', episode_categories.InfectionService
)
Expand All @@ -695,7 +701,10 @@ def test_creates_patient_and_episode(self):
episode_categories.InfectionService.display_name
)

def test_errors_if_the_hospital_number_starts_with_a_zero(self):
def test_errors_if_the_hospital_number_starts_with_a_zero(self, get_related_rows_for_mrn):
"""
The MRN starts with a zero, raise a ValueError
"""
with self.assertRaises(ValueError) as v:
loader.create_rfh_patient_from_hospital_number(
'0111', episode_categories.InfectionService
Expand All @@ -706,7 +715,10 @@ def test_errors_if_the_hospital_number_starts_with_a_zero(self):
])
self.assertEqual(str(v.exception), expected)

def test_errors_if_the_hospital_number_has_already_been_merged(self):
def test_errors_if_the_hospital_number_has_already_been_merged(self, get_related_rows_for_mrn):
"""
The MRN has already been merged, raise a Value Error
"""
patient, _ = self.new_patient_and_episode_please()
patient.mergedmrn_set.create(
mrn="111"
Expand All @@ -719,3 +731,122 @@ def test_errors_if_the_hospital_number_has_already_been_merged(self):
str(v.exception),
"MRN has already been merged into another MRN"
)

def test_create_rfh_patient_from_hospital_number(self, get_related_rows_for_mrn):
"""
The MRN passed in is inactive and has
an associated active MRN. Create a patient with the
active MRN and the associated mergedMRNs for the
MRN passed in.
"""
RETURN_VALUE = [
{
"PATIENT_NUMBER": "123",
"MERGE_COMMENTS": " ".join([
"Merged with MRN 234 on Oct 21 2014 4:44PM",
]),
"ACTIVE_INACTIVE": "INACTIVE"
},
{
"PATIENT_NUMBER": "234",
"MERGE_COMMENTS": " ".join([
"Merged with MRN 123 Oct 17 2014 11:03AM",
"Merged with MRN 123 on Oct 21 2014 4:44PM",
"Merged with MRN 456 on Apr 14 2018 1:40PM"
]),
"ACTIVE_INACTIVE": "INACTIVE",
},
{
"PATIENT_NUMBER": "456",
"MERGE_COMMENTS": " ".join([
"Merged with MRN 234 on Apr 14 2018 1:40PM",
]),
"ACTIVE_INACTIVE": "ACTIVE"
}
]
get_related_rows_for_mrn.return_value = RETURN_VALUE
patient = loader.create_rfh_patient_from_hospital_number(
'123', episode_category=episode_categories.InfectionService
)
self.assertEqual(
patient.demographics_set.get().hospital_number, "456"
)
self.assertEqual(patient.mergedmrn_set.count(), 2)

self.assertTrue(
patient.mergedmrn_set.filter(
mrn="123",
merge_comments=RETURN_VALUE[0]["MERGE_COMMENTS"],
upstream_merge_datetime=timezone.make_aware(
datetime.datetime(2014, 10, 21, 16, 44)
)
).exists()
)
self.assertTrue(
patient.mergedmrn_set.filter(
mrn="234",
merge_comments=RETURN_VALUE[1]["MERGE_COMMENTS"],
upstream_merge_datetime=timezone.make_aware(
datetime.datetime(2018, 4, 14, 13, 40)
)
).exists()
)

def test_active_mrn_with_inactive_associated_mrns(self, get_related_rows_for_mrn):
"""
The MRN passed in is active and has an associated inactive MRN.
Create a patient with the active MRN and the associated mergedMRNs for the
other MRNs
"""
RETURN_VALUE = [
{
"PATIENT_NUMBER": "123",
"MERGE_COMMENTS": " ".join([
"Merged with MRN 234 on Oct 21 2014 4:44PM",
]),
"ACTIVE_INACTIVE": "INACTIVE"
},
{
"PATIENT_NUMBER": "234",
"MERGE_COMMENTS": " ".join([
"Merged with MRN 123 Oct 17 2014 11:03AM",
"Merged with MRN 123 on Oct 21 2014 4:44PM",
"Merged with MRN 456 on Apr 14 2018 1:40PM"
]),
"ACTIVE_INACTIVE": "INACTIVE",
},
{
"PATIENT_NUMBER": "456",
"MERGE_COMMENTS": " ".join([
"Merged with MRN 234 on Apr 14 2018 1:40PM",
]),
"ACTIVE_INACTIVE": "ACTIVE"
}
]
get_related_rows_for_mrn.return_value = RETURN_VALUE
patient = loader.create_rfh_patient_from_hospital_number(
'456', episode_category=episode_categories.InfectionService
)
self.assertEqual(
patient.demographics_set.get().hospital_number, "456"
)
self.assertEqual(patient.mergedmrn_set.count(), 2)

self.assertTrue(
patient.mergedmrn_set.filter(
mrn="123",
merge_comments=RETURN_VALUE[0]["MERGE_COMMENTS"],
upstream_merge_datetime=timezone.make_aware(
datetime.datetime(2014, 10, 21, 16, 44)
)
).exists()
)
self.assertTrue(
patient.mergedmrn_set.filter(
mrn="234",
merge_comments=RETURN_VALUE[1]["MERGE_COMMENTS"],
upstream_merge_datetime=timezone.make_aware(
datetime.datetime(2018, 4, 14, 13, 40)
)
).exists()
)
Loading

0 comments on commit e126261

Please sign in to comment.