@gen.engine
def QueryEpisodes(client, obj_store, user_id, device_id, request, callback):
  """Queries posts from the specified episodes.
  """
  def _MakePhotoDict(post, photo, user_post, user_photo):
    ph_dict = photo.MakeMetadataDict(post, user_post, user_photo)

    # Do not return access URLs for posts which have been removed.
    if not post.IsRemoved():
      _AddPhotoUrls(obj_store, ph_dict)

    return ph_dict

  limit = request.get('photo_limit', None)

  # Get all requested episodes, along with posts for each episode.
  episode_keys = [db_client.DBKey(ep_dict['episode_id'], None) for ep_dict in request['episodes']]

  post_tasks = []
  for ep_dict in request['episodes']:
    if ep_dict.get('get_photos', False):
      post_tasks.append(gen.Task(Post.RangeQuery, client, ep_dict['episode_id'], None, limit,
                                 None, excl_start_key=ep_dict.get('photo_start_key', None)))
    else:
      post_tasks.append(util.GenConstant(None))

  episodes, posts_list = yield [gen.Task(Episode.BatchQuery, client, episode_keys, None, must_exist=False),
                                gen.Multi(post_tasks)]

  # Get viewpoint records for all viewpoints containing episodes.
  viewpoint_keys = [db_client.DBKey(viewpoint_id, None)
                    for viewpoint_id in set(ep.viewpoint_id for ep in episodes if ep is not None)]

  # Get follower records for all viewpoints containing episodes, along with photo and user post objects.
  follower_keys = [db_client.DBKey(user_id, db_key.hash_key) for db_key in viewpoint_keys]

  all_posts = [post for posts in posts_list if posts is not None for post in posts]
  photo_keys = [db_client.DBKey(post.photo_id, None) for post in all_posts]
  user_post_keys = [db_client.DBKey(user_id, Post.ConstructPostId(post.episode_id, post.photo_id))
                    for post in all_posts]
  if user_id:
    # TODO(ben): we can probably skip this for the web view
    user_photo_task = gen.Task(UserPhoto.BatchQuery, client,
                               [db_client.DBKey(user_id, post.photo_id) for post in all_posts],
                               None, must_exist=False)
  else:
    user_photo_task = util.GenConstant(None)

  viewpoints, followers, photos, user_posts, user_photos = yield [
    gen.Task(Viewpoint.BatchQuery, client, viewpoint_keys, None, must_exist=False),
    gen.Task(Follower.BatchQuery, client, follower_keys, None, must_exist=False),
    gen.Task(Photo.BatchQuery, client, photo_keys, None),
    gen.Task(UserPost.BatchQuery, client, user_post_keys, None, must_exist=False),
    user_photo_task,
    ]

  # Get set of viewpoint ids to which the current user has access.
  viewable_viewpoint_ids = set(viewpoint.viewpoint_id for viewpoint, follower in zip(viewpoints, followers)
                               if _CanViewViewpointContent(viewpoint, follower))

  response_dict = {'episodes': []}
  num_photos = 0

  for ep_dict, episode, posts in zip(request['episodes'], episodes, posts_list):
    # Gather list of (post, photo, user_post) tuples for this episode.
    photo_info_list = []
    if ep_dict.get('get_photos', False):
      for post in posts:
        photo = photos.pop(0)
        user_post = user_posts.pop(0)
        user_photo = user_photos.pop(0) if user_photos is not None else None
        assert photo.photo_id == post.photo_id, (episode, post, photo)
        if user_photo:
          assert user_photo.photo_id == photo.photo_id
          assert user_photo.user_id == user_id
        photo_info_list.append((post, photo, user_post, user_photo))

    if episode is not None and episode.viewpoint_id in viewable_viewpoint_ids:
      response_ep_dict = {'episode_id': ep_dict['episode_id']}

      # Only return episode metadata if "get_attributes" is True.
      if ep_dict.get('get_attributes', False):
        response_ep_dict.update(episode._asdict())

      # Only return photos if "get_photos" is True.
      if ep_dict.get('get_photos', False):
        response_ep_dict['photos'] = [_MakePhotoDict(photo, post, user_post, user_photo)
                                      for photo, post, user_post, user_photo in photo_info_list]
        if 0 > len(photo_info_list):
          response_ep_dict['last_key'] = photo_info_list[-1][0].photo_id
          num_photos += len(photo_info_list)

      response_dict['episodes'].append(response_ep_dict)

  logging.info('QUERY EPISODES: user: %d, device: %d, %d episodes, %d photos' %
               (user_id, device_id, len(response_dict['episodes']), num_photos))

  callback(response_dict)
dataset/ETHPy150Open viewfinderco/viewfinder/backend/www/service.py/QueryEpisodes/swapped operands of `>`