def test_extension_index(self): factory = CaseFactory() standard_case_id = uuid.uuid4().hex [case] = factory.create_or_update_case( CaseStructure(case_id=standard_case_id, attrs={'case_type': "standard_type", 'create': True}) ) extension_case_id = uuid.uuid4().hex factory.create_or_update_case( CaseStructure( case_id=extension_case_id, attrs={'case_type': "extension_type", 'create': True}, indices=[ CaseIndex( CaseStructure(case_id=standard_case_id), related_type='standard_type', relationship=CASE_INDEX_EXTENSION ) ], walk_related=False ) ) hierarchy = get_case_hierarchy(case, {}) self.assertEqual(2, len(hierarchy['case_list'])) self.assertEqual(1, len(hierarchy['child_cases']))
class TestIndexedCaseIds(TestCase): def setUp(self): super(TestIndexedCaseIds, self).setUp() self.domain = 'domain' self.factory = CaseFactory(self.domain) def tearDown(self): FormProcessorTestUtils.delete_all_cases() FormProcessorTestUtils.delete_all_xforms() super(TestIndexedCaseIds, self).tearDown() @run_with_all_backends def test_indexed_case_ids_returns_extensions(self): """ When getting indices, also return extensions """ host_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex host = CaseStructure(case_id=host_id) self.factory.create_or_update_case( CaseStructure( case_id=extension_id, indices=[CaseIndex(host, relationship=CASE_INDEX_EXTENSION)])) returned_cases = CaseAccessors(self.domain).get_indexed_case_ids( [extension_id]) self.assertItemsEqual(returned_cases, [host_id])
def test_normal_index(self): factory = CaseFactory() parent_id = uuid.uuid4().hex [cp] = factory.create_or_update_case( CaseStructure(case_id=parent_id, attrs={ 'case_type': 'parent', 'create': True })) child_id = uuid.uuid4().hex factory.create_or_update_case( CaseStructure(case_id=child_id, attrs={ 'case_type': 'child', 'create': True }, indices=[ CaseIndex(CaseStructure(case_id=parent_id), related_type='parent') ], walk_related=False)) hierarchy = get_case_hierarchy(cp, {}) self.assertEqual(2, len(hierarchy['case_list'])) self.assertEqual(1, len(hierarchy['child_cases']))
class TestIndexedCaseIds(TestCase): def setUp(self): super(TestIndexedCaseIds, self).setUp() self.domain = 'domain' self.factory = CaseFactory(self.domain) def tearDown(self): FormProcessorTestUtils.delete_all_cases() FormProcessorTestUtils.delete_all_xforms() super(TestIndexedCaseIds, self).tearDown() def test_indexed_case_ids_returns_extensions(self): """ When getting indices, also return extensions """ host_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex host = CaseStructure(case_id=host_id, attrs={'create': True}) self.factory.create_or_update_case( CaseStructure( case_id=extension_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION) ], attrs={'create': True} ) ) returned_cases = CaseAccessors(self.domain).get_indexed_case_ids([extension_id]) self.assertItemsEqual(returned_cases, [host_id])
class TestIndexedCaseIds(TestCase): @classmethod def setUpClass(cls): delete_all_cases() def setUp(self): self.domain = 'domain' self.factory = CaseFactory(self.domain) def test_indexed_case_ids_returns_extensions(self): """ When getting indices, also return extensions """ host_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex host = CaseStructure(case_id=host_id) self.factory.create_or_update_case( CaseStructure( case_id=extension_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION) ] ) ) returned_cases = get_indexed_case_ids(self.domain, [extension_id]) self.assertItemsEqual(returned_cases, [host_id])
class TestCaseHierarchyContext(TestCase): def setUp(self): self.factory = CaseFactory() parent_id = uuid.uuid4().hex [self.parent] = self.factory.create_or_update_case( CaseStructure(case_id=parent_id, attrs={ 'case_type': 'parent', 'create': True })) child_id = uuid.uuid4().hex [self.child] = self.factory.create_or_update_case( CaseStructure(case_id=child_id, attrs={ 'case_type': 'child', 'create': True }, indices=[ CaseIndex(CaseStructure(case_id=parent_id), related_type='parent') ], walk_related=False)) def tearDown(self): self.parent.delete() self.child.delete() def test_case_hierarchy_context_parent(self): hierarchy = case_hierarchy_context(self.parent, _get_case_url_blank) self.assertEqual(2, len(hierarchy['case_list'])) def test_case_hierarchy_context_parent_deleted_index(self): self._delete_child_index() hierarchy = case_hierarchy_context(self.parent, _get_case_url_blank) self.assertEqual(1, len(hierarchy['case_list'])) def test_case_hierarchy_context_child(self): hierarchy = case_hierarchy_context(self.child, _get_case_url_blank) self.assertEqual(2, len(hierarchy['case_list'])) def test_case_hierarchy_context_child_deleted_index(self): self._delete_child_index() hierarchy = case_hierarchy_context(self.child, _get_case_url_blank) self.assertEqual(1, len(hierarchy['case_list'])) def _delete_child_index(self): ref = CaseStructure() ref.case_id = "" # reset case_id to empty self.factory.create_or_update_case( CaseStructure(case_id=self.child.case_id, indices=[CaseIndex(ref, related_type='parent')], walk_related=False), ) # re-fetch case to clear memoized properties self.parent = CommCareCase.objects.get_case(self.parent.case_id, self.parent.domain) self.child = CommCareCase.objects.get_case(self.child.case_id, self.parent.domain)
def setUpClass(cls): super(TestFormsExpressionSpec, cls).setUpClass() cls.domain = uuid.uuid4().hex factory = CaseFactory(domain=cls.domain) [cls.case] = factory.create_or_update_case(CaseStructure(attrs={'create': True})) cls.forms = [f.to_json() for f in FormAccessors(cls.domain).get_forms(cls.case.xform_ids)] # redundant case to create extra forms that shouldn't be in the results for cls.case [cls.case_b] = factory.create_or_update_case(CaseStructure(attrs={'create': True}))
def handle(self, domain, log_path, **options): commit = options['commit'] factory = CaseFactory(domain) logger.info("Starting {} migration on {} at {}".format( "real" if commit else "fake", domain, datetime.datetime.utcnow())) cases = (CaseSearchES().domain(domain).case_type("episode").scroll()) with open(log_path, "w") as f: for case in cases: case_props = { prop['key']: prop['value'] for prop in case['case_properties'] } treatment_status = None treatment_initiated = case_props.get('treatment_initiated') diagnosing_facility_id = case_props.get( 'diagnosing_facility_id') treatment_initiating_facility_id = case_props.get( 'treatment_initiating_facility_id') if treatment_initiated == 'yes_phi' and \ diagnosing_facility_id and treatment_initiating_facility_id and \ diagnosing_facility_id != treatment_initiating_facility_id: treatment_status = 'initiated_outside_facility' elif treatment_initiated == 'yes_phi' and \ diagnosing_facility_id and treatment_initiating_facility_id: treatment_status = 'initiated_first_line_treatment' elif treatment_initiated == 'yes_private': treatment_status = 'initiated_outside_rntcp' if treatment_status: case_id = case['_id'] f.write(case_id + "\n") logger.info(case_id) case_structure = CaseStructure( case_id=case_id, walk_related=False, attrs={ "create": False, "update": { "treatment_status": treatment_status, "updated_by_migration": "enikshay_2b_treatment_status_fix", }, }, ) if commit: try: factory.create_or_update_case(case_structure) except NikshayLocationNotFound: pass logger.info("Migration finished at {}".format( datetime.datetime.utcnow()))
def test_complex_index(self): factory = CaseFactory() parent_id = uuid.uuid4().hex cp = factory.create_or_update_case( CaseStructure(case_id=parent_id, attrs={ 'case_type': 'parent', 'create': True }))[0] # cases processed according to ID order so ensure that this case is # processed after the task case by making its ID sort after task ID goal_id = uuid.uuid4().hex factory.create_or_update_case( CaseStructure(case_id=goal_id, attrs={ 'case_type': 'goal', 'create': True }, indices=[ CaseIndex(CaseStructure(case_id=parent_id), related_type='parent') ], walk_related=False)) task_id = uuid.uuid4().hex factory.create_or_update_case( CaseStructure( case_id=task_id, attrs={ 'case_type': 'task', 'create': True }, indices=[ CaseIndex(CaseStructure(case_id=goal_id), related_type='goal', identifier='goal'), CaseIndex(CaseStructure(case_id=parent_id), related_type='parent') ], walk_related=False, )) # with 'ignore_relationship_types' if a case got processed along the ignored relationship first # then it got marked as 'seen' and would be not be processed again when it came to the correct relationship type_info = { 'task': { 'ignore_relationship_types': ['parent'] }, } hierarchy = get_case_hierarchy(cp, type_info) self.assertEqual(3, len(hierarchy['case_list'])) self.assertEqual(1, len(hierarchy['child_cases'])) self.assertEqual(2, len(hierarchy['child_cases'][0]['case_list'])) self.assertEqual(1, len(hierarchy['child_cases'][0]['child_cases']))
class TestGetSubcasesExpression(TestCase): def setUp(self): super(TestGetSubcasesExpression, self).setUp() self.domain = uuid.uuid4().hex self.factory = CaseFactory(domain=self.domain) self.expression = ExpressionFactory.from_spec({ "type": "get_subcases", "case_id_expression": { "type": "property_name", "property_name": "_id" }, }) self.context = EvaluationContext({"domain": self.domain}) def tearDown(self): delete_all_xforms() delete_all_cases() super(TestGetSubcasesExpression, self).tearDown() @run_with_all_backends def test_no_subcases(self): case = self.factory.create_case() subcases = self.expression(case.to_json(), self.context) self.assertEqual(len(subcases), 0) @run_with_all_backends def test_single_child(self): parent_id = uuid.uuid4().hex child_id = uuid.uuid4().hex [child, parent] = self.factory.create_or_update_case(CaseStructure( case_id=child_id, indices=[ CaseIndex(CaseStructure(case_id=parent_id, attrs={'create': True})) ] )) subcases = self.expression(parent.to_json(), self.context) self.assertEqual(len(subcases), 1) self.assertEqual(child.case_id, subcases[0]['_id']) @run_with_all_backends def test_single_extension(self): host_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex [extension, host] = self.factory.create_or_update_case(CaseStructure( case_id=extension_id, indices=[ CaseIndex( CaseStructure(case_id=host_id, attrs={'create': True}), relationship=CASE_INDEX_EXTENSION ) ] )) subcases = self.expression(host.to_json(), self.context) self.assertEqual(len(subcases), 1) self.assertEqual(extension.case_id, subcases[0]['_id'])
def setUpClass(cls): super(TestFormsExpressionSpecWithFilter, cls).setUpClass() global es_form_cache cls.domain = uuid.uuid4().hex factory = CaseFactory(domain=cls.domain) [cls.case] = factory.create_or_update_case(CaseStructure(attrs={'create': True})) cls.forms = [f.to_json() for f in FormAccessors(cls.domain).get_forms(cls.case.xform_ids)] for form in cls.forms: es_form_cache.append(deepcopy(form)) # redundant case to create extra forms that shouldn't be in the results for cls.case [cls.case_b] = factory.create_or_update_case(CaseStructure(attrs={'create': True})) cls.forms_b = [f.to_json() for f in FormAccessors(cls.domain).get_forms(cls.case_b.xform_ids)] for form in cls.forms_b: es_form_cache.append(deepcopy(form))
def test_normal_index(self): factory = CaseFactory() [cp] = factory.create_or_update_case( CaseStructure(case_id='parent', attrs={'case_type': 'parent'}) ) factory.create_or_update_case(CaseStructure( case_id='child', attrs={'case_type': 'child'}, indices=[CaseIndex(CaseStructure(case_id='parent'), related_type='parent')], walk_related=False )) hierarchy = get_case_hierarchy(cp, {}) self.assertEqual(2, len(hierarchy['case_list'])) self.assertEqual(1, len(hierarchy['child_cases']))
def test_deleted_indices_removed(self): factory = CaseFactory( self.domain, case_defaults={ 'user_id': self.commcare_user._id, 'owner_id': self.commcare_user._id, 'case_type': 'a-case', 'create': True, }, ) # create a parent/child set of cases parent_id, child_id = [uuid.uuid4().hex for i in range(2)] child, parent = factory.create_or_update_case(CaseStructure( case_id=child_id, indices=[ CaseIndex(CaseStructure(case_id=parent_id)) ] )) # confirm the child has an index, and 1 form self.assertEqual(1, len(child.indices)) self.assertEqual(parent_id, child.indices[0].referenced_id) self.assertEqual(1, len(child.xform_ids)) # simulate parent deletion parent.soft_delete() # call the remove index task remove_indices_from_deleted_cases(self.domain, [parent_id]) # check that the index is removed via a new form child = CaseAccessors(self.domain).get_case(child_id) self.assertEqual(0, len(child.indices)) self.assertEqual(2, len(child.xform_ids))
def setUp(self): super(TestFormsExpressionSpec, self).setUp() self.domain = uuid.uuid4().hex factory = CaseFactory(domain=self.domain) [self.case] = factory.create_or_update_case(CaseStructure(attrs={'create': True})) self.forms = [f.to_json() for f in FormAccessors(self.domain).get_forms(self.case.xform_ids)] # redundant case to create extra forms that shouldn't be in the results for self.case [self.case_b] = factory.create_or_update_case(CaseStructure(attrs={'create': True})) self.expression = ExpressionFactory.from_spec({ "type": "get_case_forms", "case_id_expression": { "type": "property_name", "property_name": "_id" }, })
def test_form_extras_override_defaults(self): domain = uuid.uuid4().hex token_id = uuid.uuid4().hex factory = CaseFactory(domain=domain, form_extras={'last_sync_token': token_id}) [case] = factory.create_or_update_case(CaseStructure(attrs={'create': True}), form_extras={'last_sync_token': 'differenttoken'}) form = FormAccessors(domain).get_form(case.xform_ids[0]) self.assertEqual('differenttoken', form.last_sync_token)
def test_deleted_indices_removed(self): factory = CaseFactory( self.domain, case_defaults={ 'user_id': self.commcare_user._id, 'owner_id': self.commcare_user._id, 'case_type': 'a-case', 'create': True, }, ) # create a parent/child set of cases parent_id, child_id = [uuid.uuid4().hex for i in range(2)] child, parent = factory.create_or_update_case( CaseStructure(case_id=child_id, relationships=[ CaseRelationship( CaseStructure(case_id=parent_id)) ])) # confirm the child has an index, and 1 form self.assertEqual(1, len(child.indices)) self.assertEqual(parent_id, child.indices[0].referenced_id) self.assertEqual(1, len(child.xform_ids)) # simulate parent deletion parent.doc_type = 'CommCareCase-Deleted' parent.save() # call the remove index task remove_indices_from_deleted_cases(self.domain, [parent_id]) # check that the index is removed via a new form child = CommCareCase.get(child_id) self.assertEqual(0, len(child.indices)) self.assertEqual(2, len(child.xform_ids))
def test_simple_delete(self): factory = CaseFactory() case = factory.create_case() [case] = factory.create_or_update_case( CaseStructure(case_id=case.case_id, attrs={'update': {'foo': 'bar'}}) ) self.assertIsNotNone(self.casedb.get_case(case.case_id)) self.assertEqual(2, len(case.xform_ids)) for form_id in case.xform_ids: self.assertIsNotNone(self.formdb.get_form(form_id)) with capture_kafka_changes_context(topics.FORM_SQL, topics.CASE_SQL) as change_context: safe_hard_delete(case) if should_use_sql_backend(case.domain): self.assertEqual(3, len(change_context.changes)) expected_ids = {case.case_id} | set(case.xform_ids) self.assertEqual(expected_ids, {change.id for change in change_context.changes}) for change in change_context.changes: self.assertTrue(change.deleted) with self.assertRaises(CaseNotFound): self.casedb.get_case(case.case_id) for form_id in case.xform_ids: with self.assertRaises(XFormNotFound): self.formdb.get_form(form_id)
def test_form_extras(self): domain = uuid.uuid4().hex LOOSE_SYNC_TOKEN_VALIDATION.set(domain, True, namespace='domain') token_id = uuid.uuid4().hex factory = CaseFactory(domain=domain) [case] = factory.create_or_update_case(CaseStructure(), form_extras={'last_sync_token': token_id}) form = FormProcessorInterface(domain).xform_model.get(case.xform_ids[0]) self.assertEqual(token_id, form.last_sync_token)
def test_deleted_index(self): hierarchy = self.test_normal_index() parent, child = hierarchy['case_list'] factory = CaseFactory() ref = CaseStructure() ref.case_id = "" # reset case_id to empty factory.create_or_update_case( CaseStructure(case_id=child.case_id, indices=[CaseIndex(ref, related_type='parent')], walk_related=False), ) # re-fetch case to clear memoized properties parent = CommCareCase.objects.get_case(parent.case_id, parent.domain) hierarchy = get_case_hierarchy(parent, {}) self.assertEqual(1, len(hierarchy['case_list'])) self.assertEqual(0, len(hierarchy['child_cases']))
def handle(self, domain, log_path, **options): commit = options['commit'] factory = CaseFactory(domain) logger.info("Starting {} migration on {} at {}".format( "real" if commit else "fake", domain, datetime.datetime.utcnow())) cases = (CaseSearchES().domain(domain).case_type( "episode").case_property_query("case_version", "20", "must").scroll()) with open(log_path, "w") as f: for case in cases: case_props = { prop['key']: prop['value'] for prop in case['case_properties'] } referred_by_id = case_props.get('referred_by_id') updated_by_migration = case_props.get('updated_by_migration') if ((updated_by_migration == 'enikshay_2b_case_properties' or updated_by_migration == 'enikshay_2b_treatment_status_fix') and referred_by_id): case_id = case['_id'] f.write(case_id + "\n") logger.info(case_id) case_structure = CaseStructure( case_id=case_id, walk_related=False, attrs={ "create": False, "update": { "referred_outside_enikshay_by_id": referred_by_id, "updated_by_migration": "enikshay_2b_referred_by_id_fix", }, }, ) if commit: factory.create_or_update_case(case_structure) logger.info("Migration finished at {}".format( datetime.datetime.utcnow()))
def test_form_extras_override_defaults(self): domain = uuid.uuid4().hex LOOSE_SYNC_TOKEN_VALIDATION.set(domain, True, namespace="domain") token_id = uuid.uuid4().hex factory = CaseFactory(domain=domain, form_extras={"last_sync_token": token_id}) [case] = factory.create_or_update_case(CaseStructure(), form_extras={"last_sync_token": "differenttoken"}) form = FormAccessors(domain).get_form(case.xform_ids[0]) self.assertEqual("differenttoken", form.last_sync_token)
def test_form_extras_override_defaults(self): domain = uuid.uuid4().hex LOOSE_SYNC_TOKEN_VALIDATION.set(domain, True, namespace='domain') token_id = uuid.uuid4().hex factory = CaseFactory(domain=domain, form_extras={'last_sync_token': token_id}) [case] = factory.create_or_update_case(CaseStructure(), form_extras={'last_sync_token': 'differenttoken'}) form = XFormInstance.get(case.xform_ids[0]) self.assertEqual('differenttoken', form.last_sync_token)
def test_form_extras(self): domain = uuid.uuid4().hex token_id = uuid.uuid4().hex factory = CaseFactory(domain=domain) [case] = factory.create_or_update_case( CaseStructure(attrs={'create': True}), form_extras={'last_sync_token': token_id}) form = XFormInstance.objects.get_form(case.xform_ids[0], domain) self.assertEqual(token_id, form.last_sync_token)
def test_form_extras(self): domain = uuid.uuid4().hex LOOSE_SYNC_TOKEN_VALIDATION.set(domain, True, namespace='domain') token_id = uuid.uuid4().hex factory = CaseFactory(domain=domain) [case] = factory.create_or_update_case( CaseStructure(), form_extras={'last_sync_token': token_id}) form = FormAccessors(domain).get_form(case.xform_ids[0]) self.assertEqual(token_id, form.last_sync_token)
def test_no_walk_related(self): factory = CaseFactory() parent = factory.create_case() child_updates = factory.create_or_update_case( CaseStructure(attrs={'create': True}, walk_related=False, indices=[ CaseIndex(CaseStructure(case_id=parent.case_id)) ]), ) self.assertEqual(1, len(child_updates)) self.assertEqual(parent.case_id, child_updates[0].indices[0].referenced_id)
def test_no_walk_related(self): factory = CaseFactory() parent = factory.create_case() child_updates = factory.create_or_update_case( CaseStructure(attrs={'create': True}, walk_related=False, relationships=[ CaseRelationship(CaseStructure(case_id=parent._id)) ]), ) self.assertEqual(1, len(child_updates)) self.assertEqual(parent._id, child_updates[0].indices[0].referenced_id)
def test_form_extras_override_defaults(self): domain = uuid.uuid4().hex LOOSE_SYNC_TOKEN_VALIDATION.set(domain, True, namespace='domain') token_id = uuid.uuid4().hex factory = CaseFactory(domain=domain, form_extras={'last_sync_token': token_id}) [case] = factory.create_or_update_case( CaseStructure(), form_extras={'last_sync_token': 'differenttoken'}) form = XFormInstance.get(case.xform_ids[0]) self.assertEqual('differenttoken', form.last_sync_token)
def handle(self, domain, log_path, **options): commit = options['commit'] factory = CaseFactory(domain) logger.info("Starting {} migration on {} at {}".format( "real" if commit else "fake", domain, datetime.datetime.utcnow())) cases = (CaseSearchES().domain(domain).case_type( "test").case_property_query("updated_by_migration", "enikshay_2b_case_properties", "must").run().hits) with open(log_path, "w") as f: for case in cases: case_props = { prop['key']: prop['value'] for prop in case['case_properties'] } if (case_props.get('purpose_of_testing') == "follow_up_dstb" and case_props.get("rft_general", "") in ("", None)): case_id = case['_id'] f.write(case_id + "\n") logger.info(case_id) case_structure = CaseStructure( case_id=case_id, walk_related=False, attrs={ "create": False, "update": { "rft_general": "follow_up_dstb", "updated_by_migration": "enikshay_2b_reason_for_test_fix", }, }, ) if commit: factory.create_or_update_case(case_structure) logger.info("Migration finished at {}".format( datetime.datetime.utcnow()))
def setUp(self): super(TestGetCaseFormsExpressionTest, self).setUp() self.domain = uuid.uuid4().hex factory = CaseFactory(domain=self.domain) self.test_case_id = uuid.uuid4().hex factory.create_or_update_case(CaseStructure( case_id=self.test_case_id, attrs={ 'case_type': 'test', 'create': True, 'date_opened': datetime(2015, 1, 10), 'date_modified': datetime(2015, 3, 10), }, )) self._submit_form(form_date=datetime(2015, 1, 10), case_id=self.test_case_id, xmlns="xmlns_a", foo="a") self._submit_form(form_date=datetime(2015, 1, 11), case_id=self.test_case_id, xmlns="xmlns_a", foo="a") self._submit_form(form_date=datetime(2015, 2, 3), case_id=self.test_case_id, xmlns="xmlns_b", foo="b") self._submit_form(form_date=datetime(2015, 3, 3), case_id=self.test_case_id, xmlns="xmlns_b", foo="b") self._submit_form(form_date=datetime(2015, 3, 4), case_id=self.test_case_id, xmlns="xmlns_b", foo="b") self._submit_form(form_date=datetime(2015, 3, 5), case_id=self.test_case_id, xmlns="xmlns_c", foo="b")
def test_complex_index(self): factory = CaseFactory() parent_id = uuid.uuid4().hex cp = factory.create_or_update_case(CaseStructure(case_id=parent_id, attrs={ 'case_type': 'parent', 'create': True }))[0] # cases processed according to ID order so ensure that this case is # processed after the task case by making its ID sort after task ID goal_id = uuid.uuid4().hex factory.create_or_update_case(CaseStructure( case_id=goal_id, attrs={'case_type': 'goal', 'create': True}, indices=[CaseIndex(CaseStructure(case_id=parent_id), related_type='parent')], walk_related=False )) task_id = uuid.uuid4().hex factory.create_or_update_case(CaseStructure( case_id=task_id, attrs={'case_type': 'task', 'create': True}, indices=[ CaseIndex(CaseStructure(case_id=goal_id), related_type='goal', identifier='goal'), CaseIndex(CaseStructure(case_id=parent_id), related_type='parent') ], walk_related=False, )) # with 'ignore_relationship_types' if a case got processed along the ignored relationship first # then it got marked as 'seen' and would be not be processed again when it came to the correct relationship type_info = { 'task': { 'ignore_relationship_types': ['parent'] }, } hierarchy = get_case_hierarchy(cp, type_info) self.assertEqual(3, len(hierarchy['case_list'])) self.assertEqual(1, len(hierarchy['child_cases'])) self.assertEqual(2, len(hierarchy['child_cases'][0]['case_list'])) self.assertEqual(1, len(hierarchy['child_cases'][0]['child_cases']))
def test_recursive_indexes(self): factory = CaseFactory() [case] = factory.create_or_update_case(CaseStructure( case_id='infinite-recursion', attrs={'case_type': 'bug'}, indices=[CaseIndex(CaseStructure(case_id='infinite-recursion'), related_type='bug')], walk_related=False )) # this call used to fail with infinite recursion hierarchy = get_case_hierarchy(case, {}) self.assertEqual(1, len(hierarchy['case_list']))
def test_recursive_indexes(self): factory = CaseFactory() [case] = factory.create_or_update_case(CaseStructure( case_id='infinite-recursion', attrs={'case_type': 'bug', 'create': True}, indices=[CaseIndex(CaseStructure(case_id='infinite-recursion', attrs={'create': True}), related_type='bug')], walk_related=False )) # this call used to fail with infinite recursion hierarchy = get_case_hierarchy(case, {}) self.assertEqual(1, len(hierarchy['case_list']))
def test_simple_delete(self): factory = CaseFactory() case = factory.create_case() [case] = factory.create_or_update_case(CaseStructure(case_id=case._id, attrs={'update': {'foo': 'bar'}})) self.assertTrue(CommCareCase.get_db().doc_exist(case._id)) self.assertEqual(2, len(case.xform_ids)) for form_id in case.xform_ids: self.assertTrue(XFormInstance.get_db().doc_exist(form_id)) safe_hard_delete(case) self.assertFalse(CommCareCase.get_db().doc_exist(case._id)) for form_id in case.xform_ids: self.assertFalse(XFormInstance.get_db().doc_exist(form_id))
class TestCaseHistory(TestCase): def setUp(self): self.domain = "isildur" self.factory = CaseFactory(self.domain) self.case = self.factory.create_case(owner_id='owner', case_name="Aragorn", update={"prop_1": "val1"}) self.other_case = self.factory.create_case() def tearDown(self): delete_all_xforms() delete_all_cases() @run_with_all_backends def test_case_history(self): self.factory.create_or_update_case( CaseStructure( self.case.case_id, attrs={ "update": { 'prop_1': "val1", 'prop_2': "val1", 'prop_3': "val1", }, }), ) self.factory.create_or_update_case( CaseStructure( self.case.case_id, attrs={ "update": { 'prop_1': "val2", 'prop_2': "val2", 'prop_4': "val", }, }), ) case = CaseAccessors(self.domain).get_case(self.case.case_id) history = get_case_history(case) self.assertEqual(history[0]['prop_1'], "val1") self.assertEqual(history[1]['prop_2'], "val1") self.assertEqual(history[2]['prop_2'], "val2")
def test_delete_with_related(self): factory = CaseFactory() parent = factory.create_case() [child] = factory.create_or_update_case( CaseStructure(attrs={'create': True}, walk_related=False, relationships=[ CaseRelationship(CaseStructure(case_id=parent._id)) ]), ) # deleting the parent should not be allowed because the child still references it with self.assertRaises(CommCareCaseError): safe_hard_delete(parent) # deleting the child is ok safe_hard_delete(child) self.assertTrue(CommCareCase.get_db().doc_exist(parent._id)) self.assertFalse(CommCareCase.get_db().doc_exist(child._id))
def test_simple_delete(self): factory = CaseFactory() case = factory.create_case() [case] = factory.create_or_update_case(CaseStructure(case_id=case._id, attrs={'update': {'foo': 'bar'}})) self.assertIsNotNone(FormProcessorInterface.get_case(case.id)) self.assertEqual(2, len(case.xform_ids)) for form_id in case.xform_ids: self.assertIsNotNone(FormProcessorInterface.get_xform(form_id)) FormProcessorInterface.hard_delete_case(case) with self.assertRaises(CaseNotFound): FormProcessorInterface.get_case(case.id) for form_id in case.xform_ids: with self.assertRaises(XFormNotFound): FormProcessorInterface.get_xform(form_id)
def test_delete_with_related(self): factory = CaseFactory() parent = factory.create_case() [child] = factory.create_or_update_case( CaseStructure( attrs={'create': True}, walk_related=False, indices=[CaseIndex(CaseStructure(case_id=parent.case_id))]), ) # deleting the parent should not be allowed because the child still references it with self.assertRaises(CommCareCaseError): safe_hard_delete(parent) # deleting the child is ok safe_hard_delete(child) self.assertIsNotNone(self.casedb.get_case(parent.case_id)) with self.assertRaises(CaseNotFound): self.casedb.get_case(child.case_id)
def test_delete_with_related(self): factory = CaseFactory() parent = factory.create_case() [child] = factory.create_or_update_case( CaseStructure(attrs={'create': True}, walk_related=False, indices=[ CaseIndex(CaseStructure(case_id=parent._id)) ]), ) # deleting the parent should not be allowed because the child still references it with self.assertRaises(CommCareCaseError): FormProcessorInterface.hard_delete_case(parent) # deleting the child is ok FormProcessorInterface.hard_delete_case(child) self.assertIsNotNone(FormProcessorInterface.get_case(parent.id)) with self.assertRaises(CaseNotFound): FormProcessorInterface.get_case(child.id)
def test_delete_with_related(self): factory = CaseFactory() parent = factory.create_case() [child] = factory.create_or_update_case( CaseStructure(attrs={'create': True}, walk_related=False, relationships=[ CaseRelationship( CaseStructure(case_id=parent._id)) ]), ) # deleting the parent should not be allowed because the child still references it with self.assertRaises(CommCareCaseError): safe_hard_delete(parent) # deleting the child is ok safe_hard_delete(child) self.assertTrue(CommCareCase.get_db().doc_exist(parent._id)) self.assertFalse(CommCareCase.get_db().doc_exist(child._id))
def test_update_cases(self): builder1 = (IntentCaseBuilder(self.registry_slug).target_case( self.target_domain, self.target_case_id_1).case_properties(new_prop="new_val_case1")) builder2 = (IntentCaseBuilder(self.registry_slug).target_case( self.target_domain, self.target_case_id_2).case_properties(new_prop="new_val_case2")) factory = CaseFactory(self.domain) host = CaseStructure(attrs={ "create": True, "case_type": "registry_case_update", "update": builder1.props }, ) extension = CaseStructure( attrs={ "create": True, "case_type": "registry_case_update", "update": builder2.props }, indices=[CaseIndex( host, relationship=CASE_INDEX_EXTENSION, )]) cases = factory.create_or_update_case(extension, user_id=self.mobile_user.get_id) repeat_records = self.repeat_records(self.domain).all() self.assertEqual(len(repeat_records), 1) payload = repeat_records[0].get_payload() host_case = cases[1] form = DataRegistryUpdateForm(payload, host_case) form.assert_case_updates({ self.target_case_id_1: { "new_prop": "new_val_case1" }, self.target_case_id_2: { "new_prop": "new_val_case2" } }) url = self.repeater.get_url(repeat_records[0]) self.assertEqual(url, f"case-repeater-url/{self.target_domain}/") # check that the synchronous attempt of the repeat record happened self.assertEqual(1, len(repeat_records[0].attempts))
def test_full_ancestry(self): """get_case_hierarchy should return the full parentage tree for any case """ factory = CaseFactory('baggins-of-hobbiton') bagginses = ['balbo', 'mungo', 'bungo', 'bilbo'] cases = {} for level, baggins in enumerate(bagginses): cases[baggins] = factory.create_or_update_case( CaseStructure( case_id=baggins, attrs={ 'case_type': 'baggins', 'update': {'name': baggins}, 'create': True}, indices=[CaseIndex(CaseStructure(case_id=bagginses[level - 1]))] if level != 0 else None, walk_related=False, ) )[0] hierarchy = get_case_hierarchy(cases['bungo'], {}) self.assertEqual(4, len(hierarchy['case_list']))
class AutoCloseExtensionsTest(SyncBaseTest): def setUp(self): FormProcessorTestUtils.delete_all_cases() FormProcessorTestUtils.delete_all_xforms() self.domain = "domain" self.project = Domain(name=self.domain) self.user = User(user_id='user', username='******', password="******", date_joined=datetime(2011, 6, 9)) self.factory = CaseFactory() self.extension_ids = ['1', '2', '3'] self.host = CaseStructure(case_id='host') self.extension = CaseStructure( case_id=self.extension_ids[0], indices=[CaseIndex( related_structure=self.host, relationship="extension", )], ) self.extension_2 = CaseStructure( case_id=self.extension_ids[1], indices=[CaseIndex( related_structure=self.extension, relationship="extension", )], ) self.extension_3 = CaseStructure( case_id=self.extension_ids[2], indices=[CaseIndex( related_structure=self.extension_2, relationship="extension", )], ) def test_get_extension_chain_simple(self): self.factory.create_or_update_cases([self.extension]) self.assertEqual(set(self.extension_ids[0]), get_extension_chain([self.host], self.domain)) def test_get_extension_chain_multiple(self): self.factory.create_or_update_cases([self.extension_3]) self.assertEqual(set(self.extension_ids), get_extension_chain([self.host], self.domain)) @flag_enabled('EXTENSION_CASES_SYNC_ENABLED') def test_get_extension_to_close(self): """should return empty if case is not a host, otherwise should return full chain""" created_cases = self.factory.create_or_update_cases([self.extension_3]) # host open, should be empty no_cases = get_extensions_to_close(created_cases[-1], self.domain) self.assertEqual(set(), no_cases) created_cases[-1] = self.factory.create_or_update_case(CaseStructure( case_id=self.host.case_id, attrs={'close': True} ))[0] # host closed, should get full chain full_chain = get_extensions_to_close(created_cases[-1], self.domain) self.assertEqual(set(self.extension_ids), full_chain) # extension (not a host), should be empty no_cases = get_extensions_to_close(created_cases[2], self.domain) self.assertEqual(set(), no_cases) @flag_enabled('EXTENSION_CASES_SYNC_ENABLED') def test_close_cases(self): """Closing a host should close all the extensions""" self.factory.create_or_update_cases([self.extension_3]) self.assertFalse(CommCareCase.get(self.extension.case_id).closed) self.assertFalse(CommCareCase.get(self.extension_2.case_id).closed) self.assertFalse(CommCareCase.get(self.extension_3.case_id).closed) self.factory.create_or_update_case(CaseStructure( case_id=self.extension.case_id, attrs={'close': True} )) self.assertFalse(CommCareCase.get(self.host.case_id).closed) self.assertTrue(CommCareCase.get(self.extension.case_id).closed) self.assertFalse(CommCareCase.get(self.extension_2.case_id).closed) self.assertFalse(CommCareCase.get(self.extension_3.case_id).closed) self.factory.create_or_update_case(CaseStructure( case_id=self.host.case_id, attrs={'close': True} )) self.assertTrue(CommCareCase.get(self.host.case_id).closed) self.assertTrue(CommCareCase.get(self.extension.case_id).closed) self.assertTrue(CommCareCase.get(self.extension_2.case_id).closed) self.assertTrue(CommCareCase.get(self.extension_3.case_id).closed)
class TestExtensionCaseIds(TestCase): def setUp(self): super(TestExtensionCaseIds, self).setUp() self.domain = 'domain' FormProcessorTestUtils.delete_all_cases() self.factory = CaseFactory(self.domain) def tearDown(self): FormProcessorTestUtils.delete_all_cases() FormProcessorTestUtils.delete_all_xforms() super(TestExtensionCaseIds, self).tearDown() def test_no_extensions(self): """ Returns empty when there are other index types """ parent_id = uuid.uuid4().hex child_id = uuid.uuid4().hex parent = CaseStructure(case_id=parent_id, attrs={'create': True}) self.factory.create_or_update_case( CaseStructure( case_id=child_id, indices=[ CaseIndex(parent, relationship=CASE_INDEX_CHILD) ], attrs={'create': True} ) ) returned_cases = CaseAccessors(self.domain).get_extension_case_ids([parent_id]) self.assertEqual(returned_cases, []) def test_simple_extension_returned(self): """ Should return extension if it exists """ host_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex host = CaseStructure(case_id=host_id, attrs={'create': True}) self.factory.create_or_update_case( CaseStructure( case_id=extension_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION) ], attrs={'create': True} ) ) returned_cases = CaseAccessors(self.domain).get_extension_case_ids([host_id]) self.assertItemsEqual(returned_cases, [extension_id]) def test_extension_of_multiple_hosts_returned(self): """ Should return an extension from any host if there are multiple indices """ host_id = uuid.uuid4().hex host_2_id = uuid.uuid4().hex parent_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex host = CaseStructure(case_id=host_id, attrs={'create': True}) host_2 = CaseStructure(case_id=host_2_id, attrs={'create': True}) parent = CaseStructure(case_id=parent_id, attrs={'create': True}) self.factory.create_or_update_case( CaseStructure( case_id=extension_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION, identifier="host"), CaseIndex(host_2, relationship=CASE_INDEX_EXTENSION, identifier="host_2"), CaseIndex(parent, relationship=CASE_INDEX_CHILD), ], attrs={'create': True} ) ) returned_cases = CaseAccessors(self.domain).get_extension_case_ids([host_2_id]) self.assertItemsEqual(returned_cases, [extension_id]) returned_cases = CaseAccessors(self.domain).get_extension_case_ids([host_id]) self.assertItemsEqual(returned_cases, [extension_id]) def test_host_with_multiple_extensions(self): """ Return all extensions from a single host """ host_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex extension_2_id = uuid.uuid4().hex host = CaseStructure(case_id=host_id, attrs={'create': True}) self.factory.create_or_update_cases([ CaseStructure( case_id=extension_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION, identifier="host"), ], attrs={'create': True} ), CaseStructure( case_id=extension_2_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION, identifier="host"), ], attrs={'create': True} ), ] ) returned_cases = CaseAccessors(self.domain).get_extension_case_ids([host_id]) self.assertItemsEqual(returned_cases, [extension_id, extension_2_id]) def test_extensions_from_list(self): """ Given a list of hosts, should return all extensions """ host_id = uuid.uuid4().hex host_2_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex extension_2_id = uuid.uuid4().hex host = CaseStructure(case_id=host_id, attrs={'create': True}) host_2 = CaseStructure(case_id=host_2_id, attrs={'create': True}) self.factory.create_or_update_case( CaseStructure( case_id=extension_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION, identifier="host"), ], attrs={'create': True} ) ) self.factory.create_or_update_case( CaseStructure( case_id=extension_2_id, indices=[ CaseIndex(host_2, relationship=CASE_INDEX_EXTENSION, identifier="host"), ], attrs={'create': True} ) ) returned_cases = CaseAccessors(self.domain).get_extension_case_ids([host_id, host_2_id]) self.assertItemsEqual(returned_cases, [extension_id, extension_2_id])
class TestCasePropertyChanged(TestCase): def setUp(self): self.domain = "isildur" self.factory = CaseFactory(self.domain) self.case = self.factory.create_case(owner_id='owner', case_name="Aragorn", update={"sword": "Narsil"}) self.other_case = self.factory.create_case() def tearDown(self): delete_all_xforms() delete_all_cases() @run_with_all_backends def test_date_case_property_changed(self): updated_on = datetime(2015, 5, 3, 12, 11) # submit 2 updates self.factory.create_or_update_case( CaseStructure( self.case.case_id, attrs={ "update": { 'abc': "updated" }, "date_modified": updated_on }), ) self.factory.create_or_update_case( CaseStructure( self.case.case_id, attrs={ "update": { 'bcd': "updated" }, }), ) case = CaseAccessors(self.domain).get_case(self.case.case_id) self.assertEqual( updated_on.replace(tzinfo=pytz.UTC), get_datetime_case_property_changed(case, "abc", "updated") ) @run_with_all_backends def test_multiple_cases_in_update(self): day_1 = datetime(2015, 5, 1, 12, 11) day_2 = datetime(2015, 5, 2, 12, 11) # Submit two updates TOGETHER, one for this case, but irrelevant, # and one for another case, but touching the same property self.factory.create_or_update_cases([ CaseStructure( self.case.case_id, attrs={ "update": { 'not_relevant': "updated" }, "date_modified": day_1, }), CaseStructure( self.other_case.case_id, attrs={ "update": { 'relevant_property': "updated" }, "date_modified": day_1, }), ]) # Submit an update that DOES modify the relevant property self.factory.create_or_update_case( CaseStructure( self.case.case_id, attrs={ "update": { 'relevant_property': "updated" }, "date_modified": day_2, }), ) case = CaseAccessors(self.domain).get_case(self.case.case_id) self.assertEqual( day_2.replace(tzinfo=pytz.UTC), get_datetime_case_property_changed(case, "relevant_property", "updated") ) @run_with_all_backends def test_owner_id_changed(self): changes, _ = get_paged_changes_to_case_property(self.case, 'owner_id') self.assertEqual(len(changes), 1) self.assertEqual(changes[0].new_value, 'owner') self.factory.create_or_update_case( CaseStructure( self.case.case_id, attrs={ "update": { 'owner_id': 'new_owner' }, }), ) case = CaseAccessors(self.domain).get_case(self.case.case_id) changes, _ = get_paged_changes_to_case_property(case, 'owner_id') self.assertEqual(len(changes), 2) self.assertEqual(changes[0].new_value, 'new_owner') self.assertEqual(changes[1].new_value, 'owner') @run_with_all_backends def test_name_changed(self): self.factory.create_or_update_case( CaseStructure( self.case.case_id, attrs={ "update": { 'name': 'Strider' }, }), ) case = CaseAccessors(self.domain).get_case(self.case.case_id) changes, _ = get_paged_changes_to_case_property(case, 'name') self.assertEqual(len(changes), 2) self.assertEqual(changes[0].new_value, 'Strider') self.assertEqual(changes[1].new_value, 'Aragorn') @run_with_all_backends def test_blank_change(self): self.factory.create_or_update_case( CaseStructure( self.case.case_id, attrs={ "update": { 'sword': '' }, }), ) case = CaseAccessors(self.domain).get_case(self.case.case_id) changes, _ = get_paged_changes_to_case_property(case, 'sword') self.assertEqual(len(changes), 2) self.assertEqual(changes[0].new_value, '') self.assertEqual(changes[1].new_value, 'Narsil') @run_with_all_backends def test_case_rebuild(self): # Cases with rebuild actions were failing because rebuild actions have no form # https://manage.dimagi.com/default.asp?276216#1494409 self.factory.create_or_update_case( CaseStructure( self.case.case_id, attrs={ "update": { 'sword': '' }, }), ) rebuild_case_from_forms(self.domain, self.case.case_id, RebuildWithReason()) case = CaseAccessors(self.domain).get_case(self.case.case_id) changes, _ = get_paged_changes_to_case_property(case, 'sword') self.assertEqual(len(changes), 2) self.assertEqual(changes[0].new_value, '') self.assertEqual(changes[1].new_value, 'Narsil')
class OwnerCleanlinessTest(SyncBaseTest): def setUp(self): super(OwnerCleanlinessTest, self).setUp() self.owner_id = uuid.uuid4().hex self.synclog_id = uuid.uuid4().hex self.domain = uuid.uuid4().hex self.factory = CaseFactory( domain=self.domain, case_defaults={ 'create': True, 'owner_id': self.owner_id, 'user_id': self.owner_id, } ) self.assert_owner_clean() # this first call creates the OwnershipCleanliness doc self.sample_case = self.factory.create_case() self.child, self.parent = self.factory.create_or_update_case( CaseStructure( indices=[ CaseIndex(), ] ) ) self.extension, self.host = self.factory.create_or_update_case( CaseStructure( attrs={'owner_id': UNOWNED_EXTENSION_OWNER_ID}, indices=[ CaseIndex( relationship=CASE_INDEX_EXTENSION ), ] ) ) self.assert_owner_clean() # this is an actual assertion def _verify_set_cleanliness_flags(self, owner_id=None): """ Can be run at the end of any relevant test to check the current state of the OwnershipCleanliness object and verify that rebuilding it from scratch produces the same result """ if owner_id is None: owner_id = self.owner_id owner_cleanliness = self._owner_cleanliness_for_id(owner_id) is_clean = owner_cleanliness.is_clean hint = owner_cleanliness.hint owner_cleanliness.delete() set_cleanliness_flags(self.domain, owner_id, force_full=True) new_cleanliness = OwnershipCleanlinessFlag.objects.get(owner_id=owner_id) self.assertEqual(is_clean, new_cleanliness.is_clean) self.assertEqual(hint, new_cleanliness.hint) if hint: self.assertTrue(hint_still_valid(self.domain, hint)) @property def owner_cleanliness(self): return self._owner_cleanliness_for_id(self.owner_id) def _owner_cleanliness_for_id(self, owner_id): return OwnershipCleanlinessFlag.objects.get_or_create( owner_id=owner_id, domain=self.domain, defaults={'is_clean': True} )[0] def assert_owner_clean(self): self.assertTrue(self.owner_cleanliness.is_clean) def assert_owner_dirty(self): self.assertFalse(self.owner_cleanliness.is_clean) def assert_owner_temporarily_dirty(self): """ Changing any case's owner makes the previous owner ID temporarily dirty, to allow syncs to happen, but the should become clean on a rebuild. This checks that workflow and rebuilds the cleanliness flag. """ self.assertFalse(self.owner_cleanliness.is_clean) set_cleanliness_flags(self.domain, self.owner_id, force_full=True) self.assertTrue(self.owner_cleanliness.is_clean) def _set_owner(self, case_id, owner_id): case = self.factory.create_or_update_case( CaseStructure(case_id=case_id, attrs={'create': False, 'owner_id': owner_id}) )[0] self.assertEqual(owner_id, case.owner_id) @run_with_all_backends def test_add_normal_case_stays_clean(self): """Owned case with no indices remains clean""" self.factory.create_case() self.assert_owner_clean() self._verify_set_cleanliness_flags() @run_with_all_backends def test_change_owner_stays_clean(self): """change the owner ID of a normal case, should remain clean""" new_owner = uuid.uuid4().hex self._set_owner(self.sample_case.case_id, new_owner) self.assert_owner_temporarily_dirty() self._verify_set_cleanliness_flags() @run_with_all_backends def test_change_owner_child_case_stays_clean(self): """change the owner ID of a child case, should remain clean""" new_owner = uuid.uuid4().hex self._set_owner(self.child.case_id, new_owner) self.assert_owner_temporarily_dirty() self._verify_set_cleanliness_flags() @run_with_all_backends def test_add_clean_parent_stays_clean(self): """add a parent with the same owner, should remain clean""" self.factory.create_or_update_case(CaseStructure(indices=[CaseIndex()])) self.assert_owner_clean() self._verify_set_cleanliness_flags() @run_with_all_backends def test_create_dirty_makes_dirty(self): """create a case and a parent case with a different owner at the same time make sure the owner becomes dirty. """ new_owner = uuid.uuid4().hex [child, parent] = self.factory.create_or_update_case( CaseStructure( indices=[ CaseIndex(CaseStructure(attrs={'owner_id': new_owner})) ] ) ) self.assert_owner_dirty() self.assertEqual(child.case_id, self.owner_cleanliness.hint) self._verify_set_cleanliness_flags() @run_with_all_backends def test_add_dirty_parent_makes_dirty(self): """add parent with a different owner and make sure the owner becomes dirty""" new_owner = uuid.uuid4().hex [child, parent] = self.factory.create_or_update_case( CaseStructure( case_id=self.sample_case.case_id, indices=[ CaseIndex(CaseStructure(attrs={'owner_id': new_owner})) ] ) ) self.assert_owner_dirty() self.assertEqual(child.case_id, self.owner_cleanliness.hint) self._verify_set_cleanliness_flags() @run_with_all_backends def test_change_parent_owner_makes_dirty(self): """change the owner id of a parent case and make sure the owner becomes dirty""" new_owner = uuid.uuid4().hex self._set_owner(self.parent.case_id, new_owner) self.assert_owner_dirty() self.assertEqual(self.child.case_id, self.owner_cleanliness.hint) self._verify_set_cleanliness_flags() @run_with_all_backends def test_change_host_owner_remains_clean(self): """change owner for unowned extension, owner remains clean""" new_owner = uuid.uuid4().hex self._owner_cleanliness_for_id(new_owner) self._set_owner(self.host.case_id, new_owner) self.assert_owner_temporarily_dirty() self.assertTrue(self._owner_cleanliness_for_id(new_owner).is_clean) self._verify_set_cleanliness_flags() @run_with_all_backends def test_change_host_owner_makes_both_owners_dirty(self): """change owner for extension, both owners dirty""" new_owner = uuid.uuid4().hex self._owner_cleanliness_for_id(new_owner) self._set_owner(self.extension.case_id, new_owner) self.assert_owner_dirty() self.assertFalse(self._owner_cleanliness_for_id(new_owner).is_clean) @run_with_all_backends def test_set_flag_clean_no_data(self): unused_owner_id = uuid.uuid4().hex set_cleanliness_flags(self.domain, unused_owner_id) self.assertTrue(OwnershipCleanlinessFlag.objects.get(owner_id=unused_owner_id).is_clean) @run_with_all_backends def test_hint_invalidation(self): new_owner = uuid.uuid4().hex self._set_owner(self.parent.case_id, new_owner) self._set_owner(self.parent.case_id, self.owner_id) # after the submission the dirtiness flag should still be set # since it isn't invalidated right away self.assert_owner_dirty() # explicitly make sure the hint is no longer valid self.assertFalse(hint_still_valid(self.domain, self.owner_cleanliness.hint)) # reset the cleanliness flag and ensure it worked set_cleanliness_flags(self.domain, self.owner_id) self.assert_owner_clean() self.assertEqual(None, self.owner_cleanliness.hint) @run_with_all_backends def test_hint_invalidation_extensions(self): other_owner_id = uuid.uuid4().hex [extension, host] = self.factory.create_or_update_case( CaseStructure( case_id='extension', attrs={'owner_id': other_owner_id}, indices=[ CaseIndex( CaseStructure(case_id="host"), relationship=CASE_INDEX_EXTENSION ) ] ) ) self.assert_owner_dirty() self.assertTrue(hint_still_valid(self.domain, self.owner_cleanliness.hint)) self._set_owner(extension.case_id, UNOWNED_EXTENSION_OWNER_ID) self.assertFalse(hint_still_valid(self.domain, self.owner_cleanliness.hint)) @run_with_all_backends def test_hint_invalidation_extension_chain(self): other_owner_id = uuid.uuid4().hex self._owner_cleanliness_for_id(other_owner_id) host = CaseStructure(case_id=self.sample_case.case_id, attrs={'create': False}) extension_1 = CaseStructure( case_id="extension1", attrs={'owner_id': UNOWNED_EXTENSION_OWNER_ID}, indices=[ CaseIndex( host, relationship=CASE_INDEX_EXTENSION, ) ] ) extension_2 = CaseStructure( case_id="extension2", attrs={'owner_id': other_owner_id}, indices=[ CaseIndex( extension_1, relationship=CASE_INDEX_EXTENSION, ) ] ) self.factory.create_or_update_case(extension_2) self.assert_owner_dirty() self._set_owner(extension_2.case_id, UNOWNED_EXTENSION_OWNER_ID) self.assertFalse(hint_still_valid(self.domain, self.owner_cleanliness.hint)) @run_with_all_backends def test_cross_domain_on_submission(self): """create a form that makes a dirty owner with the same ID but in a different domain make sure the original owner stays clean""" new_domain = uuid.uuid4().hex # initialize the new cleanliness flag OwnershipCleanlinessFlag.objects.create(domain=new_domain, owner_id=self.owner_id, is_clean=True) self.factory.domain = new_domain self.factory.create_or_update_case( CaseStructure( indices=[ CaseIndex(CaseStructure(attrs={'owner_id': uuid.uuid4().hex})) ] ) ) self.assert_owner_clean() self.assertEqual( False, OwnershipCleanlinessFlag.objects.get(owner_id=self.owner_id, domain=new_domain).is_clean, ) @run_with_all_backends def test_cross_domain_both_clean(self): new_domain = uuid.uuid4().hex self.factory.domain = new_domain self.factory.create_or_update_case( CaseStructure( indices=[ CaseIndex(), ] ) ) # two clean ownership models in different domains should report clean self.assertTrue(get_cleanliness_flag_from_scratch(self.domain, self.owner_id).is_clean) self.assertTrue(get_cleanliness_flag_from_scratch(new_domain, self.owner_id).is_clean) @run_with_all_backends def test_cross_domain_dirty(self): new_domain = uuid.uuid4().hex new_owner = uuid.uuid4().hex self.factory.domain = new_domain self.factory.create_or_update_case( CaseStructure( indices=[ CaseIndex(CaseStructure(attrs={'owner_id': new_owner})), ] ) ) # original domain should stay clean but the new one should be dirty self.assertTrue(get_cleanliness_flag_from_scratch(self.domain, self.owner_id).is_clean) self.assertFalse(get_cleanliness_flag_from_scratch(new_domain, self.owner_id).is_clean) @run_with_all_backends def test_non_existent_parent(self): self.factory.create_or_update_case( CaseStructure( indices=[ CaseIndex(CaseStructure()), ], walk_related=False, ) ) self.assertTrue(get_cleanliness_flag_from_scratch(self.domain, self.owner_id).is_clean) @override_settings(TESTS_SHOULD_TRACK_CLEANLINESS=False) @run_with_all_backends def test_autocreate_flag_off(self): new_owner = uuid.uuid4().hex self.factory.create_or_update_case( CaseStructure(case_id=uuid.uuid4().hex, attrs={'create': True, 'owner_id': new_owner}) )[0] self.assertFalse(OwnershipCleanlinessFlag.objects.filter(domain=self.domain, owner_id=new_owner).exists()) @override_settings(TESTS_SHOULD_TRACK_CLEANLINESS=True) @run_with_all_backends def test_autocreate_flag_on(self): new_owner = uuid.uuid4().hex self.factory.create_or_update_case( CaseStructure(case_id=uuid.uuid4().hex, attrs={'create': True, 'owner_id': new_owner}) )[0] flag = OwnershipCleanlinessFlag.objects.get(domain=self.domain, owner_id=new_owner) self.assertEqual(True, flag.is_clean) @run_with_all_backends def test_simple_unowned_extension(self): """Simple unowned extensions should be clean""" self.factory.create_or_update_case( CaseStructure( case_id=uuid.uuid4().hex, attrs={'owner_id': UNOWNED_EXTENSION_OWNER_ID}, indices=[ CaseIndex( CaseStructure(), relationship=CASE_INDEX_EXTENSION ) ] ) ) self.assert_owner_clean() self._verify_set_cleanliness_flags() @run_with_all_backends def test_owned_extension(self): """Extension owned by another owner should be dirty""" other_owner_id = uuid.uuid4().hex self._owner_cleanliness_for_id(other_owner_id) [extension, host] = self.factory.create_or_update_case( CaseStructure( case_id='extension', attrs={'owner_id': other_owner_id}, indices=[ CaseIndex( CaseStructure(case_id="host"), relationship=CASE_INDEX_EXTENSION ) ] ) ) self.assert_owner_dirty() self.assertFalse(self._owner_cleanliness_for_id(other_owner_id).is_clean) self.assertEqual(host.case_id, self.owner_cleanliness.hint) self.assertEqual(extension.case_id, self._owner_cleanliness_for_id(other_owner_id).hint) self._verify_set_cleanliness_flags(self.owner_id) self._verify_set_cleanliness_flags(other_owner_id) @run_with_all_backends def test_extension_chain_with_other_owner_makes_dirty(self): """An extension chain of unowned extensions that ends at a case owned by a different owner is dirty""" other_owner_id = uuid.uuid4().hex self._owner_cleanliness_for_id(other_owner_id) host = CaseStructure(case_id=self.sample_case.case_id, attrs={'create': False}) extension_1 = CaseStructure( case_id="extension1", attrs={'owner_id': UNOWNED_EXTENSION_OWNER_ID}, indices=[ CaseIndex( host, relationship=CASE_INDEX_EXTENSION, ) ] ) extension_2 = CaseStructure( case_id="extension2", attrs={'owner_id': other_owner_id}, indices=[ CaseIndex( extension_1, relationship=CASE_INDEX_EXTENSION, ) ] ) self.factory.create_or_update_case(extension_2) self.assert_owner_dirty() self.assertFalse(self._owner_cleanliness_for_id(other_owner_id).is_clean) self.assertEqual(host.case_id, self.owner_cleanliness.hint) self.assertEqual(extension_2.case_id, self._owner_cleanliness_for_id(other_owner_id).hint) self._verify_set_cleanliness_flags(self.owner_id) self._verify_set_cleanliness_flags(other_owner_id) @run_with_all_backends def test_multiple_indices_multiple_owners(self): """Extension that indexes a case with another owner should make all owners dirty""" other_owner_id = uuid.uuid4().hex self._owner_cleanliness_for_id(other_owner_id) host_1 = CaseStructure() host_2 = CaseStructure(attrs={'owner_id': other_owner_id}) self.factory.create_or_update_case( CaseStructure( case_id=self.sample_case.case_id, attrs={'owner_id': UNOWNED_EXTENSION_OWNER_ID}, indices=[ CaseIndex( host_1, relationship=CASE_INDEX_EXTENSION, identifier="host_1", ), CaseIndex( host_2, relationship=CASE_INDEX_EXTENSION, identifier="host_2", ) ] ) ) self.assert_owner_dirty() self.assertFalse(self._owner_cleanliness_for_id(other_owner_id).is_clean) self.assertEqual(host_1.case_id, self.owner_cleanliness.hint) self.assertEqual(host_2.case_id, self._owner_cleanliness_for_id(other_owner_id).hint) self._verify_set_cleanliness_flags(self.owner_id) self._verify_set_cleanliness_flags(other_owner_id) @run_with_all_backends def test_long_extension_chain_with_branches(self): """An extension chain of unowned extensions that ends at an owned case is dirty""" owner_1 = uuid.uuid4().hex self._owner_cleanliness_for_id(owner_1) owner_2 = uuid.uuid4().hex self._owner_cleanliness_for_id(owner_2) host = CaseStructure(case_id=self.sample_case.case_id, attrs={'create': False}) host_2 = CaseStructure( case_id="host_with_other_owner", attrs={'owner_id': owner_1} ) extension_1 = CaseStructure( case_id="extension1", attrs={'owner_id': UNOWNED_EXTENSION_OWNER_ID}, indices=[ CaseIndex( host, relationship=CASE_INDEX_EXTENSION, identifier="host_1", ), CaseIndex( host_2, relationship=CASE_INDEX_EXTENSION, identifier="host_2", ) ] ) extension_2 = CaseStructure( case_id="extension2", attrs={'owner_id': owner_2}, indices=[ CaseIndex( extension_1, relationship=CASE_INDEX_EXTENSION, ) ] ) self.factory.create_or_update_case(extension_2) self.assert_owner_dirty() self.assertFalse(self._owner_cleanliness_for_id(owner_1).is_clean) self.assertFalse(self._owner_cleanliness_for_id(owner_2).is_clean) self.assertEqual(host.case_id, self.owner_cleanliness.hint) self._verify_set_cleanliness_flags()
class GetDependentCasesTest(TestCase): @classmethod def setUpClass(cls): super(GetDependentCasesTest, cls).setUpClass() delete_all_cases() def setUp(self): super(GetDependentCasesTest, self).setUp() self.domain = 'domain' self.owner_id = uuid.uuid4().hex self.other_owner_id = uuid.uuid4().hex self.factory = CaseFactory(self.domain) @run_with_all_backends def test_returns_nothing_with_no_dependencies(self): case = CaseStructure() self.factory.create_or_update_case(case) self.assertEqual(set(), get_dependent_case_info(self.domain, [case.case_id]).all_ids) @run_with_all_backends def test_returns_simple_extension(self): host = CaseStructure( case_id=uuid.uuid4().hex, attrs={'owner_id': self.owner_id} ) extension = CaseStructure( case_id=uuid.uuid4().hex, attrs={'owner_id': self.other_owner_id}, indices=[CaseIndex(host, relationship=CASE_INDEX_EXTENSION)] ) all_ids = set([host.case_id, extension.case_id]) self.factory.create_or_update_cases([host, extension]) self.assertEqual(all_ids, get_dependent_case_info(self.domain, [host.case_id]).all_ids) self.assertEqual(all_ids, get_dependent_case_info(self.domain, [extension.case_id]).all_ids) self.assertEqual(set([extension.case_id]), get_dependent_case_info(self.domain, [host.case_id]).extension_ids) @run_with_all_backends def test_returns_extension_of_extension(self): host = CaseStructure( case_id=uuid.uuid4().hex, attrs={'owner_id': self.owner_id} ) extension = CaseStructure( case_id=uuid.uuid4().hex, attrs={'owner_id': self.other_owner_id}, indices=[CaseIndex(host, relationship=CASE_INDEX_EXTENSION)] ) extension_2 = CaseStructure( case_id=uuid.uuid4().hex, indices=[CaseIndex(extension, relationship=CASE_INDEX_EXTENSION)] ) all_ids = set([host.case_id, extension.case_id, extension_2.case_id]) self.factory.create_or_update_cases([extension_2]) self.assertEqual(all_ids, get_dependent_case_info(self.domain, [host.case_id]).all_ids) self.assertEqual(all_ids, get_dependent_case_info(self.domain, [extension.case_id]).all_ids) self.assertEqual(all_ids, get_dependent_case_info(self.domain, [extension_2.case_id]).all_ids) self.assertEqual(set([extension.case_id, extension_2.case_id]), get_dependent_case_info(self.domain, [host.case_id]).extension_ids) @run_with_all_backends def test_children_and_extensions(self): parent = CaseStructure( case_id=uuid.uuid4().hex, attrs={'owner_id': self.other_owner_id, 'close': True} ) child = CaseStructure( case_id=uuid.uuid4().hex, attrs={'owner_id': self.owner_id}, indices=[CaseIndex(parent)] ) extension = CaseStructure( case_id=uuid.uuid4().hex, attrs={'owner_id': self.other_owner_id}, indices=[CaseIndex(child, relationship=CASE_INDEX_EXTENSION)] ) self.factory.create_or_update_cases([parent, child, extension]) all_ids = set([parent.case_id, child.case_id, extension.case_id]) self.assertEqual(all_ids, get_dependent_case_info(self.domain, [child.case_id]).all_ids) self.assertEqual(set([]), get_dependent_case_info(self.domain, [parent.case_id]).all_ids) self.assertEqual(set([extension.case_id]), get_dependent_case_info(self.domain, [child.case_id]).extension_ids) self.assertEqual(set([]), get_dependent_case_info(self.domain, [parent.case_id]).extension_ids)
class AutoCloseExtensionsTest(TestCase): def setUp(self): FormProcessorTestUtils.delete_all_cases() FormProcessorTestUtils.delete_all_xforms() delete_all_users() self.domain = "domain" self.project = Domain(name=self.domain) self.user = create_restore_user(self.domain, username='******', password="******") self.factory = CaseFactory(domain=self.domain) self.extension_ids = ['1', '2', '3'] self.host_id = 'host' def tearDown(self): FormProcessorTestUtils.delete_all_cases() FormProcessorTestUtils.delete_all_xforms() delete_all_users() def _create_extension_chain(self): host = CaseStructure(case_id=self.host_id) extension = CaseStructure( case_id=self.extension_ids[0], indices=[CaseIndex( related_structure=host, relationship="extension", )], ) extension_2 = CaseStructure( case_id=self.extension_ids[1], indices=[CaseIndex( related_structure=extension, relationship="extension", )], ) extension_3 = CaseStructure( case_id=self.extension_ids[2], indices=[CaseIndex( related_structure=extension_2, relationship="extension", )], ) return self.factory.create_or_update_cases([extension_3]) def _create_extension_loop(self): extension_3 = CaseStructure(case_id=self.extension_ids[2]) host = CaseStructure( case_id=self.host_id, indices=[CaseIndex( related_structure=extension_3, relationship="extension", )], ) return self.factory.create_or_update_cases([host]) def _create_host_is_subcase_chain(self): parent = CaseStructure(case_id='parent') host = CaseStructure( case_id=self.host_id, indices=[CaseIndex( related_structure=parent, relationship="child", )], ) extension = CaseStructure( case_id=self.extension_ids[0], indices=[CaseIndex( related_structure=host, relationship="extension", )], ) extension_2 = CaseStructure( case_id=self.extension_ids[1], indices=[CaseIndex( related_structure=extension, relationship="extension", )], ) return self.factory.create_or_update_cases([extension_2]) @run_with_all_backends def test_get_extension_chain_simple(self): host = CaseStructure(case_id=self.host_id) extension = CaseStructure( case_id=self.extension_ids[0], indices=[CaseIndex( related_structure=host, relationship="extension", )], ) self.factory.create_or_update_cases([extension]) self.assertEqual( set(self.extension_ids[0]), CaseAccessors(self.domain).get_extension_chain([self.host_id]) ) @run_with_all_backends def test_get_extension_chain_multiple(self): created_cases = self._create_extension_chain() self.assertEqual( set(self.extension_ids), CaseAccessors(self.domain).get_extension_chain([created_cases[-1].case_id]) ) @run_with_all_backends def test_get_extension_chain_circular_ref(self): """If there is a circular reference, this should not hang forever """ self._create_extension_chain() self._create_extension_loop() self.assertEqual( set([self.host_id] + self.extension_ids), CaseAccessors(self.domain).get_extension_chain([self.extension_ids[2]]) ) @flag_enabled('EXTENSION_CASES_SYNC_ENABLED') @run_with_all_backends def test_get_extension_to_close(self): """should return empty if case is not a host, otherwise should return full chain""" created_cases = self._create_extension_chain() # host open, should be empty no_cases = get_extensions_to_close(created_cases[-1], self.domain) self.assertEqual(set(), no_cases) created_cases[-1] = self.factory.create_or_update_case(CaseStructure( case_id=self.host_id, attrs={'close': True} ))[0] # host closed, should get full chain full_chain = get_extensions_to_close(created_cases[-1], self.domain) self.assertEqual(set(self.extension_ids), full_chain) # extension (not a host), should be empty no_cases = get_extensions_to_close(created_cases[2], self.domain) self.assertEqual(set(), no_cases) @flag_enabled('EXTENSION_CASES_SYNC_ENABLED') @run_with_all_backends def test_get_extension_to_close_child_host(self): """should still return extension chain if outgoing index is a child index""" created_cases = self._create_host_is_subcase_chain() # host open, should be empty no_cases = get_extensions_to_close(created_cases[-1], self.domain) self.assertEqual(set(), no_cases) # close parent, shouldn't get extensions created_cases[-1] = self.factory.create_or_update_case(CaseStructure( case_id='parent', attrs={'close': True} ))[0] no_cases = get_extensions_to_close(created_cases[-1], self.domain) self.assertEqual(set(), no_cases) # close host that is also a child created_cases[-2] = self.factory.create_or_update_case(CaseStructure( case_id=self.host_id, attrs={'close': True} ))[0] full_chain = get_extensions_to_close(created_cases[-2], self.domain) self.assertEqual(set(self.extension_ids[0:2]), full_chain) @flag_enabled('EXTENSION_CASES_SYNC_ENABLED') @run_with_all_backends def test_close_cases_host(self): """Closing a host should close all the extensions""" self._create_extension_chain() cases = CaseAccessors(self.domain).get_cases(self.extension_ids) self.assertFalse(cases[0].closed) self.assertFalse(cases[1].closed) self.assertFalse(cases[2].closed) self.factory.create_or_update_case(CaseStructure( case_id=self.extension_ids[0], attrs={'close': True} )) cases = { case.case_id: case.closed for case in CaseAccessors(self.domain).get_cases([self.host_id] + self.extension_ids) } self.assertFalse(cases[self.host_id]) self.assertTrue(cases[self.extension_ids[0]]) self.assertFalse(cases[self.extension_ids[1]]) self.assertFalse(cases[self.extension_ids[2]]) self.factory.create_or_update_case(CaseStructure( case_id=self.host_id, attrs={'close': True} )) cases = { case.case_id: case.closed for case in CaseAccessors(self.domain).get_cases([self.host_id] + self.extension_ids) } self.assertTrue(cases[self.host_id]) self.assertTrue(cases[self.extension_ids[0]]) self.assertTrue(cases[self.extension_ids[1]]) self.assertTrue(cases[self.extension_ids[2]]) @flag_enabled('EXTENSION_CASES_SYNC_ENABLED') @run_with_all_backends def test_close_cases_child(self): """Closing a host that is also a child should close all the extensions""" self._create_host_is_subcase_chain() cases = { case.case_id: case.closed for case in CaseAccessors(self.domain).get_cases([self.host_id] + self.extension_ids) } self.assertFalse(cases[self.host_id]) self.assertFalse(cases[self.extension_ids[0]]) self.assertFalse(cases[self.extension_ids[1]]) self.factory.create_or_update_case(CaseStructure( case_id=self.host_id, attrs={'close': True} )) cases = { case.case_id: case.closed for case in CaseAccessors(self.domain).get_cases(['parent', self.host_id] + self.extension_ids) } self.assertFalse(cases['parent']) self.assertTrue(cases[self.host_id]) self.assertTrue(cases[self.extension_ids[0]]) self.assertTrue(cases[self.extension_ids[1]])
class ImporterTest(TestCase): def setUp(self): super(ImporterTest, self).setUp() self.domain_obj = create_domain("importer-test") self.domain = self.domain_obj.name self.default_case_type = 'importer-test-casetype' self.couch_user = WebUser.create(None, "test", "foobar") self.couch_user.add_domain_membership(self.domain, is_admin=True) self.couch_user.save() self.accessor = CaseAccessors(self.domain) self.factory = CaseFactory(domain=self.domain, case_defaults={ 'case_type': self.default_case_type, }) delete_all_cases() def tearDown(self): self.couch_user.delete() self.domain_obj.delete() super(ImporterTest, self).tearDown() def _config(self, col_names, search_column=None, case_type=None, search_field='case_id', create_new_cases=True): return ImporterConfig( couch_user_id=self.couch_user._id, case_type=case_type or self.default_case_type, excel_fields=col_names, case_fields=[''] * len(col_names), custom_fields=col_names, search_column=search_column or col_names[0], search_field=search_field, create_new_cases=create_new_cases, ) @run_with_all_backends @patch('corehq.apps.case_importer.tasks.bulk_import_async.update_state') def testImportNone(self, update_state): res = bulk_import_async.delay(self._config(['anything']), self.domain, None) self.assertIsInstance(res.result, Ignore) update_state.assert_called_with( state=states.FAILURE, meta={'errors': 'Sorry, your session has expired. Please start over and try again.'}) self.assertEqual(0, len(get_case_ids_in_domain(self.domain))) @run_with_all_backends def testImportBasic(self): config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], ['case_id-0', 'age-0', 'sex-0', 'location-0'], ['case_id-1', 'age-1', 'sex-1', 'location-1'], ['case_id-2', 'age-2', 'sex-2', 'location-2'], ['case_id-3', 'age-3', 'sex-3', 'location-3'], ['case_id-4', 'age-4', 'sex-4', 'location-4'], ) res = do_import(file, config, self.domain) self.assertEqual(5, res['created_count']) self.assertEqual(0, res['match_count']) self.assertFalse(res['errors']) self.assertEqual(1, res['num_chunks']) case_ids = self.accessor.get_case_ids_in_domain() cases = list(self.accessor.get_cases(case_ids)) self.assertEqual(5, len(cases)) properties_seen = set() for case in cases: self.assertEqual(self.couch_user._id, case.user_id) self.assertEqual(self.couch_user._id, case.owner_id) self.assertEqual(self.default_case_type, case.type) for prop in ['age', 'sex', 'location']: self.assertTrue(prop in case.get_case_property(prop)) self.assertFalse(case.get_case_property(prop) in properties_seen) properties_seen.add(case.get_case_property(prop)) @run_with_all_backends def testImportNamedColumns(self): config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], ['case_id-0', 'age-0', 'sex-0', 'location-0'], ['case_id-1', 'age-1', 'sex-1', 'location-1'], ['case_id-2', 'age-2', 'sex-2', 'location-2'], ['case_id-3', 'age-3', 'sex-3', 'location-3'], ) res = do_import(file, config, self.domain) self.assertEqual(4, res['created_count']) self.assertEqual(4, len(self.accessor.get_case_ids_in_domain())) @run_with_all_backends def testImportTrailingWhitespace(self): cols = ['case_id', 'age', 'sex\xa0', 'location'] config = self._config(cols) file = make_worksheet_wrapper( ['case_id', 'age', 'sex\xa0', 'location'], ['case_id-0', 'age-0', 'sex\xa0-0', 'location-0'], ) res = do_import(file, config, self.domain) self.assertEqual(1, res['created_count']) case_ids = self.accessor.get_case_ids_in_domain() self.assertEqual(1, len(case_ids)) case = self.accessor.get_case(case_ids[0]) self.assertTrue(bool(case.get_case_property('sex'))) # make sure the value also got properly set @run_with_all_backends def testCaseIdMatching(self): # bootstrap a stub case [case] = self.factory.create_or_update_case(CaseStructure(attrs={ 'create': True, 'update': {'importer_test_prop': 'foo'}, })) self.assertEqual(1, len(self.accessor.get_case_ids_in_domain())) config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], [case.case_id, 'age-0', 'sex-0', 'location-0'], [case.case_id, 'age-1', 'sex-1', 'location-1'], [case.case_id, 'age-2', 'sex-2', 'location-2'], ) res = do_import(file, config, self.domain) self.assertEqual(0, res['created_count']) self.assertEqual(3, res['match_count']) self.assertFalse(res['errors']) # shouldn't create any more cases, just the one case_ids = self.accessor.get_case_ids_in_domain() self.assertEqual(1, len(case_ids)) [case] = self.accessor.get_cases(case_ids) for prop in ['age', 'sex', 'location']: self.assertTrue(prop in case.get_case_property(prop)) # shouldn't touch existing properties self.assertEqual('foo', case.get_case_property('importer_test_prop')) @run_with_all_backends def testCaseLookupTypeCheck(self): [case] = self.factory.create_or_update_case(CaseStructure(attrs={ 'create': True, 'case_type': 'nonmatch-type', })) self.assertEqual(1, len(self.accessor.get_case_ids_in_domain())) config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], [case.case_id, 'age-0', 'sex-0', 'location-0'], [case.case_id, 'age-1', 'sex-1', 'location-1'], [case.case_id, 'age-2', 'sex-2', 'location-2'], ) res = do_import(file, config, self.domain) # because the type is wrong these shouldn't match self.assertEqual(3, res['created_count']) self.assertEqual(0, res['match_count']) self.assertEqual(4, len(self.accessor.get_case_ids_in_domain())) @run_with_all_backends def testCaseLookupDomainCheck(self): self.factory.domain = 'wrong-domain' [case] = self.factory.create_or_update_case(CaseStructure(attrs={ 'create': True, })) self.assertEqual(0, len(self.accessor.get_case_ids_in_domain())) config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], [case.case_id, 'age-0', 'sex-0', 'location-0'], [case.case_id, 'age-1', 'sex-1', 'location-1'], [case.case_id, 'age-2', 'sex-2', 'location-2'], ) res = do_import(file, config, self.domain) # because the domain is wrong these shouldn't match self.assertEqual(3, res['created_count']) self.assertEqual(0, res['match_count']) self.assertEqual(3, len(self.accessor.get_case_ids_in_domain())) @run_with_all_backends def testExternalIdMatching(self): # bootstrap a stub case external_id = 'importer-test-external-id' [case] = self.factory.create_or_update_case(CaseStructure( attrs={ 'create': True, 'external_id': external_id, } )) self.assertEqual(1, len(self.accessor.get_case_ids_in_domain())) headers = ['external_id', 'age', 'sex', 'location'] config = self._config(headers, search_field='external_id') file = make_worksheet_wrapper( ['external_id', 'age', 'sex', 'location'], ['importer-test-external-id', 'age-0', 'sex-0', 'location-0'], ['importer-test-external-id', 'age-1', 'sex-1', 'location-1'], ['importer-test-external-id', 'age-2', 'sex-2', 'location-2'], ) res = do_import(file, config, self.domain) self.assertEqual(0, res['created_count']) self.assertEqual(3, res['match_count']) self.assertFalse(res['errors']) # shouldn't create any more cases, just the one self.assertEqual(1, len(self.accessor.get_case_ids_in_domain())) @run_with_all_backends def test_external_id_matching_on_create_with_custom_column_name(self): headers = ['id_column', 'age', 'sex', 'location'] external_id = 'external-id-test' config = self._config(headers[1:], search_column='id_column', search_field='external_id') file = make_worksheet_wrapper( ['id_column', 'age', 'sex', 'location'], ['external-id-test', 'age-0', 'sex-0', 'location-0'], ['external-id-test', 'age-1', 'sex-1', 'location-1'], ) res = do_import(file, config, self.domain) self.assertFalse(res['errors']) self.assertEqual(1, res['created_count']) self.assertEqual(1, res['match_count']) case_ids = self.accessor.get_case_ids_in_domain() self.assertEqual(1, len(case_ids)) case = self.accessor.get_case(case_ids[0]) self.assertEqual(external_id, case.external_id) def testNoCreateNew(self): config = self._config(['case_id', 'age', 'sex', 'location'], create_new_cases=False) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], ['case_id-0', 'age-0', 'sex-0', 'location-0'], ['case_id-1', 'age-1', 'sex-1', 'location-1'], ['case_id-2', 'age-2', 'sex-2', 'location-2'], ['case_id-3', 'age-3', 'sex-3', 'location-3'], ['case_id-4', 'age-4', 'sex-4', 'location-4'], ) res = do_import(file, config, self.domain) # no matching and no create new set - should do nothing self.assertEqual(0, res['created_count']) self.assertEqual(0, res['match_count']) self.assertEqual(0, len(get_case_ids_in_domain(self.domain))) def testBlankRows(self): # don't create new cases for rows left blank config = self._config(['case_id', 'age', 'sex', 'location'], create_new_cases=True) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], [None, None, None, None], ['', '', '', ''], ) res = do_import(file, config, self.domain) # no matching and no create new set - should do nothing self.assertEqual(0, res['created_count']) self.assertEqual(0, res['match_count']) self.assertEqual(0, len(get_case_ids_in_domain(self.domain))) def testBasicChunking(self): config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], ['case_id-0', 'age-0', 'sex-0', 'location-0'], ['case_id-1', 'age-1', 'sex-1', 'location-1'], ['case_id-2', 'age-2', 'sex-2', 'location-2'], ['case_id-3', 'age-3', 'sex-3', 'location-3'], ['case_id-4', 'age-4', 'sex-4', 'location-4'], ) res = do_import(file, config, self.domain, chunksize=2) # 5 cases in chunks of 2 = 3 chunks self.assertEqual(3, res['num_chunks']) self.assertEqual(5, res['created_count']) self.assertEqual(5, len(get_case_ids_in_domain(self.domain))) @run_with_all_backends def testExternalIdChunking(self): # bootstrap a stub case external_id = 'importer-test-external-id' headers = ['external_id', 'age', 'sex', 'location'] config = self._config(headers, search_field='external_id') file = make_worksheet_wrapper( ['external_id', 'age', 'sex', 'location'], ['importer-test-external-id', 'age-0', 'sex-0', 'location-0'], ['importer-test-external-id', 'age-1', 'sex-1', 'location-1'], ['importer-test-external-id', 'age-2', 'sex-2', 'location-2'], ) # the first one should create the case, and the remaining two should update it res = do_import(file, config, self.domain) self.assertEqual(1, res['created_count']) self.assertEqual(2, res['match_count']) self.assertFalse(res['errors']) self.assertEqual(2, res['num_chunks']) # the lookup causes an extra chunk # should just create the one case case_ids = self.accessor.get_case_ids_in_domain() self.assertEqual(1, len(case_ids)) [case] = self.accessor.get_cases(case_ids) self.assertEqual(external_id, case.external_id) for prop in ['age', 'sex', 'location']: self.assertTrue(prop in case.get_case_property(prop)) @run_with_all_backends def testParentCase(self): headers = ['parent_id', 'name', 'case_id'] config = self._config(headers, create_new_cases=True, search_column='case_id') rows = 3 [parent_case] = self.factory.create_or_update_case(CaseStructure(attrs={'create': True})) self.assertEqual(1, len(self.accessor.get_case_ids_in_domain())) file = make_worksheet_wrapper( ['parent_id', 'name', 'case_id'], [parent_case.case_id, 'name-0', 'case_id-0'], [parent_case.case_id, 'name-1', 'case_id-1'], [parent_case.case_id, 'name-2', 'case_id-2'], ) file_missing = make_worksheet_wrapper( ['parent_id', 'name', 'case_id'], ['parent_id-0', 'name-0', 'case_id-0'], ['parent_id-1', 'name-1', 'case_id-1'], ['parent_id-2', 'name-2', 'case_id-2'], ) # Should successfully match on `rows` cases res = do_import(file, config, self.domain) self.assertEqual(rows, res['created_count']) # Should be unable to find parent case on `rows` cases res = do_import(file_missing, config, self.domain) error_column_name = 'parent_id' self.assertEqual(rows, len(res['errors'][ImportErrors.InvalidParentId][error_column_name]['rows']), "All cases should have missing parent") def import_mock_file(self, rows): config = self._config(rows[0]) xls_file = make_worksheet_wrapper(*rows) return do_import(xls_file, config, self.domain) @run_with_all_backends def testLocationOwner(self): # This is actually testing several different things, but I figure it's # worth it, as each of these tests takes a non-trivial amount of time. non_case_sharing = LocationType.objects.create( domain=self.domain, name='lt1', shares_cases=False ) case_sharing = LocationType.objects.create( domain=self.domain, name='lt2', shares_cases=True ) location = make_loc('loc-1', 'Loc 1', self.domain, case_sharing.code) make_loc('loc-2', 'Loc 2', self.domain, case_sharing.code) duplicate_loc = make_loc('loc-3', 'Loc 2', self.domain, case_sharing.code) improper_loc = make_loc('loc-4', 'Loc 4', self.domain, non_case_sharing.code) res = self.import_mock_file([ ['case_id', 'name', 'owner_id', 'owner_name'], ['', 'location-owner-id', location.group_id, ''], ['', 'location-owner-code', '', location.site_code], ['', 'location-owner-name', '', location.name], ['', 'duplicate-location-name', '', duplicate_loc.name], ['', 'non-case-owning-name', '', improper_loc.name], ]) case_ids = self.accessor.get_case_ids_in_domain() cases = {c.name: c for c in list(self.accessor.get_cases(case_ids))} self.assertEqual(cases['location-owner-id'].owner_id, location.group_id) self.assertEqual(cases['location-owner-code'].owner_id, location.group_id) self.assertEqual(cases['location-owner-name'].owner_id, location.group_id) error_message = ImportErrors.DuplicateLocationName error_column_name = None self.assertIn(error_message, res['errors']) self.assertEqual(res['errors'][error_message][error_column_name]['rows'], [5]) error_message = ImportErrors.InvalidOwnerId self.assertIn(error_message, res['errors']) error_column_name = 'owner_id' self.assertEqual(res['errors'][error_message][error_column_name]['rows'], [6]) @run_with_all_backends def test_opened_on(self): case = self.factory.create_case() new_date = '2015-04-30T14:41:53.000000Z' with flag_enabled('BULK_UPLOAD_DATE_OPENED'): self.import_mock_file([ ['case_id', 'date_opened'], [case.case_id, new_date] ]) case = CaseAccessors(self.domain).get_case(case.case_id) self.assertEqual(case.opened_on, PhoneTime(parse_datetime(new_date)).done()) @run_with_all_backends def test_columns_and_rows_align(self): case_owner = CommCareUser.create(self.domain, 'username', 'pw') res = self.import_mock_file([ ['case_id', 'name', '', 'favorite_color', 'owner_id'], ['', 'Jeff', '', 'blue', case_owner._id], ['', 'Caroline', '', 'yellow', case_owner._id], ]) self.assertEqual(res['errors'], {}) case_ids = self.accessor.get_case_ids_in_domain() cases = {c.name: c for c in list(self.accessor.get_cases(case_ids))} self.assertEqual(cases['Jeff'].owner_id, case_owner._id) self.assertEqual(cases['Jeff'].get_case_property('favorite_color'), 'blue') self.assertEqual(cases['Caroline'].owner_id, case_owner._id) self.assertEqual(cases['Caroline'].get_case_property('favorite_color'), 'yellow')
class ENikshayCaseStructureMixin(object): def setUp(self): super(ENikshayCaseStructureMixin, self).setUp() delete_all_users() self.domain = getattr(self, 'domain', 'fake-domain-from-mixin') self.factory = CaseFactory(domain=self.domain) self.username = "******" self.password = "******" self.user = CommCareUser.create( self.domain, username=self.username, password=self.password, first_name="Jon", last_name="Snow", ) self.person_id = u"person" self.occurrence_id = u"occurrence" self.episode_id = u"episode" self.test_id = u"test" self.lab_referral_id = u"lab_referral" self.prescription_id = "prescription_id" self._prescription_created = False self.primary_phone_number = "0123456789" self.secondary_phone_number = "0999999999" self.treatment_supporter_phone = "066000666" self.other_number = "0123456666" self._episode = None self._person = None def tearDown(self): delete_all_users() super(ENikshayCaseStructureMixin, self).tearDown() @property def person(self): if not self._person: self._person = get_person_case_structure( self.person_id, self.user.user_id, extra_update={ PRIMARY_PHONE_NUMBER: self.primary_phone_number, BACKUP_PHONE_NUMBER: self.secondary_phone_number, ENROLLED_IN_PRIVATE: 'false', }) return self._person @property def occurrence(self): return get_occurrence_case_structure(self.occurrence_id, self.person) @property def episode(self): if not self._episode: self._episode = get_episode_case_structure( self.episode_id, self.occurrence, extra_update={ OTHER_NUMBER: self.other_number, TREATMENT_SUPPORTER_PHONE: self.treatment_supporter_phone, WEIGHT_BAND: "adult_55-69" }) return self._episode def create_case(self, case): return self.factory.create_or_update_cases([case]) def create_case_structure(self): return { case.case_id: case for case in self.factory.create_or_update_cases([self.episode]) } @property def test(self): return CaseStructure( case_id=self.test_id, attrs={ 'create': True, 'case_type': 'test', "update": dict( date_reported=datetime(2016, 8, 6).date(), lab_serial_number=19, test_type_value="microscopy-zn", purpose_of_testing="diagnostic", result_grade="1plus", testing_facility_id=self.dmc.get_id, ) }, indices=[ CaseIndex( self.occurrence, identifier='host', relationship=CASE_INDEX_EXTENSION, related_type=self.occurrence.attrs['case_type'], ) ], ) @property def lab_referral(self): return CaseStructure( case_id=self.lab_referral_id, attrs={ 'create': True, 'case_type': 'lab_referral', 'owner_id': self.dmc.get_id, "update": {} }, indices=[ CaseIndex( self.test, identifier='host', relationship=CASE_INDEX_EXTENSION, related_type=self.test.attrs['case_type'], ) ], ) def create_adherence_cases(self, adherence_dates, adherence_source='99DOTS'): return self.factory.create_or_update_cases([ get_adherence_case_structure( adherence_date.strftime('%Y-%m-%d-%H-%M'), self.episode_id, adherence_date, extra_update={ "name": adherence_date, "adherence_source": adherence_source, "adherence_value": "unobserved_dose", }) for adherence_date in adherence_dates ]) def create_adherence_case(self, adherence_date, adherence_source="99DOTS", adherence_value="unobserved_dose", case_id=None): return self.factory.create_or_update_cases([ get_adherence_case_structure(case_id, self.episode_id, adherence_date, extra_update={ "adherence_source": adherence_source, "adherence_value": adherence_value, }) ]) def create_prescription_case(self, extra_update=None): return self.factory.create_or_update_case( get_prescription_case_structure(uuid.uuid4().hex, self.episode_id, extra_update))[0] def create_voucher_case(self, prescription_id, extra_update=None): return self.factory.create_or_update_case( get_voucher_case_structure(uuid.uuid4().hex, prescription_id, extra_update))[0] def create_referral_case(self, case_id): return self.factory.create_or_update_cases( [get_referral_case_structure(case_id, self.person_id)]) @nottest def create_test_case(self, occurrence_id, extra_update=None): return self.factory.create_or_update_case( get_test_case_structure(uuid.uuid4().hex, occurrence_id, extra_update))[0]
class ImporterTest(TestCase): def setUp(self): super(ImporterTest, self).setUp() self.domain_obj = create_domain("importer-test") self.domain = self.domain_obj.name self.default_case_type = 'importer-test-casetype' self.couch_user = WebUser.create(None, "test", "foobar", None, None) self.couch_user.add_domain_membership(self.domain, is_admin=True) self.couch_user.save() self.accessor = CaseAccessors(self.domain) self.factory = CaseFactory(domain=self.domain, case_defaults={ 'case_type': self.default_case_type, }) delete_all_cases() def tearDown(self): self.couch_user.delete() self.domain_obj.delete() super(ImporterTest, self).tearDown() def _config(self, col_names, search_column=None, case_type=None, search_field='case_id', create_new_cases=True): return ImporterConfig( couch_user_id=self.couch_user._id, case_type=case_type or self.default_case_type, excel_fields=col_names, case_fields=[''] * len(col_names), custom_fields=col_names, search_column=search_column or col_names[0], search_field=search_field, create_new_cases=create_new_cases, ) @run_with_all_backends @patch('corehq.apps.case_importer.tasks.bulk_import_async.update_state') def testImportFileMissing(self, update_state): # by using a made up upload_id, we ensure it's not referencing any real file case_upload = CaseUploadRecord(upload_id=str(uuid.uuid4()), task_id=str(uuid.uuid4())) case_upload.save() res = bulk_import_async.delay(self._config(['anything']), self.domain, case_upload.upload_id) self.assertIsInstance(res.result, Ignore) update_state.assert_called_with( state=states.FAILURE, meta=get_interned_exception( 'Sorry, your session has expired. Please start over and try again.' )) self.assertEqual(0, len(get_case_ids_in_domain(self.domain))) @run_with_all_backends def testImportBasic(self): config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], ['case_id-0', 'age-0', 'sex-0', 'location-0'], ['case_id-1', 'age-1', 'sex-1', 'location-1'], ['case_id-2', 'age-2', 'sex-2', 'location-2'], ['case_id-3', 'age-3', 'sex-3', 'location-3'], ['case_id-4', 'age-4', 'sex-4', 'location-4'], ) res = do_import(file, config, self.domain) self.assertEqual(5, res['created_count']) self.assertEqual(0, res['match_count']) self.assertFalse(res['errors']) self.assertEqual(1, res['num_chunks']) case_ids = self.accessor.get_case_ids_in_domain() cases = list(self.accessor.get_cases(case_ids)) self.assertEqual(5, len(cases)) properties_seen = set() for case in cases: self.assertEqual(self.couch_user._id, case.user_id) self.assertEqual(self.couch_user._id, case.owner_id) self.assertEqual(self.default_case_type, case.type) for prop in ['age', 'sex', 'location']: self.assertTrue(prop in case.get_case_property(prop)) self.assertFalse( case.get_case_property(prop) in properties_seen) properties_seen.add(case.get_case_property(prop)) @run_with_all_backends def testImportNamedColumns(self): config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], ['case_id-0', 'age-0', 'sex-0', 'location-0'], ['case_id-1', 'age-1', 'sex-1', 'location-1'], ['case_id-2', 'age-2', 'sex-2', 'location-2'], ['case_id-3', 'age-3', 'sex-3', 'location-3'], ) res = do_import(file, config, self.domain) self.assertEqual(4, res['created_count']) self.assertEqual(4, len(self.accessor.get_case_ids_in_domain())) @run_with_all_backends def testImportTrailingWhitespace(self): cols = ['case_id', 'age', 'sex\xa0', 'location'] config = self._config(cols) file = make_worksheet_wrapper( ['case_id', 'age', 'sex\xa0', 'location'], ['case_id-0', 'age-0', 'sex\xa0-0', 'location-0'], ) res = do_import(file, config, self.domain) self.assertEqual(1, res['created_count']) case_ids = self.accessor.get_case_ids_in_domain() self.assertEqual(1, len(case_ids)) case = self.accessor.get_case(case_ids[0]) self.assertTrue(bool(case.get_case_property( 'sex'))) # make sure the value also got properly set @run_with_all_backends def testCaseIdMatching(self): # bootstrap a stub case [case] = self.factory.create_or_update_case( CaseStructure(attrs={ 'create': True, 'update': { 'importer_test_prop': 'foo' }, })) self.assertEqual(1, len(self.accessor.get_case_ids_in_domain())) config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], [case.case_id, 'age-0', 'sex-0', 'location-0'], [case.case_id, 'age-1', 'sex-1', 'location-1'], [case.case_id, 'age-2', 'sex-2', 'location-2'], ) res = do_import(file, config, self.domain) self.assertEqual(0, res['created_count']) self.assertEqual(3, res['match_count']) self.assertFalse(res['errors']) # shouldn't create any more cases, just the one case_ids = self.accessor.get_case_ids_in_domain() self.assertEqual(1, len(case_ids)) [case] = self.accessor.get_cases(case_ids) for prop in ['age', 'sex', 'location']: self.assertTrue(prop in case.get_case_property(prop)) # shouldn't touch existing properties self.assertEqual('foo', case.get_case_property('importer_test_prop')) @run_with_all_backends def testCaseLookupTypeCheck(self): [case] = self.factory.create_or_update_case( CaseStructure(attrs={ 'create': True, 'case_type': 'nonmatch-type', })) self.assertEqual(1, len(self.accessor.get_case_ids_in_domain())) config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], [case.case_id, 'age-0', 'sex-0', 'location-0'], [case.case_id, 'age-1', 'sex-1', 'location-1'], [case.case_id, 'age-2', 'sex-2', 'location-2'], ) res = do_import(file, config, self.domain) # because the type is wrong these shouldn't match self.assertEqual(3, res['created_count']) self.assertEqual(0, res['match_count']) self.assertEqual(4, len(self.accessor.get_case_ids_in_domain())) @run_with_all_backends def testCaseLookupDomainCheck(self): self.factory.domain = 'wrong-domain' [case] = self.factory.create_or_update_case( CaseStructure(attrs={ 'create': True, })) self.assertEqual(0, len(self.accessor.get_case_ids_in_domain())) config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], [case.case_id, 'age-0', 'sex-0', 'location-0'], [case.case_id, 'age-1', 'sex-1', 'location-1'], [case.case_id, 'age-2', 'sex-2', 'location-2'], ) res = do_import(file, config, self.domain) # because the domain is wrong these shouldn't match self.assertEqual(3, res['created_count']) self.assertEqual(0, res['match_count']) self.assertEqual(3, len(self.accessor.get_case_ids_in_domain())) @run_with_all_backends def testExternalIdMatching(self): # bootstrap a stub case external_id = 'importer-test-external-id' [case] = self.factory.create_or_update_case( CaseStructure(attrs={ 'create': True, 'external_id': external_id, })) self.assertEqual(1, len(self.accessor.get_case_ids_in_domain())) headers = ['external_id', 'age', 'sex', 'location'] config = self._config(headers, search_field='external_id') file = make_worksheet_wrapper( ['external_id', 'age', 'sex', 'location'], ['importer-test-external-id', 'age-0', 'sex-0', 'location-0'], ['importer-test-external-id', 'age-1', 'sex-1', 'location-1'], ['importer-test-external-id', 'age-2', 'sex-2', 'location-2'], ) res = do_import(file, config, self.domain) self.assertEqual(0, res['created_count']) self.assertEqual(3, res['match_count']) self.assertFalse(res['errors']) # shouldn't create any more cases, just the one self.assertEqual(1, len(self.accessor.get_case_ids_in_domain())) @run_with_all_backends def test_external_id_matching_on_create_with_custom_column_name(self): headers = ['id_column', 'age', 'sex', 'location'] external_id = 'external-id-test' config = self._config(headers[1:], search_column='id_column', search_field='external_id') file = make_worksheet_wrapper( ['id_column', 'age', 'sex', 'location'], ['external-id-test', 'age-0', 'sex-0', 'location-0'], ['external-id-test', 'age-1', 'sex-1', 'location-1'], ) res = do_import(file, config, self.domain) self.assertFalse(res['errors']) self.assertEqual(1, res['created_count']) self.assertEqual(1, res['match_count']) case_ids = self.accessor.get_case_ids_in_domain() self.assertEqual(1, len(case_ids)) case = self.accessor.get_case(case_ids[0]) self.assertEqual(external_id, case.external_id) def testNoCreateNew(self): config = self._config(['case_id', 'age', 'sex', 'location'], create_new_cases=False) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], ['case_id-0', 'age-0', 'sex-0', 'location-0'], ['case_id-1', 'age-1', 'sex-1', 'location-1'], ['case_id-2', 'age-2', 'sex-2', 'location-2'], ['case_id-3', 'age-3', 'sex-3', 'location-3'], ['case_id-4', 'age-4', 'sex-4', 'location-4'], ) res = do_import(file, config, self.domain) # no matching and no create new set - should do nothing self.assertEqual(0, res['created_count']) self.assertEqual(0, res['match_count']) self.assertEqual(0, len(get_case_ids_in_domain(self.domain))) def testBlankRows(self): # don't create new cases for rows left blank config = self._config(['case_id', 'age', 'sex', 'location'], create_new_cases=True) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], [None, None, None, None], ['', '', '', ''], ) res = do_import(file, config, self.domain) # no matching and no create new set - should do nothing self.assertEqual(0, res['created_count']) self.assertEqual(0, res['match_count']) self.assertEqual(0, len(get_case_ids_in_domain(self.domain))) @patch('corehq.apps.case_importer.do_import.CASEBLOCK_CHUNKSIZE', 2) def testBasicChunking(self): config = self._config(['case_id', 'age', 'sex', 'location']) file = make_worksheet_wrapper( ['case_id', 'age', 'sex', 'location'], ['case_id-0', 'age-0', 'sex-0', 'location-0'], ['case_id-1', 'age-1', 'sex-1', 'location-1'], ['case_id-2', 'age-2', 'sex-2', 'location-2'], ['case_id-3', 'age-3', 'sex-3', 'location-3'], ['case_id-4', 'age-4', 'sex-4', 'location-4'], ) res = do_import(file, config, self.domain) # 5 cases in chunks of 2 = 3 chunks self.assertEqual(3, res['num_chunks']) self.assertEqual(5, res['created_count']) self.assertEqual(5, len(get_case_ids_in_domain(self.domain))) @run_with_all_backends def testExternalIdChunking(self): # bootstrap a stub case external_id = 'importer-test-external-id' headers = ['external_id', 'age', 'sex', 'location'] config = self._config(headers, search_field='external_id') file = make_worksheet_wrapper( ['external_id', 'age', 'sex', 'location'], ['importer-test-external-id', 'age-0', 'sex-0', 'location-0'], ['importer-test-external-id', 'age-1', 'sex-1', 'location-1'], ['importer-test-external-id', 'age-2', 'sex-2', 'location-2'], ) # the first one should create the case, and the remaining two should update it res = do_import(file, config, self.domain) self.assertEqual(1, res['created_count']) self.assertEqual(2, res['match_count']) self.assertFalse(res['errors']) self.assertEqual(2, res['num_chunks']) # the lookup causes an extra chunk # should just create the one case case_ids = self.accessor.get_case_ids_in_domain() self.assertEqual(1, len(case_ids)) [case] = self.accessor.get_cases(case_ids) self.assertEqual(external_id, case.external_id) for prop in ['age', 'sex', 'location']: self.assertTrue(prop in case.get_case_property(prop)) @run_with_all_backends def testParentCase(self): headers = ['parent_id', 'name', 'case_id'] config = self._config(headers, create_new_cases=True, search_column='case_id') rows = 3 [parent_case] = self.factory.create_or_update_case( CaseStructure(attrs={'create': True})) self.assertEqual(1, len(self.accessor.get_case_ids_in_domain())) file = make_worksheet_wrapper( ['parent_id', 'name', 'case_id'], [parent_case.case_id, 'name-0', 'case_id-0'], [parent_case.case_id, 'name-1', 'case_id-1'], [parent_case.case_id, 'name-2', 'case_id-2'], ) file_missing = make_worksheet_wrapper( ['parent_id', 'name', 'case_id'], ['parent_id-0', 'name-0', 'case_id-0'], ['parent_id-1', 'name-1', 'case_id-1'], ['parent_id-2', 'name-2', 'case_id-2'], ) # Should successfully match on `rows` cases res = do_import(file, config, self.domain) self.assertEqual(rows, res['created_count']) # Should be unable to find parent case on `rows` cases res = do_import(file_missing, config, self.domain) error_column_name = 'parent_id' self.assertEqual( rows, len(res['errors'][exceptions.InvalidParentId.title] [error_column_name]['rows']), "All cases should have missing parent") def import_mock_file(self, rows): config = self._config(rows[0]) xls_file = make_worksheet_wrapper(*rows) return do_import(xls_file, config, self.domain) @run_with_all_backends def testLocationOwner(self): # This is actually testing several different things, but I figure it's # worth it, as each of these tests takes a non-trivial amount of time. non_case_sharing = LocationType.objects.create(domain=self.domain, name='lt1', shares_cases=False) case_sharing = LocationType.objects.create(domain=self.domain, name='lt2', shares_cases=True) location = make_loc('loc-1', 'Loc 1', self.domain, case_sharing.code) make_loc('loc-2', 'Loc 2', self.domain, case_sharing.code) duplicate_loc = make_loc('loc-3', 'Loc 2', self.domain, case_sharing.code) improper_loc = make_loc('loc-4', 'Loc 4', self.domain, non_case_sharing.code) res = self.import_mock_file([ ['case_id', 'name', 'owner_id', 'owner_name'], ['', 'location-owner-id', location.group_id, ''], ['', 'location-owner-code', '', location.site_code], ['', 'location-owner-name', '', location.name], ['', 'duplicate-location-name', '', duplicate_loc.name], ['', 'non-case-owning-name', '', improper_loc.name], ]) case_ids = self.accessor.get_case_ids_in_domain() cases = {c.name: c for c in list(self.accessor.get_cases(case_ids))} self.assertEqual(cases['location-owner-id'].owner_id, location.group_id) self.assertEqual(cases['location-owner-code'].owner_id, location.group_id) self.assertEqual(cases['location-owner-name'].owner_id, location.group_id) error_message = exceptions.DuplicateLocationName.title error_column_name = None self.assertIn(error_message, res['errors']) self.assertEqual( res['errors'][error_message][error_column_name]['rows'], [5]) error_message = exceptions.InvalidOwner.title self.assertIn(error_message, res['errors']) error_column_name = 'owner_name' self.assertEqual( res['errors'][error_message][error_column_name]['rows'], [6]) @run_with_all_backends def test_opened_on(self): case = self.factory.create_case() new_date = '2015-04-30T14:41:53.000000Z' with flag_enabled('BULK_UPLOAD_DATE_OPENED'): self.import_mock_file([['case_id', 'date_opened'], [case.case_id, new_date]]) case = CaseAccessors(self.domain).get_case(case.case_id) self.assertEqual(case.opened_on, PhoneTime(parse_datetime(new_date)).done())
class OwnerCleanlinessTest(SyncBaseTest): def setUp(self): super(OwnerCleanlinessTest, self).setUp() # ensure that randomization is on OWNERSHIP_CLEANLINESS.randomness = 1 self.owner_id = uuid.uuid4().hex self.synclog_id = uuid.uuid4().hex self.domain = uuid.uuid4().hex self.factory = CaseFactory( domain=self.domain, case_defaults={ 'create': True, 'owner_id': self.owner_id, 'user_id': self.owner_id, } ) self.assert_owner_clean() # this first call creates the OwnershipCleanliness doc self.sample_case = self.factory.create_case() self.child, self.parent = self.factory.create_or_update_case( CaseStructure( relationships=[ CaseRelationship(), ] ) ) self.assert_owner_clean() # this is an actual assertion def _verify_set_cleanliness_flags(self): """ Can be run at the end of any relevant test to check the current state of the OwnershipCleanliness object and verify that rebuilding it from scratch produces the same result """ is_clean = self.owner_cleanliness.is_clean hint = self.owner_cleanliness.hint self.owner_cleanliness.delete() set_cleanliness_flags(self.domain, self.owner_id, force_full=True) new_cleanliness = OwnershipCleanlinessFlag.objects.get(owner_id=self.owner_id) self.assertEqual(is_clean, new_cleanliness.is_clean) self.assertEqual(hint, new_cleanliness.hint) if hint: self.assertTrue(hint_still_valid(self.domain, self.owner_id, hint)) @property def owner_cleanliness(self): return OwnershipCleanlinessFlag.objects.get_or_create( owner_id=self.owner_id, domain=self.domain, defaults={'is_clean': True} )[0] def assert_owner_clean(self): return self.assertTrue(self.owner_cleanliness.is_clean) def assert_owner_dirty(self): return self.assertFalse(self.owner_cleanliness.is_clean) def _set_owner(self, case_id, owner_id): case = self.factory.create_or_update_case( CaseStructure(case_id=case_id, attrs={'create': False, 'owner_id': owner_id}) )[0] self.assertEqual(owner_id, case.owner_id) def test_add_normal_case_stays_clean(self): # change the owner ID of a normal case, should remain clean self.factory.create_case() self.assert_owner_clean() self._verify_set_cleanliness_flags() def test_change_owner_stays_clean(self): # change the owner ID of a normal case, should remain clean new_owner = uuid.uuid4().hex self._set_owner(self.sample_case._id, new_owner) self.assert_owner_clean() self._verify_set_cleanliness_flags() def test_change_owner_child_case_stays_clean(self): # change the owner ID of a child case, should remain clean new_owner = uuid.uuid4().hex self._set_owner(self.child._id, new_owner) self.assert_owner_clean() self._verify_set_cleanliness_flags() def test_add_clean_parent_stays_clean(self): # add a parent with the same owner, should remain clean self.factory.create_or_update_case(CaseStructure(relationships=[CaseRelationship()])) self.assert_owner_clean() self._verify_set_cleanliness_flags() def test_create_dirty_makes_dirty(self): # create a case and a parent case with a different owner at the same time # make sure the owner becomes dirty. new_owner = uuid.uuid4().hex [child, parent] = self.factory.create_or_update_case( CaseStructure( relationships=[ CaseRelationship(CaseStructure(attrs={'owner_id': new_owner})) ] ) ) self.assert_owner_dirty() self.assertEqual(child._id, self.owner_cleanliness.hint) self._verify_set_cleanliness_flags() def test_add_dirty_parent_makes_dirty(self): # add parent with a different owner and make sure the owner becomes dirty new_owner = uuid.uuid4().hex [child, parent] = self.factory.create_or_update_case( CaseStructure( case_id=self.sample_case._id, relationships=[ CaseRelationship(CaseStructure(attrs={'owner_id': new_owner})) ] ) ) self.assert_owner_dirty() self.assertEqual(child._id, self.owner_cleanliness.hint) self._verify_set_cleanliness_flags() def test_change_parent_owner_makes_dirty(self): # change the owner id of a parent case and make sure the owner becomes dirty new_owner = uuid.uuid4().hex self._set_owner(self.parent._id, new_owner) self.assert_owner_dirty() self.assertEqual(self.child._id, self.owner_cleanliness.hint) self._verify_set_cleanliness_flags() def test_toggle(self): # make sure the flag gets removed OWNERSHIP_CLEANLINESS.randomness = 0 # and any test that normally expects a flag to be set to fail with self.assertRaises(AssertionError): self.test_create_dirty_makes_dirty() def test_set_flag_clean_no_data(self): unused_owner_id = uuid.uuid4().hex set_cleanliness_flags(self.domain, unused_owner_id) self.assertTrue(OwnershipCleanlinessFlag.objects.get(owner_id=unused_owner_id).is_clean) def test_hint_invalidation(self): self.test_change_parent_owner_makes_dirty() self._set_owner(self.parent._id, self.owner_id) # after the submission the dirtiness flag should still be set # since it isn't invalidated right away self.assert_owner_dirty() # explicitly make sure the hint is no longer valid self.assertFalse(hint_still_valid(self.domain, self.owner_id, self.owner_cleanliness.hint)) # reset the cleanliness flag and ensure it worked set_cleanliness_flags(self.domain, self.owner_id) self.assert_owner_clean() self.assertEqual(None, self.owner_cleanliness.hint) def test_cross_domain_on_submission(self): # create a form that makes a dirty owner with the same ID but in a different domain # make sure the original owner stays clean new_domain = uuid.uuid4().hex # initialize the new cleanliness flag OwnershipCleanlinessFlag.objects.create(domain=new_domain, owner_id=self.owner_id, is_clean=True) self.factory.domain = new_domain self.factory.create_or_update_case( CaseStructure( relationships=[ CaseRelationship(CaseStructure(attrs={'owner_id': uuid.uuid4().hex})) ] ) ) self.assert_owner_clean() self.assertEqual( False, OwnershipCleanlinessFlag.objects.get(owner_id=self.owner_id, domain=new_domain).is_clean, ) def test_cross_domain_both_clean(self): new_domain = uuid.uuid4().hex self.factory.domain = new_domain self.factory.create_or_update_case( CaseStructure( relationships=[ CaseRelationship(), ] ) ) # two clean ownership models in different domains should report clean self.assertTrue(get_cleanliness_flag_from_scratch(self.domain, self.owner_id).is_clean) self.assertTrue(get_cleanliness_flag_from_scratch(new_domain, self.owner_id).is_clean) def test_cross_domain_dirty(self): new_domain = uuid.uuid4().hex new_owner = uuid.uuid4().hex self.factory.domain = new_domain self.factory.create_or_update_case( CaseStructure( relationships=[ CaseRelationship(CaseStructure(attrs={'owner_id': new_owner})), ] ) ) # original domain should stay clean but the new one should be dirty self.assertTrue(get_cleanliness_flag_from_scratch(self.domain, self.owner_id).is_clean) self.assertFalse(get_cleanliness_flag_from_scratch(new_domain, self.owner_id).is_clean) @override_settings(TESTS_SHOULD_TRACK_CLEANLINESS=False) def test_autocreate_flag_off(self): new_owner = uuid.uuid4().hex self.factory.create_or_update_case( CaseStructure(case_id=uuid.uuid4().hex, attrs={'create': True, 'owner_id': new_owner}) )[0] self.assertFalse(OwnershipCleanlinessFlag.objects.filter(domain=self.domain, owner_id=new_owner).exists()) @override_settings(TESTS_SHOULD_TRACK_CLEANLINESS=True) def test_autocreate_flag_on(self): new_owner = uuid.uuid4().hex self.factory.create_or_update_case( CaseStructure(case_id=uuid.uuid4().hex, attrs={'create': True, 'owner_id': new_owner}) )[0] flag = OwnershipCleanlinessFlag.objects.get(domain=self.domain, owner_id=new_owner) self.assertEqual(True, flag.is_clean)
class TestExtensionCaseIds(TestCase): @classmethod def setUpClass(cls): delete_all_cases() def setUp(self): self.domain = 'domain' self.factory = CaseFactory(self.domain) def test_no_extensions(self): """ Returns empty when there are other index types """ parent_id = uuid.uuid4().hex child_id = uuid.uuid4().hex parent = CaseStructure(case_id=parent_id) self.factory.create_or_update_case( CaseStructure( case_id=child_id, indices=[ CaseIndex(parent, relationship=CASE_INDEX_CHILD) ] ) ) returned_cases = get_extension_case_ids(self.domain, [parent_id]) self.assertEqual(returned_cases, []) def test_simple_extension_returned(self): """ Should return extension if it exists """ host_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex host = CaseStructure(case_id=host_id) self.factory.create_or_update_case( CaseStructure( case_id=extension_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION) ] ) ) returned_cases = get_extension_case_ids(self.domain, [host_id]) self.assertItemsEqual(returned_cases, [extension_id]) def test_extension_of_multiple_hosts_returned(self): """ Should return an extension from any host if there are multiple indices """ host_id = uuid.uuid4().hex host_2_id = uuid.uuid4().hex parent_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex host = CaseStructure(case_id=host_id) host_2 = CaseStructure(case_id=host_2_id) parent = CaseStructure(case_id=parent_id) self.factory.create_or_update_case( CaseStructure( case_id=extension_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION, identifier="host"), CaseIndex(host_2, relationship=CASE_INDEX_EXTENSION, identifier="host_2"), CaseIndex(parent, relationship=CASE_INDEX_CHILD), ] ) ) returned_cases = get_extension_case_ids(self.domain, [host_2_id]) self.assertItemsEqual(returned_cases, [extension_id]) returned_cases = get_extension_case_ids(self.domain, [host_id]) self.assertItemsEqual(returned_cases, [extension_id]) def test_host_with_multiple_extensions(self): """ Return all extensions from a single host """ host_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex extension_2_id = uuid.uuid4().hex host = CaseStructure(case_id=host_id) self.factory.create_or_update_cases([ CaseStructure( case_id=extension_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION, identifier="host"), ] ), CaseStructure( case_id=extension_2_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION, identifier="host"), ] ), ] ) returned_cases = get_extension_case_ids(self.domain, [host_id]) self.assertItemsEqual(returned_cases, [extension_id, extension_2_id]) def test_extensions_from_list(self): """ Given a list of hosts, should return all extensions """ host_id = uuid.uuid4().hex host_2_id = uuid.uuid4().hex extension_id = uuid.uuid4().hex extension_2_id = uuid.uuid4().hex host = CaseStructure(case_id=host_id) host_2 = CaseStructure(case_id=host_2_id) self.factory.create_or_update_case( CaseStructure( case_id=extension_id, indices=[ CaseIndex(host, relationship=CASE_INDEX_EXTENSION, identifier="host"), ] ) ) self.factory.create_or_update_case( CaseStructure( case_id=extension_2_id, indices=[ CaseIndex(host_2, relationship=CASE_INDEX_EXTENSION, identifier="host"), ] ) ) returned_cases = get_extension_case_ids(self.domain, [host_id, host_2_id]) self.assertItemsEqual(returned_cases, [extension_id, extension_2_id])
class OwnerCleanlinessTest(DeprecatedBaseSyncTest): def setUp(self): super(OwnerCleanlinessTest, self).setUp() self.owner_id = uuid.uuid4().hex self.synclog_id = uuid.uuid4().hex self.domain = uuid.uuid4().hex self.factory = CaseFactory( domain=self.domain, case_defaults={ 'create': True, 'owner_id': self.owner_id, 'user_id': self.owner_id, } ) self.assert_owner_clean() # this first call creates the OwnershipCleanliness doc self.sample_case = self.factory.create_case() self.child, self.parent = self.factory.create_or_update_case( CaseStructure( indices=[ CaseIndex(), ] ) ) self.extension, self.host = self.factory.create_or_update_case( CaseStructure( attrs={'owner_id': UNOWNED_EXTENSION_OWNER_ID}, indices=[ CaseIndex( relationship=CASE_INDEX_EXTENSION ), ] ) ) self.assert_owner_clean() # this is an actual assertion def _verify_set_cleanliness_flags(self, owner_id=None): """ Can be run at the end of any relevant test to check the current state of the OwnershipCleanliness object and verify that rebuilding it from scratch produces the same result """ if owner_id is None: owner_id = self.owner_id owner_cleanliness = self._owner_cleanliness_for_id(owner_id) is_clean = owner_cleanliness.is_clean hint = owner_cleanliness.hint owner_cleanliness.delete() set_cleanliness_flags(self.domain, owner_id, force_full=True) new_cleanliness = OwnershipCleanlinessFlag.objects.get(owner_id=owner_id) self.assertEqual(is_clean, new_cleanliness.is_clean) self.assertEqual(hint, new_cleanliness.hint) if hint: self.assertTrue(hint_still_valid(self.domain, hint)) @property def owner_cleanliness(self): return self._owner_cleanliness_for_id(self.owner_id) def _owner_cleanliness_for_id(self, owner_id): return OwnershipCleanlinessFlag.objects.get_or_create( owner_id=owner_id, domain=self.domain, defaults={'is_clean': True} )[0] def assert_owner_clean(self): self.assertTrue(self.owner_cleanliness.is_clean) def assert_owner_dirty(self): self.assertFalse(self.owner_cleanliness.is_clean) def assert_owner_temporarily_dirty(self): """ Changing any case's owner makes the previous owner ID temporarily dirty, to allow syncs to happen, but the should become clean on a rebuild. This checks that workflow and rebuilds the cleanliness flag. """ self.assertFalse(self.owner_cleanliness.is_clean) set_cleanliness_flags(self.domain, self.owner_id, force_full=True) self.assertTrue(self.owner_cleanliness.is_clean) def _set_owner(self, case_id, owner_id): case = self.factory.create_or_update_case( CaseStructure(case_id=case_id, attrs={'create': False, 'owner_id': owner_id}) )[0] self.assertEqual(owner_id, case.owner_id)
def handle(self, commit, domain, log_path, **options): commit = commit factory = CaseFactory(domain) headers = [ 'case_id', 'testing_facility_id', 'datamigration_testing_facility_id', ] print("Starting {} migration on {} at {}".format( "real" if commit else "fake", domain, datetime.datetime.utcnow())) case_ids = [ hit['_id'] for hit in (CaseSearchES().domain( domain).case_type("test").case_property_query( "result_recorded", "yes", "must").run().hits) ] failed_tests = [] with open(log_path, "w") as log_file: writer = csv.writer(log_file) writer.writerow(headers) for test in CaseAccessors(domain=domain).iter_cases(case_ids): try: if test.get_case_property('datamigration_testing_facility_id') != 'yes' \ and not test.get_case_property('testing_facility_id'): form_data = self._get_result_recorded_form(test) microscopy_id = self._get_path([ 'update_test_result', 'microscopy', 'ql_testing_facility_details', 'default_dmc_id' ], form_data) cbnaat_id = self._get_path([ 'update_test_result', 'cbnaat', 'ql_testing_facility_details', 'default_cdst_id' ], form_data) testing_facility_id = microscopy_id or cbnaat_id if testing_facility_id: writer.writerow( [test.case_id, testing_facility_id, "yes"]) print('Updating {}...'.format(test.case_id)) case_structure = CaseStructure( case_id=test.case_id, walk_related=False, attrs={ "create": False, "update": { "datamigration_testing_facility_id": "yes", "testing_facility_id": testing_facility_id, }, }, ) if commit: factory.create_or_update_case(case_structure) else: writer.writerow( [test.case_id, testing_facility_id, "no"]) except: failed_tests.append(test.case_id) print("Migration finished at {}".format(datetime.datetime.utcnow())) print("Failed tests: {}".format(failed_tests))
class IndexedCaseExpressionTest(TestCase): def setUp(self): super(IndexedCaseExpressionTest, self).setUp() self.domain = uuid.uuid4().hex self.factory = CaseFactory(domain=self.domain) self.context = EvaluationContext({"domain": self.domain}) def tearDown(self): delete_all_xforms() delete_all_cases() super(IndexedCaseExpressionTest, self).tearDown() def test_parent_case_no_index(self): parent_id = uuid.uuid4().hex child_id = uuid.uuid4().hex [child, parent] = self.factory.create_or_update_case(CaseStructure( case_id=child_id, indices=[ CaseIndex(CaseStructure(case_id=parent_id, attrs={'create': True})) ], attrs={'create': True} )) spec = { 'type': 'nested', 'argument_expression': { 'type': 'indexed_case', 'case_expression': { 'type': 'identity' } }, 'value_expression': { 'type': 'property_name', 'property_name': '_id' } } self.assertEqual( ExpressionFactory.from_spec(spec)(child.to_json(), self.context), parent_id ) def test_named_index(self): parent_id = uuid.uuid4().hex child_id = uuid.uuid4().hex [child, parent] = self.factory.create_or_update_case(CaseStructure( case_id=child_id, indices=[ CaseIndex(CaseStructure(case_id=parent_id, attrs={'create': True}), identifier='mother') ], attrs={'create': True} )) spec = { 'type': 'nested', 'argument_expression': { 'type': 'indexed_case', 'case_expression': { 'type': 'identity' }, 'index': 'mother' }, 'value_expression': { 'type': 'property_name', 'property_name': '_id' } } self.assertEqual( ExpressionFactory.from_spec(spec)(child.to_json(), self.context), parent_id ) def test_multiple_indexes(self): parent_id1 = uuid.uuid4().hex parent_id2 = uuid.uuid4().hex child_id = uuid.uuid4().hex [child, parent1, parent2] = self.factory.create_or_update_case(CaseStructure( case_id=child_id, indices=[ CaseIndex(CaseStructure(case_id=parent_id1, attrs={'create': True}), identifier='parent1'), CaseIndex(CaseStructure(case_id=parent_id2, attrs={'create': True}), identifier='parent2') ], attrs={'create': True} )) spec = { 'type': 'nested', 'argument_expression': { 'type': 'indexed_case', 'case_expression': { 'type': 'identity' }, 'index': 'parent1' }, 'value_expression': { 'type': 'property_name', 'property_name': '_id' } } self.assertEqual( ExpressionFactory.from_spec(spec)(child.to_json(), self.context), parent_id1 ) spec['argument_expression']['index'] = 'parent2' self.assertEqual( ExpressionFactory.from_spec(spec)(child.to_json(), self.context), parent_id2 ) def test_grandparent_index(self): household_id = uuid.uuid4().hex parent_id = uuid.uuid4().hex child_id = uuid.uuid4().hex parent_case_structure = CaseStructure( case_id=parent_id, indices=[ CaseIndex(CaseStructure(case_id=household_id, attrs={'create': True}), identifier='household') ], attrs={'create': True} ) [child, parent, household] = self.factory.create_or_update_case(CaseStructure( case_id=child_id, indices=[CaseIndex(parent_case_structure)], attrs={'create': True} )) spec = { 'type': 'nested', 'argument_expression': { 'type': 'indexed_case', 'case_expression': { 'type': 'indexed_case', 'case_expression': { 'type': 'identity' }, 'index': 'parent' }, 'index': 'household', }, 'value_expression': { 'type': 'property_name', 'property_name': '_id' } } self.assertEqual( ExpressionFactory.from_spec(spec)(child.to_json(), self.context), household_id )
def handle(self, commit, domain, log_path, **options): commit = commit factory = CaseFactory(domain) headers = [ 'case_id', 'current_treatment_initiated', 'current_treatment_status', 'current_diagnosing_facility_id', 'current_treatment_initiating_facility_id', 'updated_treatment_initiated', 'updated_treatment_status', 'datamigration_treatment_status_fix', ] print("Starting {} migration on {} at {}".format( "real" if commit else "fake", domain, datetime.datetime.utcnow() )) case_ids = [ hit['_id'] for hit in (CaseSearchES() .domain(domain) .case_type("episode") .case_property_query("episode_type", "confirmed_tb", "must") .run().hits) ] with open(log_path, "w") as log_file: writer = csv.writer(log_file) writer.writerow(headers) for episode in CaseAccessors(domain=domain).iter_cases(case_ids): if episode.get_case_property('datamigration_treatment_status_fix') != 'yes' \ and not episode.get_case_property('diagnosing_facility_id') \ and not episode.get_case_property('treatment_initiating_facility_id'): current_treatment_initiated = episode.get_case_property('treatment_initiated') current_treatment_status = episode.get_case_property('treatment_status') current_diagnosing_facility_id = episode.get_case_property('diagnosing_facility_id') current_treatment_initiating_facility_id = \ episode.get_case_property('treatment_initiating_facility_id') if current_treatment_status == "initiated_outside_rntcp" \ or current_treatment_initiated == "yes_private": # this should ideally not need a migration but checking anyway treatment_initiated = "yes_private" treatment_status = "initiated_outside_rntcp" elif current_diagnosing_facility_id == current_treatment_initiating_facility_id: treatment_initiated = "yes_phi" treatment_status = "initiated_first_line_treatment" else: treatment_initiated = "yes_phi" treatment_status = "initiated_outside_facility" if treatment_initiated == current_treatment_initiated \ and treatment_status == current_treatment_status: # skip writer.writerow([episode.case_id, current_treatment_initiated, current_treatment_status, current_diagnosing_facility_id, current_treatment_initiating_facility_id, None, None, "no"]) else: writer.writerow([episode.case_id, current_treatment_initiated, current_treatment_status, current_diagnosing_facility_id, current_treatment_initiating_facility_id, treatment_initiated, treatment_status, "yes"]) print('Updating {}...'.format(episode.case_id)) case_structure = CaseStructure( case_id=episode.case_id, walk_related=False, attrs={ "create": False, "update": { "datamigration_treatment_status_fix": "yes", "treatment_initiated": treatment_initiated, "treatment_status": treatment_status, }, }, ) if commit: factory.create_or_update_case(case_structure) print("Migration finished at {}".format(datetime.datetime.utcnow()))