def testOtherUserUpdatesIndex(self): # create a parent and child case (with index) from one user parent_id = "other_updates_index_parent" case_id = "other_updates_index_child" self._createCaseStubs([parent_id]) child = CaseBlock( create=True, case_id=case_id, user_id=USER_ID, owner_id=USER_ID, version=V2, index={'mother': ('mother', parent_id)} ).as_xml() self._postFakeWithSyncToken(child, self.sync_log.get_id) assert_user_doesnt_have_case(self, self.user, parent_id, restore_id=self.sync_log.get_id) assert_user_doesnt_have_case(self, self.user, case_id, restore_id=self.sync_log.get_id) # assign the parent case away from same user parent_update = CaseBlock( create=False, case_id=parent_id, user_id=USER_ID, owner_id=OTHER_USER_ID, update={"greeting": "hello"}, version=V2).as_xml() self._postFakeWithSyncToken(parent_update, self.sync_log.get_id) self.sync_log = SyncLog.get(self.sync_log.get_id) # these tests added to debug another issue revealed by this test self.assertTrue(self.sync_log.phone_has_case(case_id)) self.assertTrue(self.sync_log.phone_has_dependent_case(parent_id)) self.assertTrue(self.sync_log.phone_is_holding_case(case_id)) self.assertTrue(self.sync_log.phone_is_holding_case(parent_id)) # original user syncs again # make sure there are no new changes assert_user_doesnt_have_case(self, self.user, parent_id, restore_id=self.sync_log.get_id, purge_restore_cache=True) assert_user_doesnt_have_case(self, self.user, case_id, restore_id=self.sync_log.get_id) assert_user_has_case(self, self.other_user, parent_id, restore_id=self.other_sync_log.get_id, purge_restore_cache=True) # update the parent case from another user self.other_sync_log = SyncLog.last_for_user(OTHER_USER_ID) other_parent_update = CaseBlock( create=False, case_id=parent_id, user_id=OTHER_USER_ID, update={"greeting2": "hi"}, version=V2 ).as_xml() self._postFakeWithSyncToken(other_parent_update, self.other_sync_log.get_id) # make sure the indexed case syncs again self.sync_log = SyncLog.last_for_user(USER_ID) assert_user_has_case(self, self.user, parent_id, restore_id=self.sync_log.get_id, purge_restore_cache=True)
def testMultiUserEdits(self): # create a case from one user case_id = "multi_user_edits" self._createCaseStubs([case_id], owner_id=SHARED_ID) # both users syncs generate_restore_payload(self.user) generate_restore_payload(self.other_user) self.sync_log = SyncLog.last_for_user(USER_ID) self.other_sync_log = SyncLog.last_for_user(OTHER_USER_ID) # update case from same user my_change = CaseBlock( create=False, case_id=case_id, user_id=USER_ID, version=V2, update={'greeting': 'hello'} ).as_xml() self._postFakeWithSyncToken( my_change, self.sync_log.get_id ) # update from another user their_change = CaseBlock( create=False, case_id=case_id, user_id=USER_ID, version=V2, update={'greeting_2': 'hello'} ).as_xml() self._postFakeWithSyncToken( their_change, self.other_sync_log.get_id ) # original user syncs again # make sure updates both appear (and merge?) joint_change = CaseBlock( create=False, case_id=case_id, user_id=USER_ID, version=V2, update={ 'greeting': 'hello', 'greeting_2': 'hello' }, owner_id=SHARED_ID, case_name='', case_type='mother', ).as_xml() check_user_has_case(self, self.user, joint_change, restore_id=self.sync_log.get_id, version=V2) check_user_has_case(self, self.other_user, joint_change, restore_id=self.other_sync_log.get_id, version=V2)
def testOtherUserEdits(self): # create a case by one user case_id = "other_user_edits" self._createCaseStubs([case_id], owner_id=SHARED_ID) # sync to the other's phone to be able to edit check_user_has_case(self, self.other_user, CaseBlock(case_id=case_id, version=V2).as_xml(), should_have=True, line_by_line=False, restore_id=self.other_sync_log.get_id, version=V2) latest_sync = SyncLog.last_for_user(OTHER_USER_ID) # update from another self._postFakeWithSyncToken( CaseBlock(create=False, case_id=case_id, user_id=OTHER_USER_ID, version=V2, update={'greeting': "Hello!"} ).as_xml(), latest_sync.get_id) # original user syncs again # make sure updates take updated_case = CaseBlock(create=False, case_id=case_id, user_id=USER_ID, version=V2, update={'greeting': "Hello!"}).as_xml() match = check_user_has_case(self, self.user, updated_case, should_have=True, line_by_line=False, restore_id=self.sync_log.get_id, version=V2) self.assertTrue("Hello!" in ElementTree.tostring(match))
def testOtherUserAddsIndex(self): # create a case from one user case_id = "other_user_adds_index" self._createCaseStubs([case_id], owner_id=SHARED_ID) # sync to the other's phone to be able to edit check_user_has_case(self, self.other_user, CaseBlock(case_id=case_id, version=V2).as_xml(), should_have=True, line_by_line=False, restore_id=self.other_sync_log.get_id, version=V2) latest_sync = SyncLog.last_for_user(OTHER_USER_ID) mother_id = "other_user_adds_index_mother" # parent case parent_case = CaseBlock( create=True, case_id=mother_id, user_id=OTHER_USER_ID, case_type=PARENT_TYPE, version=V2, ).as_xml() self._postFakeWithSyncToken( parent_case, latest_sync.get_id ) # the original user should not get the parent case check_user_has_case(self, self.user, parent_case, should_have=False, restore_id=self.sync_log.get_id, version=V2) # update the original case from another, adding an indexed case self._postFakeWithSyncToken( CaseBlock( create=False, case_id=case_id, user_id=OTHER_USER_ID, owner_id=USER_ID, version=V2, index={'mother': ('mother', mother_id)} ).as_xml(), latest_sync.get_id ) # original user syncs again # make sure index updates take and indexed case also syncs expected_parent_case = CaseBlock( create=True, case_id=mother_id, user_id=OTHER_USER_ID, case_type=PARENT_TYPE, owner_id=OTHER_USER_ID, version=V2, ).as_xml() check_user_has_case(self, self.user, expected_parent_case, restore_id=self.sync_log.get_id, version=V2) orig = check_user_has_case(self, self.user, CaseBlock(case_id=case_id, version=V2).as_xml(), line_by_line=False, restore_id=self.sync_log.get_id, version=V2) self.assertTrue("index" in ElementTree.tostring(orig))
def testOtherUserUpdatesUnowned(self): # create a case from one user and assign ownership elsewhere case_id = "other_user_updates_unowned" self._createCaseStubs([case_id], owner_id=OTHER_USER_ID) # sync and update from another user assert_user_has_case(self, self.other_user, case_id, restore_id=self.other_sync_log.get_id) self.other_sync_log = SyncLog.last_for_user(OTHER_USER_ID) update = CaseBlock(create=False, case_id=case_id, user_id=OTHER_USER_ID, version=V2, update={ 'greeting': 'hello' }).as_xml() self._postFakeWithSyncToken(update, self.other_sync_log.get_id) # original user syncs again # make sure there are no new changes assert_user_doesnt_have_case(self, self.user, case_id, restore_id=self.sync_log.get_id)
def testOtherUserUpdatesUnowned(self): # create a case from one user and assign ownership elsewhere case_id = "other_user_updates_unowned" self._createCaseStubs([case_id], owner_id=OTHER_USER_ID) # sync and update from another user check_user_has_case(self, self.other_user, CaseBlock(case_id=case_id, version=V2).as_xml(), should_have=True, line_by_line=False, restore_id=self.other_sync_log.get_id, version=V2) self.other_sync_log = SyncLog.last_for_user(OTHER_USER_ID) update = CaseBlock( create=False, case_id=case_id, user_id=OTHER_USER_ID, version=V2, update={'greeting': 'hello'} ).as_xml() self._postFakeWithSyncToken( update, self.other_sync_log.get_id ) # original user syncs again # make sure there are no new changes # sync and update from another user check_user_has_case(self, self.user, update, should_have=False, restore_id=self.sync_log.get_id, version=V2)
def testOtherUserEdits(self): # create a case by one user case_id = "other_user_edits" self._createCaseStubs([case_id], owner_id=SHARED_ID) # sync to the other's phone to be able to edit assert_user_has_case(self, self.other_user, case_id, restore_id=self.other_sync_log.get_id) latest_sync = SyncLog.last_for_user(OTHER_USER_ID) # update from another self._postFakeWithSyncToken( CaseBlock(create=False, case_id=case_id, user_id=OTHER_USER_ID, version=V2, update={ 'greeting': "Hello!" }).as_xml(), latest_sync.get_id) # original user syncs again # make sure updates take match = assert_user_has_case(self, self.user, case_id, restore_id=self.sync_log.get_id) self.assertTrue("Hello!" in ElementTree.tostring(match))
def rows(self): rows = [] user_ids = map(lambda user: user.user_id, self.users) user_xform_dicts_map = get_last_form_submissions_by_user( self.domain, user_ids, self.selected_app_id) for user in self.users: xform_dict = last_seen = last_sync = app_name = None if user_xform_dicts_map.get(user.user_id): xform_dict = user_xform_dicts_map[user.user_id][0] if xform_dict: last_seen = string_to_utc_datetime( xform_dict.get('received_on')) if xform_dict.get('app_id'): try: app = get_app(self.domain, xform_dict.get('app_id')) except ResourceNotFound: pass else: app_name = app.name else: app_name = get_meta_appversion_text( xform_dict['form']['meta']) app_version_info = get_app_version_info( self.domain, xform_dict.get('build_id'), xform_dict.get('version'), xform_dict['form']['meta'], ) build_html = _build_html(app_version_info) commcare_version = ('CommCare {}'.format( app_version_info.commcare_version) if app_version_info.commcare_version else _("Unknown CommCare Version")) commcare_version_html = mark_safe( '<span class="label label-info">{}</span>'.format( commcare_version)) app_name = app_name or _("Unknown App") app_name = format_html(u'{} {} {}', app_name, mark_safe(build_html), commcare_version_html) if app_name is None and self.selected_app_id: continue last_sync_log = SyncLog.last_for_user(user.user_id) if last_sync_log: last_sync = last_sync_log.date rows.append([ user.username_in_report, _fmt_date(last_seen), _fmt_date(last_sync), app_name or "---" ]) return rows
def testOtherUserAddsIndex(self): time = datetime.utcnow() # create a case from one user case_id = "other_user_adds_index" self._createCaseStubs([case_id], owner_id=SHARED_ID) # sync to the other's phone to be able to edit assert_user_has_case(self, self.other_user, case_id, restore_id=self.other_sync_log.get_id) latest_sync = SyncLog.last_for_user(OTHER_USER_ID) mother_id = "other_user_adds_index_mother" parent_case = CaseBlock( create=True, date_modified=time, case_id=mother_id, user_id=OTHER_USER_ID, case_type=PARENT_TYPE, version=V2, ).as_xml(format_datetime=json_format_datetime) self._postFakeWithSyncToken( parent_case, latest_sync.get_id ) # the original user should not get the parent case assert_user_doesnt_have_case(self, self.user, mother_id, restore_id=self.sync_log.get_id) # update the original case from another, adding an indexed case self._postFakeWithSyncToken( CaseBlock( create=False, case_id=case_id, user_id=OTHER_USER_ID, owner_id=USER_ID, version=V2, index={'mother': ('mother', mother_id)} ).as_xml(format_datetime=json_format_datetime), latest_sync.get_id ) # original user syncs again # make sure index updates take and indexed case also syncs expected_parent_case = CaseBlock( create=True, date_modified=time, case_id=mother_id, user_id=OTHER_USER_ID, case_type=PARENT_TYPE, owner_id=OTHER_USER_ID, version=V2, ).as_xml(format_datetime=json_format_datetime) check_user_has_case(self, self.user, expected_parent_case, restore_id=self.sync_log.get_id, version=V2, purge_restore_cache=True) _, orig = assert_user_has_case(self, self.user, case_id, restore_id=self.sync_log.get_id) self.assertTrue("index" in ElementTree.tostring(orig))
def rows(self): rows = [] user_ids = map(lambda user: user.user_id, self.users) user_xform_dicts_map = get_last_form_submissions_by_user(self.domain, user_ids, self.selected_app_id) for user in self.users: xform_dict = last_seen = last_sync = app_name = None app_version_info_from_form = app_version_info_from_sync = None if user_xform_dicts_map.get(user.user_id): xform_dict = user_xform_dicts_map[user.user_id][0] if xform_dict: last_seen = string_to_utc_datetime(xform_dict.get('received_on')) if xform_dict.get('app_id'): try: app = get_app(self.domain, xform_dict.get('app_id')) except ResourceNotFound: pass else: app_name = app.name else: app_name = get_meta_appversion_text(xform_dict['form']['meta']) app_version_info_from_form = get_app_version_info( self.domain, xform_dict.get('build_id'), xform_dict.get('version'), xform_dict['form']['meta'], ) if app_name is None and self.selected_app_id: continue last_sync_log = SyncLog.last_for_user(user.user_id) if last_sync_log: last_sync = last_sync_log.date if last_sync_log.build_id: build_version = get_version_from_build_id(self.domain, last_sync_log.build_id) app_version_info_from_sync = AppVersionInfo( build_version, app_version_info_from_form.commcare_version if app_version_info_from_form else None, BuildVersionSource.BUILD_ID ) app_version_info_to_use = _choose_latest_version( app_version_info_from_sync, app_version_info_from_form, ) commcare_version = _get_commcare_version(app_version_info_to_use) build_version = _get_build_version(app_version_info_to_use) rows.append([ user.username_in_report, _fmt_date(last_seen), _fmt_date(last_sync), app_name or "---", build_version, commcare_version ]) return rows
def rows(self): rows = [] selected_app = self.request_params.get(SelectApplicationFilter.slug, None) user_ids = map(lambda user: user.user_id, self.users) user_xform_dicts_map = get_last_form_submissions_by_user(self.domain, user_ids, selected_app) for user in self.users: xform_dict = last_seen = last_sync = app_name = None if user_xform_dicts_map.get(user.user_id): xform_dict = user_xform_dicts_map[user.user_id][0] if xform_dict: last_seen = string_to_utc_datetime(xform_dict.get('received_on')) if xform_dict.get('app_id'): try: app = get_app(self.domain, xform_dict.get('app_id')) except ResourceNotFound: pass else: app_name = app.name else: app_name = get_meta_appversion_text(xform_dict['form']['meta']) app_version_info = get_app_version_info( self.domain, xform_dict.get('build_id'), xform_dict.get('version'), xform_dict['form']['meta'], ) build_html = _build_html(app_version_info) commcare_version = ( 'CommCare {}'.format(app_version_info.commcare_version) if app_version_info.commcare_version else _("Unknown CommCare Version") ) commcare_version_html = mark_safe('<span class="label label-info">{}</span>'.format( commcare_version) ) app_name = app_name or _("Unknown App") app_name = format_html( u'{} {} {}', app_name, mark_safe(build_html), commcare_version_html ) if app_name is None and selected_app: continue last_sync_log = SyncLog.last_for_user(user.user_id) if last_sync_log: last_sync = last_sync_log.date rows.append( [user.username_in_report, _fmt_date(last_seen), _fmt_date(last_sync), app_name or "---"] ) return rows
def rows(self): rows = [] selected_app = self.request_params.get(SelectApplicationFilter.slug, '') for user in self.users: last_seen = last_sync = app_name = None key = make_form_couch_key(self.domain, user_id=user.user_id, app_id=selected_app or None) xform = XFormInstance.view( "reports_forms/all_forms", startkey=key + [{}], endkey=key, include_docs=True, descending=True, reduce=False, limit=1, ).first() if xform: last_seen = xform.received_on build_version, build_version_source = get_build_version(xform) if xform.app_id: try: app = get_app(self.domain, xform.app_id) except ResourceNotFound: pass else: app_name = app.name else: app_name = get_meta_appversion_text(xform) build_html = _build_html(build_version, build_version_source) app_name = app_name or _("Unknown App") app_name = format_html( u'{} {}', app_name, mark_safe(build_html), ) if app_name is None and selected_app: continue last_sync_log = SyncLog.last_for_user(user.user_id) if last_sync_log: last_sync = last_sync_log.date rows.append([ user.username_in_report, _fmt_date(last_seen), _fmt_date(last_sync), app_name or "---" ]) return rows
def setUp(self): super(MultiUserSyncTest, self).setUp() # the other user is an "owner" of the original users cases as well, # for convenience self.other_user = User(user_id=OTHER_USER_ID, username="******", password="******", date_joined=datetime(2011, 6, 9), additional_owner_ids=[SHARED_ID]) # this creates the initial blank sync token in the database generate_restore_payload(self.other_user) self.other_sync_log = SyncLog.last_for_user(OTHER_USER_ID) self.assertTrue(SHARED_ID in self.other_sync_log.owner_ids_on_phone) self.assertTrue(OTHER_USER_ID in self.other_sync_log.owner_ids_on_phone) self.user.additional_owner_ids = [SHARED_ID] generate_restore_payload(self.user) self.sync_log = SyncLog.last_for_user(self.user.user_id) self.assertTrue(SHARED_ID in self.sync_log.owner_ids_on_phone) self.assertTrue(USER_ID in self.sync_log.owner_ids_on_phone)
def testComplicatedGatesBug(self): # found this bug in the wild, used the real (test) forms to fix it # just running through this test used to fail hard, even though there # are no asserts self.assertEqual(0, len(CommCareCase.view("case/by_user", reduce=False).all())) folder_path = os.path.join("bugs", "dependent_case_conflicts") files = ["reg1.xml", "reg2.xml", "cf.xml", "close.xml"] for f in files: form = self._postWithSyncToken(os.path.join(folder_path, f), self.sync_log.get_id) form = XFormInstance.get(form.get_id) self.assertFalse(hasattr(form, "problem")) generate_restore_payload(self.user, version="2.0") self.sync_log = SyncLog.last_for_user(self.user.user_id)
def rows(self): rows = [] selected_app = self.request_params.get(SelectApplicationFilter.slug, '') for user in self.users: last_seen = last_sync = app_name = None key = make_form_couch_key(self.domain, user_id=user.user_id, app_id=selected_app or None) xform = XFormInstance.view( "reports_forms/all_forms", startkey=key+[{}], endkey=key, include_docs=True, descending=True, reduce=False, limit=1, ).first() if xform: last_seen = xform.received_on build_version, build_version_source = get_build_version(xform) if xform.app_id: try: app = get_app(self.domain, xform.app_id) except ResourceNotFound: pass else: app_name = app.name else: app_name = get_meta_appversion_text(xform) build_html = _build_html(build_version, build_version_source) app_name = app_name or _("Unknown App") app_name = format_html( u'{} {}', app_name, mark_safe(build_html), ) if app_name is None and selected_app: continue last_sync_log = SyncLog.last_for_user(user.user_id) if last_sync_log: last_sync = last_sync_log.date rows.append( [user.username_in_report, _fmt_date(last_seen), _fmt_date(last_sync), app_name or "---"] ) return rows
def test_build_id(self): app = Application(domain=self.domain) app.save() config = RestoreConfig( project=self.project, restore_user=self.restore_user, params=RestoreParams(app=app, ), ) config.get_payload() # this generates the sync log sync_log = SyncLog.last_for_user(self.restore_user.user_id) self.assertEqual(self.restore_user.user_id, sync_log.user_id) self.assertEqual(self.restore_user.domain, sync_log.domain) self.assertEqual(app._id, sync_log.build_id) self.addCleanup(app.delete)
def rows(self): rows = [] selected_app = self.request_params.get(SelectApplicationFilter.slug, None) for user in self.users: last_seen = last_sync = app_name = None xform = get_last_form_submission_for_user_for_app( self.domain, user.user_id, selected_app) if xform: last_seen = xform.received_on if xform.app_id: try: app = get_app(self.domain, xform.app_id) except ResourceNotFound: pass else: app_name = app.name else: app_name = get_meta_appversion_text(xform) app_version_info = get_app_version_info(xform) build_html = _build_html(app_version_info) commcare_version = ( 'CommCare {}'.format(app_version_info.commcare_version) if app_version_info.commcare_version else _("Unknown CommCare Version") ) commcare_version_html = mark_safe('<span class="label label-info">{}</span>'.format( commcare_version) ) app_name = app_name or _("Unknown App") app_name = format_html( u'{} {} {}', app_name, mark_safe(build_html), commcare_version_html ) if app_name is None and selected_app: continue last_sync_log = SyncLog.last_for_user(user.user_id) if last_sync_log: last_sync = last_sync_log.date rows.append( [user.username_in_report, _fmt_date(last_seen), _fmt_date(last_sync), app_name or "---"] ) return rows
def test_build_id(self): app = Application(domain=self.domain) app.save() config = RestoreConfig( project=self.project, restore_user=self.restore_user, params=RestoreParams( app=app, ), ) config.get_payload() # this generates the sync log sync_log = SyncLog.last_for_user(self.restore_user.user_id) self.assertEqual(self.restore_user.user_id, sync_log.user_id) self.assertEqual(self.restore_user.domain, sync_log.domain) self.assertEqual(app._id, sync_log.build_id) self.addCleanup(app.delete)
def rows(self): rows = [] selected_app = self.request_params.get(SelectApplicationFilter.slug, None) for user in self.users: last_seen = last_sync = app_name = None xform = get_last_form_submission_for_user_for_app( self.domain, user.user_id, selected_app) if xform: last_seen = xform.received_on build_version, build_version_source = get_build_version(xform) if xform.app_id: try: app = get_app(self.domain, xform.app_id) except ResourceNotFound: pass else: app_name = app.name else: app_name = get_meta_appversion_text(xform) build_html = _build_html(build_version, build_version_source) app_name = app_name or _("Unknown App") app_name = format_html( u'{} {}', app_name, mark_safe(build_html), ) if app_name is None and selected_app: continue last_sync_log = SyncLog.last_for_user(user.user_id) if last_sync_log: last_sync = last_sync_log.date rows.append( [user.username_in_report, _fmt_date(last_seen), _fmt_date(last_sync), app_name or "---"] ) return rows
def testOtherUserCloses(self): # create a case from one user case_id = "other_user_closes" self._createCaseStubs([case_id], owner_id=SHARED_ID) # sync then close case from another user generate_restore_payload(self.other_user) self.other_sync_log = SyncLog.last_for_user(OTHER_USER_ID) close_block = CaseBlock( create=False, case_id=case_id, user_id=USER_ID, version=V2, close=True ).as_xml() self._postFakeWithSyncToken( close_block, self.other_sync_log.get_id ) # original user syncs again # make sure close block appears check_user_has_case(self, self.user, close_block, line_by_line=False, restore_id=self.sync_log.get_id, version=V2)
def rows(self): rows = [] selected_app = self.request_params.get(SelectApplicationFilter.slug, '') def _fmt_date(date): def _timedelta_class(delta): if delta > timedelta(days=7): return "label label-important" elif delta > timedelta(days=3): return "label label-warning" else: return "label label-success" if not date: return self.table_cell( -1, '<span class="label">{0}</span>'.format(_("Never"))) else: return self.table_cell( date.toordinal(), '<span class="{cls}">{text}</span>'.format( cls=_timedelta_class(datetime.utcnow() - date), text=naturaltime(date), )) for user in self.users: last_seen = last_sync = app_name = None key = make_form_couch_key(self.domain, user_id=user.user_id, app_id=selected_app or None) xform = XFormInstance.view( "reports_forms/all_forms", startkey=key + [{}], endkey=key, include_docs=True, descending=True, reduce=False, limit=1, ).first() if xform: last_seen = xform.received_on build_version, build_version_source = get_build_version(xform) if xform.app_id: try: app = get_app(self.domain, xform.app_id) except ResourceNotFound: pass else: app_name = app.name else: app_name = get_meta_appversion_text(xform) build_html = _build_html(build_version, build_version_source) app_name = app_name or _("Unknown App") app_name = format_html( u'{} {}', app_name, mark_safe(build_html), ) if app_name is None and selected_app: continue last_sync_log = SyncLog.last_for_user(user.user_id) if last_sync_log: last_sync = last_sync_log.date rows.append([ user.username_in_report, _fmt_date(last_seen), _fmt_date(last_sync), app_name or "---" ]) return rows
def test_basic_properties(self): # kick off a restore to generate the sync log generate_restore_payload(self.project, self.restore_user, items=True) sync_log = SyncLog.last_for_user(self.restore_user.user_id) self.assertEqual(self.restore_user.user_id, sync_log.user_id) self.assertEqual(self.restore_user.domain, sync_log.domain)
def testOtherUserReassignsIndexed(self): # create a parent and child case (with index) from one user parent_id = "other_reassigns_index_parent" case_id = "other_reassigns_index_child" self._createCaseStubs([parent_id]) child = CaseBlock( create=True, case_id=case_id, user_id=USER_ID, owner_id=SHARED_ID, version=V2, index={'mother': ('mother', parent_id)} ).as_xml() self._postFakeWithSyncToken(child, self.sync_log.get_id) # assign the parent case away from the same user parent_update = CaseBlock( create=False, case_id=parent_id, user_id=USER_ID, owner_id=OTHER_USER_ID, update={"greeting": "hello"}, version=V2).as_xml() self._postFakeWithSyncToken(parent_update, self.sync_log.get_id) # sync cases to second user self.other_sync_log = synclog_from_restore_payload(generate_restore_payload(self.other_user)) # change the child's owner from another user child_reassignment = CaseBlock( create=False, case_id=case_id, user_id=OTHER_USER_ID, owner_id=OTHER_USER_ID, version=V2, update={"childgreeting": "hi!"}, ).as_xml() self._postFakeWithSyncToken(child_reassignment, self.other_sync_log.get_id) # also change the parent from the second user other_parent_update = CaseBlock( create=False, case_id=parent_id, user_id=OTHER_USER_ID, owner_id=OTHER_USER_ID, update={"other_greeting": "something new"}, version=V2).as_xml() self._postFakeWithSyncToken(other_parent_update, self.other_sync_log.get_id) # original user syncs again self.sync_log = SyncLog.last_for_user(self.user.user_id) # both cases should sync to original user with updated ownership / edits assert_user_has_case(self, self.user, case_id, restore_id=self.sync_log.get_id) assert_user_has_case(self, self.user, parent_id, restore_id=self.sync_log.get_id) # Ghetto payload = generate_restore_payload(self.user, self.sync_log.get_id, version=V2) self.assertTrue("something new" in payload) self.assertTrue("hi!" in payload) # change the parent again from the second user other_parent_update = CaseBlock( create=False, case_id=parent_id, user_id=OTHER_USER_ID, owner_id=OTHER_USER_ID, update={"other_greeting": "something different"}, version=V2).as_xml() self._postFakeWithSyncToken(other_parent_update, self.other_sync_log.get_id) # original user syncs again self.sync_log = SyncLog.last_for_user(self.user.user_id) # should be no changes assert_user_doesnt_have_case(self, self.user, case_id, restore_id=self.sync_log.get_id) assert_user_doesnt_have_case(self, self.user, parent_id, restore_id=self.sync_log.get_id) # change the child again from the second user other_child_update = CaseBlock( create=False, case_id=case_id, user_id=OTHER_USER_ID, owner_id=OTHER_USER_ID, version=V2, update={"childgreeting": "hi changed!"}, ).as_xml() self._postFakeWithSyncToken(other_child_update, self.other_sync_log.get_id) # original user syncs again self.sync_log = SyncLog.last_for_user(self.user.user_id) # should be no changes assert_user_doesnt_have_case(self, self.user, case_id, restore_id=self.sync_log.get_id) assert_user_doesnt_have_case(self, self.user, parent_id, restore_id=self.sync_log.get_id) # change owner of child back to orginal user from second user child_reassignment = CaseBlock( create=False, case_id=case_id, user_id=OTHER_USER_ID, owner_id=USER_ID, version=V2 ).as_xml() self._postFakeWithSyncToken(child_reassignment, self.other_sync_log.get_id) # original user syncs again self.sync_log = SyncLog.last_for_user(self.user.user_id) # both cases should now sync assert_user_has_case(self, self.user, case_id, restore_id=self.sync_log.get_id) assert_user_has_case(self, self.user, parent_id, restore_id=self.sync_log.get_id) # ghetto payload = generate_restore_payload(self.user, self.sync_log.get_id, version=V2) self.assertTrue("something different" in payload) self.assertTrue("hi changed!" in payload)
def rows(self): rows = [] selected_app = self.request_params.get(SelectApplicationFilter.slug, '') def _fmt_date(date): def _timedelta_class(delta): if delta > timedelta(days=7): return "label label-important" elif delta > timedelta(days=3): return "label label-warning" else: return "label label-success" if not date: return self.table_cell(-1, '<span class="label">{0}</span>'.format(_("Never"))) else: return self.table_cell(date.toordinal(), '<span class="{cls}">{text}</span>'.format( cls=_timedelta_class(datetime.utcnow() - date), text=naturaltime(date), )) for user in self.users: last_seen = last_sync = app_name = None key = make_form_couch_key(self.domain, user_id=user.user_id, app_id=selected_app or None) xform = XFormInstance.view( "reports_forms/all_forms", startkey=key+[{}], endkey=key, include_docs=True, descending=True, reduce=False, limit=1, ).first() if xform: last_seen = xform.received_on build_version, build_version_source = get_build_version(xform) if xform.app_id: try: app = get_app(self.domain, xform.app_id) except ResourceNotFound: pass else: app_name = app.name else: app_name = get_meta_appversion_text(xform) build_html = _build_html(build_version, build_version_source) app_name = app_name or _("Unknown App") app_name = format_html( u'{} {}', app_name, mark_safe(build_html), ) if app_name is None and selected_app: continue last_sync_log = SyncLog.last_for_user(user.user_id) if last_sync_log: last_sync = last_sync_log.date rows.append( [user.username_in_report, _fmt_date(last_seen), _fmt_date(last_sync), app_name or "---"] ) return rows
def testOtherUserAddsIndex(self): time = datetime.now() # create a case from one user case_id = "other_user_adds_index" self._createCaseStubs([case_id], owner_id=SHARED_ID) # sync to the other's phone to be able to edit assert_user_has_case(self, self.other_user, case_id, restore_id=self.other_sync_log.get_id) latest_sync = SyncLog.last_for_user(OTHER_USER_ID) mother_id = "other_user_adds_index_mother" parent_case = CaseBlock( create=True, date_modified=time, case_id=mother_id, user_id=OTHER_USER_ID, case_type=PARENT_TYPE, version=V2, ).as_xml(format_datetime=json_format_datetime) self._postFakeWithSyncToken(parent_case, latest_sync.get_id) # the original user should not get the parent case assert_user_doesnt_have_case(self, self.user, mother_id, restore_id=self.sync_log.get_id) # update the original case from another, adding an indexed case self._postFakeWithSyncToken( CaseBlock(create=False, case_id=case_id, user_id=OTHER_USER_ID, owner_id=USER_ID, version=V2, index={ 'mother': ('mother', mother_id) }).as_xml(format_datetime=json_format_datetime), latest_sync.get_id) # original user syncs again # make sure index updates take and indexed case also syncs expected_parent_case = CaseBlock( create=True, date_modified=time, case_id=mother_id, user_id=OTHER_USER_ID, case_type=PARENT_TYPE, owner_id=OTHER_USER_ID, version=V2, ).as_xml(format_datetime=json_format_datetime) check_user_has_case(self, self.user, expected_parent_case, restore_id=self.sync_log.get_id, version=V2) orig = assert_user_has_case(self, self.user, case_id, restore_id=self.sync_log.get_id) self.assertTrue("index" in ElementTree.tostring(orig))
def testOtherUserUpdatesIndex(self): # create a parent and child case (with index) from one user parent_id = "other_updates_index_parent" case_id = "other_updates_index_child" self._createCaseStubs([parent_id]) parent = CaseBlock(case_id=parent_id, version=V2).as_xml() child = CaseBlock(create=True, case_id=case_id, user_id=USER_ID, owner_id=USER_ID, version=V2, index={ 'mother': ('mother', parent_id) }).as_xml() self._postFakeWithSyncToken(child, self.sync_log.get_id) assert_user_doesnt_have_case(self, self.user, parent_id, restore_id=self.sync_log.get_id) assert_user_doesnt_have_case(self, self.user, case_id, restore_id=self.sync_log.get_id) # assign the parent case away from same user parent_update = CaseBlock(create=False, case_id=parent_id, user_id=USER_ID, owner_id=OTHER_USER_ID, update={ "greeting": "hello" }, version=V2).as_xml() self._postFakeWithSyncToken(parent_update, self.sync_log.get_id) self.sync_log = SyncLog.get(self.sync_log.get_id) # these tests added to debug another issue revealed by this test self.assertTrue(self.sync_log.phone_has_case(case_id)) self.assertTrue(self.sync_log.phone_has_dependent_case(parent_id)) self.assertTrue(self.sync_log.phone_is_holding_case(case_id)) self.assertTrue(self.sync_log.phone_is_holding_case(parent_id)) # original user syncs again # make sure there are no new changes assert_user_doesnt_have_case(self, self.user, parent_id, restore_id=self.sync_log.get_id) assert_user_doesnt_have_case(self, self.user, case_id, restore_id=self.sync_log.get_id) # update the parent case from another user assert_user_has_case(self, self.other_user, parent_id, restore_id=self.other_sync_log.get_id) self.other_sync_log = SyncLog.last_for_user(OTHER_USER_ID) other_parent_update = CaseBlock(create=False, case_id=parent_id, user_id=OTHER_USER_ID, update={ "greeting2": "hi" }, version=V2).as_xml() self._postFakeWithSyncToken(other_parent_update, self.other_sync_log.get_id) # make sure the indexed case syncs again self.sync_log = SyncLog.last_for_user(USER_ID) assert_user_has_case(self, self.user, parent_id, restore_id=self.sync_log.get_id)
def testOtherUserReassignsIndexed(self): # create a parent and child case (with index) from one user parent_id = "other_reassigns_index_parent" case_id = "other_reassigns_index_child" self._createCaseStubs([parent_id]) child = CaseBlock(create=True, case_id=case_id, user_id=USER_ID, owner_id=SHARED_ID, version=V2, index={ 'mother': ('mother', parent_id) }).as_xml() self._postFakeWithSyncToken(child, self.sync_log.get_id) # assign the parent case away from the same user parent_update = CaseBlock(create=False, case_id=parent_id, user_id=USER_ID, owner_id=OTHER_USER_ID, update={ "greeting": "hello" }, version=V2).as_xml() self._postFakeWithSyncToken(parent_update, self.sync_log.get_id) # sync cases to second user self.other_sync_log = synclog_from_restore_payload( generate_restore_payload(self.other_user)) # change the child's owner from another user child_reassignment = CaseBlock( create=False, case_id=case_id, user_id=OTHER_USER_ID, owner_id=OTHER_USER_ID, version=V2, update={ "childgreeting": "hi!" }, ).as_xml() self._postFakeWithSyncToken(child_reassignment, self.other_sync_log.get_id) # also change the parent from the second user other_parent_update = CaseBlock(create=False, case_id=parent_id, user_id=OTHER_USER_ID, owner_id=OTHER_USER_ID, update={ "other_greeting": "something new" }, version=V2).as_xml() self._postFakeWithSyncToken(other_parent_update, self.other_sync_log.get_id) # original user syncs again self.sync_log = SyncLog.last_for_user(self.user.user_id) # both cases should sync to original user with updated ownership / edits assert_user_has_case(self, self.user, case_id, restore_id=self.sync_log.get_id) assert_user_has_case(self, self.user, parent_id, restore_id=self.sync_log.get_id) # Ghetto payload = generate_restore_payload(self.user, self.sync_log.get_id, version=V2) self.assertTrue("something new" in payload) self.assertTrue("hi!" in payload) # change the parent again from the second user other_parent_update = CaseBlock(create=False, case_id=parent_id, user_id=OTHER_USER_ID, owner_id=OTHER_USER_ID, update={ "other_greeting": "something different" }, version=V2).as_xml() self._postFakeWithSyncToken(other_parent_update, self.other_sync_log.get_id) # original user syncs again self.sync_log = SyncLog.last_for_user(self.user.user_id) # should be no changes assert_user_doesnt_have_case(self, self.user, case_id, restore_id=self.sync_log.get_id) assert_user_doesnt_have_case(self, self.user, parent_id, restore_id=self.sync_log.get_id) # change the child again from the second user other_child_update = CaseBlock( create=False, case_id=case_id, user_id=OTHER_USER_ID, owner_id=OTHER_USER_ID, version=V2, update={ "childgreeting": "hi changed!" }, ).as_xml() self._postFakeWithSyncToken(other_child_update, self.other_sync_log.get_id) # original user syncs again self.sync_log = SyncLog.last_for_user(self.user.user_id) # should be no changes assert_user_doesnt_have_case(self, self.user, case_id, restore_id=self.sync_log.get_id) assert_user_doesnt_have_case(self, self.user, parent_id, restore_id=self.sync_log.get_id) # change owner of child back to orginal user from second user child_reassignment = CaseBlock(create=False, case_id=case_id, user_id=OTHER_USER_ID, owner_id=USER_ID, version=V2).as_xml() self._postFakeWithSyncToken(child_reassignment, self.other_sync_log.get_id) # original user syncs again self.sync_log = SyncLog.last_for_user(self.user.user_id) # both cases should now sync assert_user_has_case(self, self.user, case_id, restore_id=self.sync_log.get_id) assert_user_has_case(self, self.user, parent_id, restore_id=self.sync_log.get_id) # ghetto payload = generate_restore_payload(self.user, self.sync_log.get_id, version=V2) self.assertTrue("something different" in payload) self.assertTrue("hi changed!" in payload)
def test_basic_properties(self): # kick off a restore to generate the sync log generate_restore_payload(self.project, self.restore_user, items=True) sync_log = SyncLog.last_for_user(self.restore_user.user_id) self.assertEqual(self.restore_user.user_id, sync_log.user_id) self.assertEqual(self.restore_user.domain, sync_log.domain)