def QueryFollowerIds(cls, client, viewpoint_id, callback, excl_start_key=None, limit=None): """Query followers belonging to the viewpoint (up to 'limit' total) for the specified 'viewpoint_id'. The query is for followers starting with (but excluding) 'excl_start_key'. The callback is invoked with an array of follower user ids and the last queried key. """ def _OnQueryFollowerKeys(follower_keys): follower_ids = [key.hash_key for key in follower_keys] last_key = follower_ids[-1] if len(follower_ids) > 0 else None callback((follower_ids, last_key)) # Query the viewpoint_id secondary index with excl_start_key & limit. query_expr = ('follower.viewpoint_id={id}', {'id': viewpoint_id}) start_index_key = db_client.DBKey( excl_start_key, viewpoint_id) if excl_start_key is not None else None Follower.IndexQueryKeys(client, query_expr, callback=_OnQueryFollowerKeys, start_index_key=start_index_key, limit=limit)
def _FormatAllAttributes(self, item): """Build list of (column, key, value, pretty_value). We need a list to keep the columns ordered. The interpretation of the 'key' column depends on the beginning of the 'term' column.""" attrs = [] term = item.get('t', None) key = item.get('k', None) data = item.get('d', None) split = term.split(':') table = split[0] key_pretty = key if table == 'co': db_key = Contact._ParseIndexKey(key) key_pretty = self._SortQueryLink('Contact', db_key.hash_key, db_key.range_key) elif table == 'ev': key_pretty = self._EpisodeLink(key) elif table == 'fo': db_key = Follower._ParseIndexKey(key) key_pretty = self._SortQueryLink('Follower', db_key.hash_key, db_key.range_key) elif table == 'id': key_pretty = self._HashQueryLink('Identity', key) elif table == 'vp': key_pretty = self._ViewpointLink(key) attrs.append(('term', 't', term, term)) attrs.append(('key', 'k', key, key_pretty)) attrs.append(('data', 't', data, data)) attrs.append(('_version', '_ve', data, data)) return attrs
def _RunService(callback): """Removes the follower by loading follower relation and deleting it. Removes all notifications related to the viewpoint. """ assert options.options.user_id, 'must specify a user id (--user_id)' assert options.options.viewpoint_id, 'must specify a viewpoint id (--viewpoint_id)' client = DBClient.Instance() def _OnService(response_dict): logging.info('result: %s' % util.ToCanonicalJSON(response_dict, indent=2)) callback() def _OnNotification(n, cb): if n.viewpoint_id == options.options.viewpoint_id: n.name = 'clear_badges' n.sender_id = 494 n.sender_device_id = 2260 n.invalidate = None n.op_id = None n.viewpoint_id = None n.update_seq = None n.viewed_seq = None n.activity_id = None n.badge = 0 print 'resetting notification to clear_badges: %s' % n n.Update(client, cb) else: cb() def _DoneQueryFollowed(): Notification.VisitRange(client, options.options.user_id, None, None, _OnNotification, callback) def _OnQueryFollowed(f, cb): if f.viewpoint_id == options.options.viewpoint_id: print 'deleting: %s' % f f.Delete(client, cb) else: cb() def _OnDeleteFollower(): Followed.VisitRange(client, options.options.user_id, None, None, _OnQueryFollowed, _DoneQueryFollowed) def _OnQueryFollower(f): if f: print 'deleting: %s' % f f.Delete(client, _OnDeleteFollower) else: _OnDeleteFollower() Follower.Query(client, options.options.user_id, options.options.viewpoint_id, None, _OnQueryFollower, must_exist=False)
def _UpdateFollower(follower_id): """Create a new follower of this viewpoint in the database.""" follower = Follower(user_id=follower_id, viewpoint_id=self.viewpoint_id) follower.timestamp = timestamp follower.adding_user_id = adding_user_id follower.viewed_seq = 0 follower.labels = [Follower.CONTRIBUTE] # Create the follower and corresponding Followed record. yield [ gen.Task(follower.Update, client), gen.Task( Followed.UpdateDateUpdated, client, follower_id, self.viewpoint_id, old_timestamp=None, new_timestamp=timestamp, ), ] raise gen.Return(follower)
def VisitFollowerIds(cls, client, viewpoint_id, visitor, callback, consistent_read=False): """Visit all followers of the specified viewpoint and invoke the "visitor" function with each follower id. See VisitIndexKeys for additional detail. """ def _OnVisit(follower_key, visit_callback): visitor(follower_key.hash_key, visit_callback) query_expr = ('follower.viewpoint_id={id}', {'id': viewpoint_id}) Follower.VisitIndexKeys(client, query_expr, _OnVisit, callback, consistent_read=consistent_read)
def CreateNew(cls, client, **vp_dict): """Creates the viewpoint specified by 'vp_dict' and creates a follower relation between the requesting user and the viewpoint with the ADMIN label. The caller is responsible for checking permission to do this, as well as ensuring that the viewpoint does not yet exist (or is just being identically rewritten). Returns a tuple containing the newly created objects: (viewpoint, follower). """ tasks = [] # Create the viewpoint. assert 'viewpoint_id' in vp_dict and 'user_id' in vp_dict and 'timestamp' in vp_dict, vp_dict viewpoint = Viewpoint.CreateFromKeywords(**vp_dict) viewpoint.last_updated = viewpoint.timestamp viewpoint.update_seq = 0 tasks.append(gen.Task(viewpoint.Update, client)) # Create the follower and give all permissions, since it's the owner. foll_dict = { 'user_id': viewpoint.user_id, 'viewpoint_id': viewpoint.viewpoint_id, 'timestamp': viewpoint.timestamp, 'labels': list(Follower.PERMISSION_LABELS), 'viewed_seq': 0 } if viewpoint.IsDefault(): foll_dict['labels'].append(Follower.PERSONAL) follower = Follower.CreateFromKeywords(**foll_dict) tasks.append(gen.Task(follower.Update, client)) # Create the corresponding Followed record. tasks.append( gen.Task(Followed.UpdateDateUpdated, client, viewpoint.user_id, viewpoint.viewpoint_id, old_timestamp=None, new_timestamp=viewpoint.last_updated)) yield tasks raise gen.Return((viewpoint, follower))
def _UpdateFollower(follower_id): """Create a new follower of this viewpoint in the database.""" follower = Follower(user_id=follower_id, viewpoint_id=self.viewpoint_id) follower.timestamp = timestamp follower.adding_user_id = adding_user_id follower.viewed_seq = 0 follower.labels = [Follower.CONTRIBUTE] # Create the follower and corresponding Followed record. yield [ gen.Task(follower.Update, client), gen.Task(Followed.UpdateDateUpdated, client, follower_id, self.viewpoint_id, old_timestamp=None, new_timestamp=timestamp) ] raise gen.Return(follower)
def testMissingActivities(self): """Verifies detection of missing activities.""" self._CreateTestViewpoint('vp1', self._user.user_id, [self._user2.user_id]) self._CreateTestEpisode('vp1', 'ep1', self._user.user_id) self._CreateTestEpisode('vp1', 'ep2', self._user.user_id) self._CreateTestEpisode('vp1', 'ep3', self._user.user_id) self._CreateTestViewpoint('vp2', self._user.user_id, [self._user2.user_id]) self._CreateTestEpisode('vp2', 'ep4', self._user.user_id) self._CreateTestComment('vp2', 'cm1', self._user.user_id, 'a comment') self._RunAsync(Activity.CreateShareNew, self._client, self._user.user_id, 'vp2', 'a1', time.time(), 0, [{'new_episode_id': 'ep4', 'photo_ids': []}], []) self._CreateTestEpisode(self._user.private_vp_id, 'ep5', self._user.user_id) # Create an episode that would be created by save_photos. ep_dict = {'episode_id': 'ep6', 'user_id': self._user.user_id, 'viewpoint_id': self._user.private_vp_id, 'parent_ep_id': 'ep4', 'publish_timestamp': time.time(), 'timestamp': time.time()} self._RunAsync(Episode.CreateNew, self._client, **ep_dict) # Create viewpoint that's missing a follower activity. self._CreateTestViewpoint('vp3', self._user.user_id, []) follower = Follower.CreateFromKeywords(user_id=self._user2.user_id, viewpoint_id='vp3') self._RunAsync(follower.Update, self._client) # Create viewpoint with a follower covered by merge_accounts (shouldn't be tagged as missing follower). self._CreateTestViewpoint('vp4', self._user.user_id, [self._user2.user_id]) self._RunAsync(Activity.CreateMergeAccounts, self._client, self._user.user_id, 'vp2', 'a1', time.time(), 0, self._user2.user_id, self._user.user_id) self._RunAsync(self._checker.CheckAllViewpoints) # Default viewpoints created by DBBaseTestCase are missing Followed records. corruption_text = \ ' ---- viewpoint v-F- ----\n' \ ' missing save_photos activity (1 instance)\n' \ ' missing upload_episode activity (1 instance)\n' \ '\n' \ ' ---- viewpoint vp4 ----\n' \ ' empty viewpoint (1 instance)\n' \ '\n' \ ' ---- viewpoint vp1 ----\n' \ ' missing share_existing activity (2 instances)\n' \ ' missing share_new activity (1 instance)\n' \ '\n' \ ' ---- viewpoint vp3 ----\n' \ ' missing followed (1 instance)\n' \ ' empty viewpoint (1 instance)\n' \ '\n' \ ' ---- viewpoint vp2 ----\n' \ ' missing post_comment activity (1 instance)\n' \ ' missing add_followers activity (1 instance)\n' \ '\n' \ 'python dbchk.py --devbox --repair=True --viewpoints=v-F-,vp4,vp1,vp3,vp2' self.assertEqual(self._checker._email_args['text'], 'Found corruption(s) in database:\n\n%s' % corruption_text) self._RunDbChk({'viewpoints': ['vp1', 'vp2', 'vp3', 'vp4', 'v-F-'], 'repair': True}) # Validate by checking again and finding no issues. self._RunAsync(self._checker.CheckAllViewpoints) self.assertIsNone(self._checker._email_args)