Exemple #1
0
def notify_posts(shard, post_list, sequence_numbers=None):
    """Notifies logged-in users of a set of new posts.

    Args:
        shard: Shard ID to notify for.
        post_list: When the post_list is a list of strings, then it's assumed
            these are the IDs of Posts that must be fetched prior to
            notification. Otherwise these should be Post entities.
        sequence_numbers: When supplied, should be a list of sequence numbers
            that correspond to each of the items in the post_list, in order.
            This is used to tell the user what the sequence ID of each post is
            within a particular shard.
    """
    if not post_list:
        return

    if isinstance(post_list[0], basestring):
        post_keys = [ndb.Key(models.Post._get_kind(), post_id)
                     for post_id in post_list]
        post_list = yield ndb.get_multi_async(post_keys)

    if not sequence_numbers:
        sequence_numbers = [None] * len(post_list)

    for post, sequence in zip(post_list, sequence_numbers):
        post.sequence = sequence

    posts_json = json.dumps({
        'posts': marshal_posts(shard, post_list),
    })

    login_record_list = presence.get_present_users(shard)
    rpc_list = []
    for login_record in login_record_list:
        logging.debug(
            'Informing shard=%r, user=%r, nickname=%r about messages '
            'with sequence_numbers=%r', shard, login_record.user_id,
            login_record.nickname, sequence_numbers)
        browser_token = presence.get_token(login_record.user_id)
        rpc_list.append(send_message_async(browser_token, posts_json))

    for rpc in rpc_list:
        try:
            yield rpc
        except channel.Error, e:
            # NOTE: When receiving an InvalidChannelKeyError the message may
            # still be available the next time the user connects to the channel
            # with that same application key due to buffering in the backends.
            # The dev_appserver mimics this behavior, but it's not reliable in
            # prod.
            logging.warning('Could not send JSON message to user=%r with '
                            'browser_token=%r. %s: %s', login_record.user_id,
                            browser_token, e.__class__.__name__, str(e))
Exemple #2
0
def list_topics(root_shard_id, user_id):
    """Lists topics for a root shard and associated read state for the user.

    Args:
        root_shard_id: Shard ID of the root with associated topics.
        user_id: User ID that is requesting the list of topics and read states.

    Returns:
        Tuple (root_shard, shard_and_state_list) where:
            root_shard: Shard entity for the root.
            shard_and_state_list: List of pairs (Shard, ReadState) for
                associated topics and read states for the given user_id. Will
                be in order of update_time with most recently updated shards
                first.
    """
    # TODO(bslatkin): Remove the root_shard return value.
    root_shard_future = models.Shard.get_by_id_async(
        root_shard_id, use_cache=False, use_memcache=False)

    oldest_update_time = (
        datetime.datetime.now() -
        datetime.timedelta(seconds=config.ephemeral_lifetime_seconds))

    query = models.Shard.query()
    query = query.filter(models.Shard.root_shard == root_shard_id)
    query = query.filter(models.Shard.update_time > oldest_update_time)
    query = query.order(-models.Shard.update_time)
    shard_list = yield query.fetch_async(100)

    # Include the root shard in the list of topics so the email digester
    # will include updates to the root shard if no topics have ever been sent.
    root_shard = yield root_shard_future
    shard_list.append(root_shard)

    # Get the current user's readstate for each shard that was found.
    read_state_key_list = [
        ndb.Key(models.LoginRecord._get_kind(), user_id,
                models.ReadState._get_kind(), shard.shard_id)
        for shard in shard_list]
    read_state_list = yield ndb.get_multi_async(read_state_key_list)
    shard_and_state_list = zip(shard_list, read_state_list)

    raise ndb.Return((root_shard, shard_and_state_list))