def _AutoSave(self): """For each follower that has enabled auto-save for this viewpoint, trigger save_photos operation that will save the shared photos to their default viewpoint. """ # Get ids of all the source episodes that will be provided to save_photos. source_ep_ids = [ep_dict['new_episode_id'] for ep_dict in self._ep_dicts] for follower in self._followers: # Skip follower if he did not mark this viewpoint for auto-saving. if not follower.ShouldAutoSave(): continue # Skip follower if he is removed from the conversation. if follower.IsRemoved(): continue follower_user = yield gen.Task(User.Query, self._client, follower.user_id, None) # Skip follower if he is the sharer, and is sharing only episodes from his default viewpoint. if follower_user.user_id == self._user_id: if all(source_episode.viewpoint_id == follower_user.private_vp_id for source_episode, posts in self._source_ep_posts_list): continue # Allocate ids for save_photos operation and activity. first_id = yield gen.Task(User.AllocateAssetIds, self._client, follower.user_id, 2) op_id = Operation.ConstructOperationId(follower_user.webapp_dev_id, first_id) activity_id = Activity.ConstructActivityId(self._act_dict['timestamp'], follower_user.webapp_dev_id, first_id + 1) # Generate ids for any target episodes that don't already exist. target_ep_ids = yield ViewfinderOperation._AllocateTargetEpisodeIds(self._client, follower.user_id, follower_user.webapp_dev_id, follower_user.private_vp_id, source_ep_ids) # Create target episode dicts expected by the SavePhotos op. target_eps_list = [] for ep_dict, target_ep_id in zip(self._ep_dicts, target_ep_ids): target_eps_list.append({'existing_episode_id': ep_dict['new_episode_id'], 'new_episode_id': target_ep_id, 'photo_ids': ep_dict['photo_ids']}) save_photos_dict = {'headers': {'op_id': op_id, 'op_timestamp': self._op.timestamp}, 'user_id': follower.user_id, 'activity': {'activity_id': activity_id, 'timestamp': self._act_dict['timestamp']}, 'episodes': target_eps_list} # Create the save_photos op for this user. Use the raw DBClient instance since self._client # is wrapped with OpMgrDBClient. yield gen.Task(Operation.CreateAndExecute, DBClient.Instance(), follower.user_id, follower_user.webapp_dev_id, 'SavePhotosOperation.Execute', save_photos_dict)
def _AutoSave(self): """For each follower that has enabled auto-save for this viewpoint, trigger save_photos operation that will save the shared photos to their default viewpoint. """ # Get ids of all the source episodes that will be provided to save_photos. source_ep_ids = [ ep_dict['new_episode_id'] for ep_dict in self._ep_dicts ] for follower in self._followers: # Skip follower if he did not mark this viewpoint for auto-saving. if not follower.ShouldAutoSave(): continue # Skip follower if he is removed from the conversation. if follower.IsRemoved(): continue follower_user = yield gen.Task(User.Query, self._client, follower.user_id, None) # Skip follower if he is the sharer, and is sharing only episodes from his default viewpoint. if follower_user.user_id == self._user_id: if all(source_episode.viewpoint_id == follower_user.private_vp_id for source_episode, posts in self._source_ep_posts_list): continue # Allocate ids for save_photos operation and activity. first_id = yield gen.Task(User.AllocateAssetIds, self._client, follower.user_id, 2) op_id = Operation.ConstructOperationId(follower_user.webapp_dev_id, first_id) activity_id = Activity.ConstructActivityId( self._act_dict['timestamp'], follower_user.webapp_dev_id, first_id + 1) # Generate ids for any target episodes that don't already exist. target_ep_ids = yield ViewfinderOperation._AllocateTargetEpisodeIds( self._client, follower.user_id, follower_user.webapp_dev_id, follower_user.private_vp_id, source_ep_ids) # Create target episode dicts expected by the SavePhotos op. target_eps_list = [] for ep_dict, target_ep_id in zip(self._ep_dicts, target_ep_ids): target_eps_list.append({ 'existing_episode_id': ep_dict['new_episode_id'], 'new_episode_id': target_ep_id, 'photo_ids': ep_dict['photo_ids'] }) save_photos_dict = { 'headers': { 'op_id': op_id, 'op_timestamp': self._op.timestamp }, 'user_id': follower.user_id, 'activity': { 'activity_id': activity_id, 'timestamp': self._act_dict['timestamp'] }, 'episodes': target_eps_list } # Create the save_photos op for this user. Use the raw DBClient instance since self._client # is wrapped with OpMgrDBClient. yield gen.Task(Operation.CreateAndExecute, DBClient.Instance(), follower.user_id, follower_user.webapp_dev_id, 'SavePhotosOperation.Execute', save_photos_dict)
def _CreateSaveEpisodeDicts(self): """Creates a list of dicts describing the source and target episodes of the save. The episode dicts passed in the save_photos request are combined with episodes in any of the viewpoints passed in the save_photos request. Returns the list. """ # Query episode ids from viewpoints given in the request, but skip those that are already in the request. vp_ep_ids = [] skip_vp_ep_ids = set(ep_dict['existing_episode_id'] for ep_dict in self._ep_dicts) # Query photo_ids from viewpoint episodes. ep_ph_ids = {} @gen.coroutine def _VisitPosts(photo_ids, post): photo_ids.append(post.photo_id) @gen.coroutine def _VisitEpisodeKeys(episode_key): episode_id = episode_key.hash_key # Get list of episodes in the viewpoint that need a target episode id discovered/generated. if episode_id not in skip_vp_ep_ids: vp_ep_ids.append(episode_id) # For each episode in the viewpoint, get the complete list of photo ids in that episode. photo_ids = [] yield gen.Task(Post.VisitRange, self._client, episode_id, None, None, partial(_VisitPosts, photo_ids)) ep_ph_ids[episode_id] = photo_ids tasks = [] for viewpoint_id in set(self._viewpoint_ids): query_expr = ('episode.viewpoint_id={id}', {'id': viewpoint_id}) tasks.append(gen.Task(Episode.VisitIndexKeys, self._client, query_expr, _VisitEpisodeKeys)) yield tasks # Allocate target ids for all episodes not given by the client. target_ep_ids = yield ViewfinderOperation._AllocateTargetEpisodeIds(self._client, self._user.user_id, self._user.webapp_dev_id, self._user.private_vp_id, vp_ep_ids) # Create save dicts for each of the viewpoint episodes to save. save_ep_dicts = {} for source_ep_id, target_ep_id in zip(vp_ep_ids, target_ep_ids): save_ep_dicts[target_ep_id] = {'existing_episode_id': source_ep_id, 'new_episode_id': target_ep_id, 'photo_ids': ep_ph_ids[source_ep_id]} # Now add the save dicts from the request, validating rules as we go. for ep_dict in self._ep_dicts: existing_ep_dict = save_ep_dicts.get(ep_dict['new_episode_id'], None) if existing_ep_dict is not None: if ep_dict['existing_episode_id'] != existing_ep_dict['existing_episode_id']: raise InvalidRequestError('Cannot save episodes "%s" and "%s" to same target episode "%s".' % (existing_ep_dict['existing_episode_id'], ep_dict['existing_episode_id'], ep_dict['new_episode_id'])) existing_ep_dict['photo_ids'].extend(ep_dict['photo_ids']) existing_ep_dict['photo_ids'] = sorted(set(existing_ep_dict['photo_ids'])) else: photo_ids = ep_dict['photo_ids'] if ep_dict['existing_episode_id'] in ep_ph_ids: photo_ids.extend(ep_ph_ids[ep_dict['existing_episode_id']]) save_ep_dicts[ep_dict['new_episode_id']] = {'existing_episode_id': ep_dict['existing_episode_id'], 'new_episode_id': ep_dict['new_episode_id'], 'photo_ids': sorted(set(photo_ids))} save_ep_dicts = [ep_dict for ep_dict in save_ep_dicts.itervalues()] save_ep_dicts.sort(key=itemgetter('new_episode_id')) raise gen.Return(save_ep_dicts)