def lookup_case(search_field, search_id, domain, case_type): """ Attempt to find the case in CouchDB by the provided search_field and search_id. Returns a tuple with case (if found) and an error code (if there was an error in lookup). """ found = False case_accessors = CaseAccessors(domain) if search_field == 'case_id': try: case = case_accessors.get_case(search_id) if case.domain == domain and case.type == case_type: found = True except CaseNotFound: pass elif search_field == EXTERNAL_ID: cases_by_type = case_accessors.get_cases_by_external_id(search_id, case_type=case_type) if not cases_by_type: return (None, LookupErrors.NotFound) elif len(cases_by_type) > 1: return (None, LookupErrors.MultipleResults) else: case = cases_by_type[0] found = True if found: return (case, None) else: return (None, LookupErrors.NotFound)
class EnikshayCaseFactory(object): domain = None patient_detail = None def __init__(self, domain, patient_detail, nikshay_codes_to_location, test_phi=None): self.domain = domain self.patient_detail = patient_detail self.case_accessor = CaseAccessors(domain) self.nikshay_codes_to_location = nikshay_codes_to_location self.test_phi = test_phi @property def nikshay_id(self): return self.patient_detail.PregId @property @memoized def existing_person_case(self): """ Get the existing person case for this nikshay ID, or None if no person case exists """ matching_external_ids = self.case_accessor.get_cases_by_external_id( self.nikshay_id, case_type='person') if matching_external_ids: assert len(matching_external_ids) == 1 return matching_external_ids[0] return None @property def creating_person_case(self): return self.existing_person_case is not None @property @memoized def existing_occurrence_case(self): """ Get the existing occurrence case for this nikshay ID, or None if no occurrence case exists """ if self.existing_person_case: try: return get_open_occurrence_case_from_person( self.domain, self.existing_person_case.case_id) except ENikshayCaseNotFound: return None @property @memoized def existing_episode_case(self): """ Get the existing episode case for this nikshay ID, or None if no episode case exists """ if self.existing_occurrence_case: try: return get_open_episode_case_from_occurrence( self.domain, self.existing_occurrence_case.case_id) except ENikshayCaseNotFound: return None @property @memoized def existing_drtb_hiv_case(self): """ Get the existing episode case for this nikshay ID, or None if no episode case exists """ if self.existing_episode_case: try: return get_open_drtb_hiv_case_from_episode( self.domain, self.existing_episode_case.case_id) except ENikshayCaseNotFound: return None def get_case_structures_to_create(self): person_structure = self.get_person_case_structure() ocurrence_structure = self.get_occurrence_case_structure( person_structure) episode_structure = self.get_episode_case_structure( ocurrence_structure) if not self._outcome or self._outcome.hiv_status in [ 'unknown', 'reactive' ]: drtb_hiv_referral_structure = self.get_drtb_hiv_referral_case_structure( episode_structure) return [drtb_hiv_referral_structure] else: return [episode_structure] def get_person_case_structure(self): kwargs = { 'attrs': { 'case_type': PERSON_CASE_TYPE, 'external_id': self.nikshay_id, 'update': { 'age': self.patient_detail.page, 'age_entered': self.patient_detail.page, 'current_address': self.patient_detail.paddress, 'current_episode_type': 'confirmed_tb', 'current_patient_type_choice': self.patient_detail.patient_type_choice, 'dataset': 'real', 'dob': date(date.today().year - self.patient_detail.page, 7, 1), 'dob_known': 'no', 'has_open_tests': 'no', 'hiv_status': self._outcome.hiv_status if self._outcome else 'unknown', 'first_name': self.patient_detail.first_name, 'last_name': self.patient_detail.last_name, 'name': self.patient_detail.pname, 'person_id': self.patient_detail.person_id, 'phone_number': validate_phone_number(self.patient_detail.pmob), 'secondary_contact_name_address': ((self.patient_detail.cname or '') + ', ' + (self.patient_detail.caddress or '')), 'secondary_contact_phone_number': validate_phone_number(self.patient_detail.cmob), 'sex': self.patient_detail.sex, 'migration_created_case': 'true', }, }, } if self.phi: if self.phi.location_type.code == 'phi': kwargs['attrs']['owner_id'] = self.phi.location_id kwargs['attrs']['update']['phi'] = self.phi.name kwargs['attrs']['update'][ 'phi_assigned_to'] = self.phi.location_id kwargs['attrs']['update']['tu_choice'] = self.tu.location_id else: kwargs['attrs']['owner_id'] = ARCHIVED_CASE_OWNER_ID kwargs['attrs']['update'][ 'archive_reason'] = 'migration_not_phi_location' kwargs['attrs']['update'][ 'migration_error'] = 'not_phi_location' kwargs['attrs']['update'][ 'migration_error_details'] = self._phi_code else: kwargs['attrs']['owner_id'] = ARCHIVED_CASE_OWNER_ID kwargs['attrs']['update'][ 'archive_reason'] = 'migration_location_not_found' kwargs['attrs']['update']['migration_error'] = 'location_not_found' kwargs['attrs']['update'][ 'migration_error_details'] = self._phi_code if self._outcome: if self._outcome.hiv_status: kwargs['attrs']['update'][ 'hiv_status'] = self._outcome.hiv_status if self._outcome.is_treatment_ended: kwargs['attrs']['owner_id'] = ARCHIVED_CASE_OWNER_ID kwargs['attrs']['update']['is_active'] = 'no' else: kwargs['attrs']['update']['is_active'] = 'yes' if self._outcome.treatment_outcome == 'died': kwargs['attrs']['close'] = True else: kwargs['attrs']['update']['is_active'] = 'yes' if self.patient_detail.paadharno is not None: kwargs['attrs']['update'][ 'aadhaar_number'] = self.patient_detail.paadharno if self.existing_person_case is not None: kwargs['case_id'] = self.existing_person_case.case_id kwargs['attrs']['create'] = False else: kwargs['attrs']['create'] = True return CaseStructure(**kwargs) def get_occurrence_case_structure(self, person_structure): """ This gets the occurrence case structure with a nested person case structure. """ kwargs = { 'attrs': { 'case_type': OCCURRENCE_CASE_TYPE, 'owner_id': '-', 'update': { 'current_episode_type': 'confirmed_tb', 'ihv_date': self.patient_detail.ihv_date, 'initial_home_visit_status': self.patient_detail.initial_home_visit_status, 'name': 'Occurrence #1', 'occurrence_episode_count': 1, 'occurrence_id': get_human_friendly_id(), 'migration_created_case': 'true', }, }, 'indices': [ CaseIndex( person_structure, identifier='host', relationship=CASE_INDEX_EXTENSION, related_type=PERSON_CASE_TYPE, ) ], } if self._outcome: if self._outcome.is_treatment_ended: kwargs['attrs']['close'] = True if self.existing_occurrence_case: kwargs['case_id'] = self.existing_occurrence_case.case_id kwargs['attrs']['create'] = False else: kwargs['attrs']['create'] = True return CaseStructure(**kwargs) def get_episode_case_structure(self, occurrence_structure): """ This gets the episode case structure with a nested occurrence and person case structures inside of it. """ kwargs = { 'attrs': { 'case_type': EPISODE_CASE_TYPE, 'date_opened': self.patient_detail.pregdate1, 'owner_id': '-', 'update': { 'adherence_schedule_date_start': (self.patient_detail.treatment_initiation_date if self.patient_detail.treatment_initiation_date else self.patient_detail.pregdate1), 'adherence_schedule_id': 'schedule_mwf', 'date_of_diagnosis': self.patient_detail.pregdate1, 'date_of_mo_signature': (self.patient_detail.date_of_mo_signature if self.patient_detail.date_of_mo_signature else self.patient_detail.pregdate1), 'disease_classification': self.patient_detail.disease_classification, 'dots_99_enabled': 'false', 'episode_id': get_human_friendly_id(), 'episode_pending_registration': 'no', 'episode_type': 'confirmed_tb', 'name': 'Episode #1: Confirmed TB (Patient)', 'nikshay_id': self.nikshay_id, 'occupation': self.patient_detail.occupation, 'patient_type_choice': self.patient_detail.patient_type_choice, 'transfer_in': 'yes' if self.patient_detail.patient_type_choice == 'transfer_in' else '', 'treatment_card_completed_date': self.patient_detail.pregdate1, 'treatment_initiated': 'yes_phi', 'treatment_initiation_date': (self.patient_detail.treatment_initiation_date if self.patient_detail.treatment_initiation_date else self.patient_detail.pregdate1), 'treatment_supporter_designation': self.patient_detail.treatment_supporter_designation, 'treatment_supporter_first_name': self.patient_detail.treatment_supporter_first_name, 'treatment_supporter_last_name': self.patient_detail.treatment_supporter_last_name, 'treatment_supporter_mobile_number': validate_phone_number(self.patient_detail.dotmob), 'migration_created_case': 'true', }, }, 'indices': [ CaseIndex( occurrence_structure, identifier='host', relationship=CASE_INDEX_EXTENSION, related_type=OCCURRENCE_CASE_TYPE, ) ], } if self.patient_detail.disease_classification == 'extra_pulmonary': kwargs['attrs']['update'][ 'site_choice'] = self.patient_detail.site_choice if self._outcome: if self._outcome.treatment_outcome: kwargs['attrs']['update'][ 'treatment_outcome'] = self._outcome.treatment_outcome assert self._outcome.treatment_outcome_date is not None kwargs['attrs']['update'][ 'treatment_outcome_date'] = self._outcome.treatment_outcome_date if self._outcome.is_treatment_ended: kwargs['attrs']['close'] = True if self.existing_episode_case: kwargs['case_id'] = self.existing_episode_case.case_id kwargs['attrs']['create'] = False else: kwargs['attrs']['create'] = True return CaseStructure(**kwargs) def get_drtb_hiv_referral_case_structure(self, episode_structure): kwargs = { 'attrs': { 'case_type': DRTB_HIV_REFERRAL_CASE_TYPE, 'owner_id': self.drtb_hiv.location_id, 'update': { 'name': self.patient_detail.pname, } }, 'indices': [ CaseIndex( episode_structure, identifier='host', relationship=CASE_INDEX_EXTENSION, related_type=EPISODE_CASE_TYPE, ) ], } if self.existing_drtb_hiv_case: kwargs['case_id'] = self.existing_drtb_hiv_case.case_id kwargs['attrs']['create'] = False else: kwargs['attrs']['create'] = True return CaseStructure(**kwargs) @property @memoized def _outcome(self): zero_or_one_outcomes = list( Outcome.objects.filter(PatientId=self.patient_detail)) if zero_or_one_outcomes: return zero_or_one_outcomes[0] else: return None @property def tu(self): if self.test_phi is not None: return MockLocation('FAKETU', 'fake_tu_id', MockLocationType('tu', 'tu')) return self.nikshay_codes_to_location.get('-'.join( self._phi_code.split('-')[:3])) @property def phi(self): if self.test_phi is not None: return MockLocation('FAKEPHI', self.test_phi, MockLocationType('phi', 'phi')) return self.nikshay_codes_to_location.get(self._phi_code) @property def drtb_hiv(self): if self.test_phi is not None: return MockLocation('FAKEDRTBHIV', 'fake_drtb_hiv_id', MockLocationType('drtb_hiv', 'drtb_hiv')) dto = self.nikshay_codes_to_location['-'.join( self._phi_code.split('-')[:2])] for dto_child in dto.get_children(): if dto_child.location_type.code == 'drtb-hiv': return dto_child @property def _phi_code(self): return '%s-%s-%d-%d' % ( self.patient_detail.scode, self.patient_detail.Dtocode, self.patient_detail.Tbunitcode, self.patient_detail.PHI, )
class EnikshayCaseFactory(object): domain = None patient_detail = None def __init__(self, domain, patient_detail, nikshay_codes_to_location): self.domain = domain self.patient_detail = patient_detail self.case_accessor = CaseAccessors(domain) self.nikshay_codes_to_location = nikshay_codes_to_location @property def nikshay_id(self): return self.patient_detail.PregId @property @memoized def existing_person_case(self): """ Get the existing person case for this nikshay ID, or None if no person case exists """ matching_external_ids = self.case_accessor.get_cases_by_external_id(self.nikshay_id, case_type='person') if matching_external_ids: assert len(matching_external_ids) == 1 return matching_external_ids[0] return None @property def creating_person_case(self): return self.existing_person_case is not None @property @memoized def existing_occurrence_case(self): """ Get the existing occurrence case for this nikshay ID, or None if no occurrence case exists """ if self.existing_person_case: try: return get_open_occurrence_case_from_person( self.domain, self.existing_person_case.case_id ) except ENikshayCaseNotFound: return None @property @memoized def existing_episode_case(self): """ Get the existing episode case for this nikshay ID, or None if no episode case exists """ if self.existing_occurrence_case: try: return get_open_episode_case_from_occurrence( self.domain, self.existing_occurrence_case.case_id ) except ENikshayCaseNotFound: return None def get_case_structures_to_create(self): person_structure = self.get_person_case_structure() ocurrence_structure = self.get_occurrence_case_structure(person_structure) episode_structure = self.get_episode_case_structure(ocurrence_structure) test_structures = [ self.get_test_case_structure(followup, ocurrence_structure) for followup in self._followups ] return [episode_structure] + test_structures def get_person_case_structure(self): kwargs = { 'attrs': { 'case_type': PERSON_CASE_TYPE, 'external_id': self.nikshay_id, 'update': { 'age': self.patient_detail.page, 'age_entered': self.patient_detail.page, 'contact_phone_number': validate_phone_number(self.patient_detail.pmob), 'current_address': self.patient_detail.paddress, 'current_address_district_choice': self.district.location_id, 'current_address_state_choice': self.state.location_id, 'dob': date(date.today().year - self.patient_detail.page, 7, 1), 'dob_known': 'no', 'first_name': self.patient_detail.first_name, 'last_name': self.patient_detail.last_name, 'name': self.patient_detail.pname, 'nikshay_id': self.nikshay_id, 'person_id': 'FROM_NIKSHAY_' + self.nikshay_id, 'secondary_contact_name_address': ( (self.patient_detail.cname or '') + ', ' + (self.patient_detail.caddress or '') ), 'secondary_contact_phone_number': validate_phone_number(self.patient_detail.cmob), 'sex': self.patient_detail.sex, 'tu_choice': self.tu.name, 'migration_created_case': 'true', }, }, } if self.phi: if self.phi.location_type.code == 'phi': kwargs['attrs']['owner_id'] = self.phi.location_id kwargs['attrs']['update']['phi'] = self.phi.name else: kwargs['attrs']['owner_id'] = ARCHIVED_CASE_OWNER_ID kwargs['attrs']['update']['archive_reason'] = 'migration_not_phi_location' kwargs['attrs']['update']['migration_error'] = 'not_phi_location' kwargs['attrs']['update']['migration_error_details'] = self._nikshay_code else: kwargs['attrs']['owner_id'] = ARCHIVED_CASE_OWNER_ID kwargs['attrs']['update']['archive_reason'] = 'migration_location_not_found' kwargs['attrs']['update']['migration_error'] = 'location_not_found' kwargs['attrs']['update']['migration_error_details'] = self._nikshay_code if self.patient_detail.paadharno is not None: kwargs['attrs']['update']['aadhaar_number'] = self.patient_detail.paadharno if self.existing_person_case is not None: kwargs['case_id'] = self.existing_person_case.case_id kwargs['attrs']['create'] = False else: kwargs['attrs']['create'] = True return CaseStructure(**kwargs) def get_occurrence_case_structure(self, person_structure): """ This gets the occurrence case structure with a nested person case structure. """ kwargs = { 'attrs': { 'case_type': OCCURRENCE_CASE_TYPE, 'owner_id': '-', 'update': { 'current_episode_type': 'confirmed_tb', 'ihv_date': self.patient_detail.ihv_date, 'initial_home_visit_status': self.patient_detail.initial_home_visit_status, 'name': 'Occurrence #1', 'occurrence_episode_count': 1, 'occurrence_id': datetime.utcnow().strftime('%Y%m%d%H%M%S%f')[:-3], 'migration_created_case': 'true', }, }, 'indices': [CaseIndex( person_structure, identifier='host', relationship=CASE_INDEX_EXTENSION, related_type=PERSON_CASE_TYPE, )], } if self._outcome: # TODO - store with correct value kwargs['attrs']['update']['hiv_status'] = self._outcome.HIVStatus if self.existing_occurrence_case: kwargs['case_id'] = self.existing_occurrence_case.case_id kwargs['attrs']['create'] = False else: kwargs['attrs']['create'] = True return CaseStructure(**kwargs) def get_episode_case_structure(self, occurrence_structure): """ This gets the episode case structure with a nested occurrence and person case structures inside of it. """ kwargs = { 'attrs': { 'case_type': EPISODE_CASE_TYPE, 'date_opened': self.patient_detail.pregdate1, 'owner_id': '-', 'update': { 'date_of_mo_signature': self.patient_detail.date_of_mo_signature, 'disease_classification': self.patient_detail.disease_classification, 'dots_99_enabled': 'false', 'episode_pending_registration': 'no', 'episode_type': 'confirmed_tb', 'name': 'Episode #1: Confirmed TB (Patient)', 'occupation': self.patient_detail.occupation, 'patient_type_choice': self.patient_detail.patient_type_choice, 'treatment_initiation_date': self.patient_detail.treatment_initiation_date, 'treatment_supporter_designation': self.patient_detail.treatment_supporter_designation, 'treatment_supporter_first_name': self.patient_detail.treatment_supporter_first_name, 'treatment_supporter_last_name': self.patient_detail.treatment_supporter_last_name, 'treatment_supporter_mobile_number': validate_phone_number(self.patient_detail.dotmob), 'migration_created_case': 'true', }, }, 'indices': [CaseIndex( occurrence_structure, identifier='host', relationship=CASE_INDEX_EXTENSION, related_type=OCCURRENCE_CASE_TYPE, )], } if self.existing_episode_case: kwargs['case_id'] = self.existing_episode_case.case_id kwargs['attrs']['create'] = False else: kwargs['attrs']['create'] = True return CaseStructure(**kwargs) def get_test_case_structure(self, followup, occurrence_structure): kwargs = { 'attrs': { 'create': True, 'case_type': TEST_CASE_TYPE, 'owner_id': '-', 'update': { 'date_tested': followup.TestDate, 'migration_created_case': 'true', 'migration_followup_id': followup.id, }, }, 'indices': [CaseIndex( occurrence_structure, identifier='host', relationship=CASE_INDEX_EXTENSION, related_type=occurrence_structure.attrs['case_type'], )], # this prevents creating duplicate occurrence data on creation of the test cases 'walk_related': False, } if self.existing_occurrence_case: matching_test_case = next(( extension_case for extension_case in self.case_accessor.get_cases([ index.referenced_id for index in self.existing_occurrence_case.reverse_indices ]) if ( extension_case.type == TEST_CASE_TYPE and followup.id == int(extension_case.dynamic_case_properties().get('migration_followup_id', -1)) ) ), None) if matching_test_case: kwargs['case_id'] = matching_test_case.case_id kwargs['attrs']['create'] = False else: kwargs['attrs']['create'] = True return CaseStructure(**kwargs) @property @memoized def _outcome(self): zero_or_one_outcomes = list(Outcome.objects.filter(PatientId=self.patient_detail)) if zero_or_one_outcomes: return zero_or_one_outcomes[0] else: return None @property @memoized def _followups(self): return Followup.objects.filter(PatientID=self.patient_detail) @property def state(self): return self.nikshay_codes_to_location.get(self.patient_detail.PregId.split('-')[0]) @property def district(self): return self.nikshay_codes_to_location.get('-'.join(self.patient_detail.PregId.split('-')[:2])) @property def tu(self): return self.nikshay_codes_to_location.get('-'.join(self.patient_detail.PregId.split('-')[:3])) @property def phi(self): return self.nikshay_codes_to_location.get(self._nikshay_code) @property def _nikshay_code(self): return '-'.join(self.patient_detail.PregId.split('-')[:4])