diff --git a/apps/tb/migrations/0029_auto_20180815_1338.py b/apps/tb/migrations/0029_auto_20180815_1338.py index eea24c389..6274ecd15 100644 --- a/apps/tb/migrations/0029_auto_20180815_1338.py +++ b/apps/tb/migrations/0029_auto_20180815_1338.py @@ -11,7 +11,7 @@ class Migration(migrations.Migration): dependencies = [ - ('opal', '0035_auto_20180806_1150'), + ('opal', '0034_auto_20171214_1845'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('tb', '0028_pregnancy'), ] diff --git a/intrahospital_api/apis/prod_api.py b/intrahospital_api/apis/prod_api.py index 393e2372f..e6724a1e7 100644 --- a/intrahospital_api/apis/prod_api.py +++ b/intrahospital_api/apis/prod_api.py @@ -17,8 +17,12 @@ # if we fail in a query, the amount of seconds we wait before retrying RETRY_DELAY = 30 +MAIN_DEMOGRAPHICS_VIEW = "VIEW_CRS_Patient_Masterfile" -DEMOGRAPHICS_QUERY = "SELECT top(1) * FROM {view} WHERE Patient_Number = \ +PATHOLOGY_DEMOGRAPHICS_QUERY = "SELECT top(1) * FROM {view} WHERE Patient_Number = \ +@hospital_number ORDER BY last_updated DESC;" + +MAIN_DEMOGRAPHICS_QUERY = "SELECT top(1) * FROM {view} WHERE Patient_Number = \ @hospital_number ORDER BY last_updated DESC;" ALL_DATA_QUERY_FOR_HOSPITAL_NUMBER = "SELECT * FROM {view} WHERE Patient_Number = \ @@ -90,7 +94,61 @@ def wrap(*args, **kw): return wrap -class Row(object): +class MainDemographicsRow(object): + DEMOGRAPHICS_FIELDS = [ + "hospital_number", + "nhs_number", + "first_name", + "surname", + "date_of_birth", + "sex", + "ethnicity", + "title" + ] + + def __init__(self, db_row): + self.db_row = db_row + + def get_hospital_number(self): + return self.db_row.get("PATIENT_NUMBER") + + def get_nhs_number(self): + return self.db_row.get("NHS_NUMBER") + + def get_first_name(self): + return self.db_row.get("FORENAME1") + + def get_surname(self): + return self.db_row.get("SURNAME") + + def get_date_of_birth(self): + dob = self.db_row.get("DOB") + if dob: + return to_date_str(dob.date()) + + def get_sex(self): + sex_abbreviation = self.db_row.get("SEX") + + if sex_abbreviation: + if sex_abbreviation == "M": + return "Male" + else: + return "Female" + + def get_ethnicity(self): + return ETHNICITY_MAPPING.get(self.db_row.get("ETHNIC_GROUP")) + + def get_title(self): + return self.db_row.get("TITLE") + + def get_demographics_dict(self): + result = {} + for field in self.DEMOGRAPHICS_FIELDS: + result[field] = getattr(self, "get_{}".format(field))() + return result + + +class PathologyRow(object): """ a simple wrapper to get us the fields we actually want out of a row """ DEMOGRAPHICS_FIELDS = [ @@ -305,8 +363,8 @@ def execute_query(self, query, params=None): return result @property - def demographics_query(self): - return DEMOGRAPHICS_QUERY.format(view=self.view) + def pathology_demographics_query(self): + return PATHOLOGY_DEMOGRAPHICS_QUERY.format(view=self.view) @property def all_data_for_hospital_number_query(self): @@ -324,20 +382,48 @@ def all_data_query_for_lab_number(self): def all_data_query_for_lab_test_type(self): return ALL_DATA_QUERY_WITH_LAB_TEST_TYPE.format(view=self.view) - @timing - @db_retry + + @property + def main_demographics_query(self): + return MAIN_DEMOGRAPHICS_QUERY.format(view=MAIN_DEMOGRAPHICS_VIEW) + def demographics(self, hospital_number): hospital_number = hospital_number.strip() + + demographics_result = self.main_demographics(hospital_number) + + if not demographics_result: + demographics_result = self.pathology_demographics(hospital_number) + + if demographics_result: + demographics_result["external_system"] = EXTERNAL_SYSTEM + return demographics_result + + + @timing + @db_retry + def main_demographics(self, hospital_number): + rows = list(self.execute_query( + self.pathology_demographics_query, + params=dict(hospital_number=hospital_number) + )) + if not len(rows): + return + + return MainDemographicsRow(rows[0]).get_demographics_dict() + + + @timing + @db_retry + def pathology_demographics(self, hospital_number): rows = list(self.execute_query( - self.demographics_query, + self.pathology_demographics_query, params=dict(hospital_number=hospital_number) )) if not len(rows): return - demographics_dict = Row(rows[0]).get_demographics_dict() - demographics_dict["external_system"] = EXTERNAL_SYSTEM - return demographics_dict + return PathologyRow(rows[0]).get_demographics_dict() def raw_data(self, hospital_number, lab_number=None, test_type=None): """ not all data, I lied. Only the last year's @@ -375,7 +461,7 @@ def data_delta_query(self, since): self.all_data_since_query, params=dict(since=since) ) - return (Row(r) for r in all_rows) + return (PathologyRow(r) for r in all_rows) def data_deltas(self, some_datetime): """ yields an iterator of dictionary @@ -407,7 +493,7 @@ def data_deltas(self, some_datetime): def cooked_data(self, hospital_number): raw_data = self.raw_data(hospital_number) - return (Row(row).get_all_fields() for row in raw_data) + return (PathologyRow(row).get_all_fields() for row in raw_data) def cast_rows_to_lab_test(self, rows): """ We cast multiple rows to lab tests. @@ -453,5 +539,5 @@ def results_for_hospital_number(self, hospital_number): """ raw_rows = self.raw_data(hospital_number) - rows = (Row(raw_row) for raw_row in raw_rows) + rows = (PathologyRow(raw_row) for raw_row in raw_rows) return self.cast_rows_to_lab_test(rows) diff --git a/intrahospital_api/test/test_prod_api.py b/intrahospital_api/test/test_prod_api.py index 3d6b7739a..525d91083 100644 --- a/intrahospital_api/test/test_prod_api.py +++ b/intrahospital_api/test/test_prod_api.py @@ -5,10 +5,11 @@ from datetime import datetime, date from opal.core.test import OpalTestCase from intrahospital_api.apis import prod_api +from intrahospital_api import constants from lab import models as lmodels -FAKE_ROW_DATA = { +FAKE_PATHOLOGY_DATA = { u'Abnormal_Flag': u'', u'Accession_number': u'73151060487', u'CRS_ADDRESS_LINE1': u'James Centre', @@ -149,11 +150,11 @@ def tests_reraises(self, time): ) -class RowTestCase(OpalTestCase): +class PathologyRowTestCase(OpalTestCase): def get_row(self, **kwargs): - raw_data = copy.copy(FAKE_ROW_DATA) + raw_data = copy.copy(FAKE_PATHOLOGY_DATA) raw_data.update(kwargs) - return prod_api.Row(raw_data) + return prod_api.PathologyRow(raw_data) def test_get_or_fall_back_hit_first(self): row = self.get_row(Department="something") @@ -377,6 +378,18 @@ def test_get_all_fields(self): self.assertEqual(result, expected) +FAKE_MAIN_DEMOGRAPHICS_ROW = { + u'PATIENT_NUMBER': u'20552710', + u'NHS_NUMBER': u'111', + u'FORENAME1': u'TEST', + u'SURNAME': u'ZZZTEST', + u'DOB': datetime(1980, 10, 10, 0, 0), + u'SEX': u'F', + u'ETHNIC_GROUP': u'D', + u'TITLE': u'Ms', +} + + class ProdApiTestcase(OpalTestCase): REQUIRED_FIELDS = dict( ip_address="0.0.0.0", @@ -447,9 +460,9 @@ def test_execute_query_without_params(self, pytds): def test_raw_data(self, dt): dt.today.return_value = date(2017, 10, 1) api = self.get_api() - expected = [copy.copy(FAKE_ROW_DATA)] + expected = [copy.copy(FAKE_PATHOLOGY_DATA)] with mock.patch.object(api, 'execute_query') as execute_query: - execute_query.return_value = [copy.copy(FAKE_ROW_DATA)] + execute_query.return_value = [copy.copy(FAKE_PATHOLOGY_DATA)] result = api.raw_data("12312222") self.assertEqual(result, expected) @@ -469,15 +482,15 @@ def test_raw_data(self, dt): def test_cooked_data(self): api = self.get_api() with mock.patch.object(api, "raw_data") as raw_data: - raw_data.return_value = [copy.copy(FAKE_ROW_DATA)] + raw_data.return_value = [copy.copy(FAKE_PATHOLOGY_DATA)] rows = api.cooked_data("123") self.assertEqual(len(list(rows)), 1) - def test_demographics_success(self): + def test_pathology_demographics_success(self): api = self.get_api() with mock.patch.object(api, "execute_query") as execute_query: - execute_query.return_value = [FAKE_ROW_DATA] - result = api.demographics("123") + execute_query.return_value = [FAKE_PATHOLOGY_DATA] + result = api.pathology_demographics("123") self.assertEqual( result["first_name"], "TEST" @@ -492,18 +505,96 @@ def test_demographics_success(self): execute_query.call_args[1]["params"], dict(hospital_number="123") ) - def test_empty_demographics(self): + def test_pathology_demographics_hospital_number_fail(self): api = self.get_api() with mock.patch.object(api, "execute_query") as execute_query: execute_query.return_value = [] - result = api.demographics("123") + result = api.pathology_demographics("A1' 23") self.assertIsNone(result) - def test_demographics_hospital_number_fail(self): + def test_main_demographics_success(self): + api = self.get_api() + with mock.patch.object(api, "execute_query") as execute_query: + execute_query.return_value = [FAKE_MAIN_DEMOGRAPHICS_ROW] + result = api.main_demographics("123") + + self.assertEqual( + result["first_name"], "TEST" + ) + + self.assertEqual( + result["surname"], "ZZZTEST" + ) + self.assertEqual( + result["hospital_number"], "20552710" + ) + self.assertEqual( + result["date_of_birth"], "10/10/1980" + ) + self.assertEqual( + result["sex"], "Female" + ) + self.assertEqual( + result["title"], "Ms" + ) + self.assertEqual( + result["ethnicity"], "Mixed - White and Black Caribbean" + ) + + expected_query = "SELECT top(1) * FROM some_view WHERE Patient_Number \ += @hospital_number ORDER BY last_updated DESC;" + self.assertEqual( + execute_query.call_args[0][0], expected_query + ) + self.assertEqual( + execute_query.call_args[1]["params"], dict(hospital_number="123") + ) + + def test_main_demographics_fail(self): api = self.get_api() with mock.patch.object(api, "execute_query") as execute_query: execute_query.return_value = [] - result = api.demographics("A1' 23") + result = api.main_demographics("A1' 23") + + self.assertIsNone(result) + + def test_demographics_found_in_main(self): + api = self.get_api() + with mock.patch.object(api, "main_demographics") as main_demographics: + with mock.patch.object(api, "pathology_demographics") as pathology_demographics: + main_demographics.return_value = dict(first_name="Wilma") + result = api.demographics("111") + + self.assertEqual( + result, + dict(first_name="Wilma", external_system=constants.EXTERNAL_SYSTEM) + ) + + main_demographics.assert_called_once_with("111") + self.assertFalse(pathology_demographics.called) + + def test_demographics_found_in_pathology(self): + api = self.get_api() + with mock.patch.object(api, "main_demographics") as main_demographics: + with mock.patch.object(api, "pathology_demographics") as pathology_demographics: + main_demographics.return_value = None + pathology_demographics.return_value = dict(first_name="Wilma") + result = api.demographics("111") + + self.assertEqual( + result, + dict(first_name="Wilma", external_system=constants.EXTERNAL_SYSTEM) + ) + + main_demographics.assert_called_once_with("111") + pathology_demographics.assert_called_once_with("111") + + def test_demographics_not_found_in_either(self): + api = self.get_api() + + with mock.patch.object(api, "execute_query") as execute_query: + execute_query.return_value = [] + result = api.demographics("123") self.assertIsNone(result)