def test_archive(self): log = SyncLog(cases_on_phone=[CaseState(case_id="c1", indices=[CommCareCaseIndex(identifier="d1-id", referenced_id="d1")]), CaseState(case_id="c2", indices=[CommCareCaseIndex(identifier="d1-id", referenced_id="d1"), CommCareCaseIndex(identifier="d2-id", referenced_id="d2")]), CaseState(case_id="c3", indices=[])], dependent_cases_on_phone=[CaseState(case_id="d1", indices=[]), CaseState(case_id="d2", indices=[])]) self.assertEqual(5, len(log.get_footprint_of_cases_on_phone())) self.assertTrue("c3" in log.get_footprint_of_cases_on_phone()) log.archive_case("c3") self.assertEqual(4, len(log.get_footprint_of_cases_on_phone())) self.assertFalse("c3" in log.get_footprint_of_cases_on_phone()) self.assertTrue("c2" in log.get_footprint_of_cases_on_phone()) self.assertTrue("d2" in log.get_footprint_of_cases_on_phone()) log.archive_case("c2") self.assertEqual(2, len(log.get_footprint_of_cases_on_phone())) self.assertFalse("c2" in log.get_footprint_of_cases_on_phone()) self.assertFalse("d2" in log.get_footprint_of_cases_on_phone()) self.assertTrue("c1" in log.get_footprint_of_cases_on_phone()) self.assertTrue("d1" in log.get_footprint_of_cases_on_phone()) log.archive_case("c1") self.assertEqual(0, len(log.get_footprint_of_cases_on_phone()))
def test_update_dependent_case_owner_still_present(self): dependent_case_state = CaseState(case_id="d1", indices=[]) sync_log = SyncLog(domain="domain", user_id="user", cases_on_phone=[ CaseState(case_id="c1", indices=[ CommCareCaseIndex( identifier="d1-id", referenced_id="d1") ]) ], dependent_cases_on_phone=[dependent_case_state], owner_ids_on_phone=['user1']) xform_id = uuid.uuid4().hex xform = XFormInstance(_id=xform_id) form_actions = [ CommCareCaseAction(action_type=CASE_ACTION_UPDATE, updated_known_properties={'owner_id': 'user2'}) ] with patch.object(CommCareCase, 'get_actions_for_form', return_value=form_actions): parent_case = CommCareCase(_id='d1') # before this test was added, the following call raised a ValueError on legacy logs. for log in [ sync_log, SimplifiedSyncLog.from_other_format(sync_log) ]: log.update_phone_lists(xform, [parent_case]) self.assertIn(dependent_case_state, log.test_only_get_dependent_cases_on_phone())
def test_indices(self): parents = ['catelyn', 'ned', 'cersei', 'jaimie'] index_structure = { 'bran': [ {'identifier': 'mom', 'referenced_id': 'catelyn'}, {'identifier': 'dad', 'referenced_id': 'ned'}, ], 'myrcella': [ {'identifier': 'mom', 'referenced_id': 'cersei'}, {'identifier': 'dad', 'referenced_id': 'jaimie'}, ] } sync_log = SyncLog( cases_on_phone=[ CaseState(case_id='bran', indices=[ CommCareCaseIndex(**args) for args in index_structure['bran'] ]), CaseState(case_id='myrcella', indices=[ CommCareCaseIndex(**args) for args in index_structure['myrcella'] ]) ], dependent_cases_on_phone=[ CaseState(case_id=parent) for parent in parents ] ) migrated = SimplifiedSyncLog.from_other_format(sync_log) for case_id, indices in index_structure.items(): self.assertTrue(case_id in migrated.index_tree.indices) for index in indices: self.assertEqual(index['referenced_id'], migrated.index_tree.indices[case_id][index['identifier']]) for parent in parents: self.assertTrue(parent in migrated.case_ids_on_phone) self.assertTrue(parent in migrated.dependent_case_ids_on_phone)
def _get_case_payload(self, response, user, last_sync, synclog): sync_operation = user.get_case_updates(last_sync) synclog.cases_on_phone = [ CaseState.from_case(c) for c in sync_operation.actual_owned_cases ] synclog.dependent_cases_on_phone = [ CaseState.from_case(c) for c in sync_operation.actual_extended_cases ] synclog.save(**get_safe_write_kwargs()) # case blocks case_xml_elements = ( xml.get_case_element(op.case, op.required_updates, self.version) for op in sync_operation.actual_cases_to_sync ) for case_elem in case_xml_elements: response.append(case_elem) # commtrack balance sections case_state_list = [CaseState.from_case(op.case) for op in sync_operation.actual_cases_to_sync] commtrack_elements = self.get_stock_payload(case_state_list) for ct_elem in commtrack_elements: response.append(ct_elem) return response
def test_cases_in_footprint(self): log = SyncLog(cases_on_phone=[CaseState(case_id="c1", indices=[]), CaseState(case_id="c2", indices=[])]) self.assertEqual(2, len(log.get_footprint_of_cases_on_phone())) log.cases_on_phone.append(CaseState(case_id="c3", indices=[])) self.assertEqual(3, len(log.get_footprint_of_cases_on_phone()))
def test_update_dependent_case(self): sync_log = SyncLog( cases_on_phone=[ CaseState( case_id='bran', indices=[ CommCareCaseIndex(identifier='legs', referenced_id='hodor') ], ), ], dependent_cases_on_phone=[CaseState(case_id='hodor')], user_id="someuser") xform_id = uuid.uuid4().hex xform = XFormInstance(_id=xform_id) form_actions = [CommCareCaseAction(action_type=CASE_ACTION_UPDATE, )] with patch.object(CommCareCase, 'get_actions_for_form', return_value=form_actions): parent_case = CommCareCase(_id='hodor') # before this test was added, the following call raised a SyncLogAssertionError on legacy logs. # this test just ensures it doesn't still do that. for log in [ sync_log, SimplifiedSyncLog.from_other_format(sync_log) ]: log.update_phone_lists(xform, [parent_case])
def get_payload(self): user = self.user last_sync = self.sync_log self.validate() cached_payload = self.get_cached_payload() if cached_payload: return cached_payload sync_operation = user.get_case_updates(last_sync) case_xml_elements = [ xml.get_case_element(op.case, op.required_updates, self.version) for op in sync_operation.actual_cases_to_sync ] commtrack_elements = self.get_stock_payload(sync_operation) last_seq = str(get_db().info()["update_seq"]) # create a sync log for this previous_log_id = last_sync.get_id if last_sync else None synclog = SyncLog( user_id=user.user_id, last_seq=last_seq, owner_ids_on_phone=user.get_owner_ids(), date=datetime.utcnow(), previous_log_id=previous_log_id, cases_on_phone=[CaseState.from_case(c) for c in sync_operation.actual_owned_cases], dependent_cases_on_phone=[CaseState.from_case(c) for c in sync_operation.actual_extended_cases], ) synclog.save(**get_safe_write_kwargs()) # start with standard response response = get_response_element( "Successfully restored account %s!" % user.username, ResponseNature.OTA_RESTORE_SUCCESS ) # add sync token info response.append(xml.get_sync_element(synclog.get_id)) # registration block response.append(xml.get_registration_element(user)) # fixture block for fixture in generator.get_fixtures(user, self.version, last_sync): response.append(fixture) # case blocks for case_elem in case_xml_elements: response.append(case_elem) for ct_elem in commtrack_elements: response.append(ct_elem) if self.items: response.attrib["items"] = "%d" % len(response.getchildren()) resp = xml.tostring(response) self.set_cached_payload_if_enabled(resp) return resp
def test_purge_on_migrate(self): sync_log = SyncLog( cases_on_phone=[ CaseState(case_id='robert'), CaseState(case_id='cersei'), ], dependent_cases_on_phone=[CaseState(case_id='gendry')]) migrated = SimplifiedSyncLog.from_other_format(sync_log) self.assertTrue('gendry' not in migrated.case_ids_on_phone) self.assertEqual(sync_log.get_state_hash(), migrated.get_state_hash())
def test_properties_deleted(self): sync_log = SyncLog( cases_on_phone=[CaseState(case_id='nymeria')], dependent_cases_on_phone=[CaseState(case_id='lady')], ) self.assertTrue(hasattr(sync_log, 'cases_on_phone')) self.assertTrue(hasattr(sync_log, 'dependent_cases_on_phone')) migrated = SimplifiedSyncLog.from_other_format(sync_log) self.assertFalse(hasattr(migrated, 'cases_on_phone')) self.assertFalse(hasattr(migrated, 'dependent_cases_on_phone'))
def test_dependent_cases(self): log = SyncLog(cases_on_phone=[CaseState(case_id="c1", indices=[CommCareCaseIndex(identifier="d1-id", referenced_id="d1")])], dependent_cases_on_phone=[CaseState(case_id="d1", indices=[]), CaseState(case_id="d2", indices=[])]) # d1 counts because it's referenced, d2 doesn't self.assertEqual(2, len(log.get_footprint_of_cases_on_phone())) self.assertTrue("d1" in log.get_footprint_of_cases_on_phone()) self.assertFalse("d2" in log.get_footprint_of_cases_on_phone())
def test_dependent_cases_on_phone(self): sync_log = SyncLog( cases_on_phone=[ CaseState( case_id='bran', indices=[CommCareCaseIndex(identifier='legs', referenced_id='hodor')], ), ], dependent_cases_on_phone=[CaseState(case_id='hodor')] ) migrated = SimplifiedSyncLog.from_other_format(sync_log) self.assertTrue('bran' in migrated.case_ids_on_phone) self.assertTrue('hodor' in migrated.case_ids_on_phone) self.assertTrue('hodor' in migrated.dependent_case_ids_on_phone)
def test_update_dependent_case_owner_still_present(self): sync_log = SimplifiedSyncLog( domain="domain", case_ids_on_phone={'c1', 'd1'}, dependent_case_ids_on_phone={'d1'}, index_tree=IndexTree(indices={'c1': { 'd1-id': 'd1' }}), user_id="user", owner_ids_on_phone={'user1'}) dependent_case_state = CaseState(case_id="d1", indices=[]) xform_id = uuid.uuid4().hex xform = XFormInstance(_id=xform_id) form_actions = [ CommCareCaseAction(action_type=CASE_ACTION_UPDATE, updated_known_properties={'owner_id': 'user2'}) ] with patch.object(CommCareCase, 'get_actions_for_form', return_value=form_actions): parent_case = CommCareCase(_id='d1') # before this test was added, the following call raised a ValueError on legacy logs. sync_log.update_phone_lists(xform, [parent_case]) self.assertIn(dependent_case_state, sync_log.test_only_get_dependent_cases_on_phone())
def test_cases_on_phone(self): case_ids = ['nymeria', 'lady'] sync_log = SyncLog(cases_on_phone=[ CaseState(case_id=case_id) for case_id in case_ids ], ) migrated = SimplifiedSyncLog.from_other_format(sync_log) for case_id in case_ids: self.assertTrue(case_id in migrated.case_ids_on_phone) self.assertFalse(case_id in migrated.dependent_case_ids_on_phone)
def update_relevant_cases(self, cases): new_cases = [] for case in cases: state = CaseState.from_case(case) if state.case_id not in self.actual_relevant_cases_dict: self.actual_relevant_cases_dict[state.case_id] = state new_cases.append(case) return new_cases
def generate_restore_payload(user, restore_id="", version="1.0", state_hash=""): """ Gets an XML payload suitable for OTA restore. If you need to do something other than find all cases matching user_id = user.user_id then you have to pass in a user object that overrides the get_case_updates() method. It should match the same signature as models.user.get_case_updates(): user: who the payload is for. must implement get_case_updates restore_id: sync token version: the CommCare version returns: the xml payload of the sync operation """ check_version(version) last_sync = None if restore_id: try: last_sync = SyncLog.get(restore_id) except Exception: logging.error("Request for bad sync log %s by %s, ignoring..." % (restore_id, user)) if last_sync and state_hash: parsed_hash = CaseStateHash.parse(state_hash) if last_sync.get_state_hash() != parsed_hash: raise BadStateException(expected=last_sync.get_state_hash(), actual=parsed_hash, case_ids=last_sync.get_footprint_of_cases_on_phone()) sync_operation = user.get_case_updates(last_sync) case_xml_elements = [xml.get_case_element(op.case, op.required_updates, version) \ for op in sync_operation.actual_cases_to_sync] last_seq = get_db().info()["update_seq"] # create a sync log for this previous_log_id = last_sync.get_id if last_sync else None synclog = SyncLog(user_id=user.user_id, last_seq=last_seq, owner_ids_on_phone=user.get_owner_ids(), date=datetime.utcnow(), previous_log_id=previous_log_id, cases_on_phone=[CaseState.from_case(c) for c in \ sync_operation.actual_owned_cases], dependent_cases_on_phone=[CaseState.from_case(c) for c in \ sync_operation.actual_extended_cases]) synclog.save() # start with standard response response = get_response_element( "Successfully restored account %s!" % user.username, ResponseNature.OTA_RESTORE_SUCCESS) # add sync token info response.append(xml.get_sync_element(synclog.get_id)) # registration block response.append(xml.get_registration_element(user)) # fixture block for fixture in generator.get_fixtures(user, version, last_sync): response.append(fixture) # case blocks for case_elem in case_xml_elements: response.append(case_elem) return xml.tostring(response)
return cached_payload sync_operation = user.get_case_updates(last_sync) case_xml_elements = [xml.get_case_element(op.case, op.required_updates, self.version) for op in sync_operation.actual_cases_to_sync] commtrack_elements = self.get_stock_payload(sync_operation) last_seq = str(get_db().info()["update_seq"]) # create a sync log for this previous_log_id = last_sync.get_id if last_sync else None synclog = SyncLog(user_id=user.user_id, last_seq=last_seq, owner_ids_on_phone=user.get_owner_ids(), date=datetime.utcnow(), previous_log_id=previous_log_id, cases_on_phone=[CaseState.from_case(c) for c in \ sync_operation.actual_owned_cases], dependent_cases_on_phone=[CaseState.from_case(c) for c in \ sync_operation.actual_extended_cases]) synclog.save(**get_safe_write_kwargs()) # start with standard response response = get_response_element( "Successfully restored account %s!" % user.username, ResponseNature.OTA_RESTORE_SUCCESS) # add sync token info response.append(xml.get_sync_element(synclog.get_id)) # registration block response.append(xml.get_registration_element(user)) # fixture block
def update_synced_cases(self, case_updates): self.all_synced_cases_dict.update( {update.case.case_id: CaseState.from_case(update.case) for update in case_updates} )
def update_owned_cases(self, cases): self.actual_owned_cases_dict.update( {case['_id']: CaseState.from_case(case) for case in cases} )