Example #1
0
    def get_items(self):
        timer = g.stats.get_timer("CommentBuilder.get_items")
        timer.start()
        r = link_comments_and_sort(self.link, self.sort.col)
        cids, cid_tree, depth, parents, sorter = r
        timer.intermediate("load_storage")

        if self.comment and not self.comment._id in depth:
            g.log.error(
                "Hack - self.comment (%d) not in depth. Defocusing..." %
                self.comment._id)
            self.comment = None

        more_recursions = {}
        dont_collapse = []
        candidates = []
        offset_depth = 0

        if self.children:
            # requested specific child comments
            children = [cid for cid in self.children if cid in cids]
            self.update_candidates(candidates, sorter, children)
            dont_collapse.extend(comment for sort_val, comment in candidates)

        elif self.comment:
            # requested the tree from a specific comment

            # construct path back to top level from this comment, a maximum of
            # `context` levels
            comment = self.comment._id
            path = []
            while comment and len(path) <= self.context:
                path.append(comment)
                comment = parents[comment]

            dont_collapse.extend(path)

            # rewrite cid_tree so the parents lead only to the requested comment
            for comment in path:
                parent = parents[comment]
                cid_tree[parent] = [comment]

            # start building comment tree from earliest comment
            self.update_candidates(candidates, sorter, path[-1])

            # set offset_depth because we may not be at the top level and can
            # show deeper levels
            offset_depth = depth.get(path[-1], 0)

        else:
            # full tree requested, start with the top level comments
            top_level_comments = cid_tree.get(None, ())
            self.update_candidates(candidates, sorter, top_level_comments)

        timer.intermediate("pick_candidates")

        if not candidates:
            timer.stop()
            return []

        # choose which comments to show
        items = []
        while (self.num is None or len(items) < self.num) and candidates:
            sort_val, comment_id = heapq.heappop(candidates)
            if comment_id not in cids:
                continue

            comment_depth = depth[comment_id] - offset_depth
            if comment_depth < self.max_depth:
                items.append(comment_id)

                # add children
                if comment_id in cid_tree:
                    children = cid_tree[comment_id]
                    self.update_candidates(candidates, sorter, children)

            elif (self.continue_this_thread
                  and parents.get(comment_id) is not None):
                # the comment is too deep to add, so add a MoreRecursion for
                # its parent
                parent_id = parents[comment_id]
                if parent_id not in more_recursions:
                    w = Wrapped(
                        MoreRecursion(self.link, depth=0, parent_id=parent_id))
                else:
                    w = more_recursions[parent_id]
                w.children.append(comment_id)
                more_recursions[parent_id] = w

        timer.intermediate("pick_comments")

        # retrieve num_children for the visible comments
        top_level_candidates = [
            comment for sort_val, comment in candidates
            if depth.get(comment, 0) == 0
        ]
        needs_num_children = items + top_level_candidates
        num_children = get_num_children(needs_num_children, cid_tree)
        timer.intermediate("calc_num_children")

        comments = Comment._byID(items,
                                 data=True,
                                 return_dict=False,
                                 stale=self.stale)
        timer.intermediate("lookup_comments")
        wrapped = self.wrap_items(comments)
        timer.intermediate("wrap_comments")
        wrapped_by_id = {comment._id: comment for comment in wrapped}
        final = []

        for comment in wrapped:
            # skip deleted comments with no children
            if (comment.deleted and not cid_tree.has_key(comment._id)
                    and not c.user_is_admin):
                continue

            comment.num_children = num_children[comment._id]

            if comment.collapsed and comment._id in dont_collapse:
                comment.collapsed = False

            # add the comment as a child of its parent or to the top level of
            # the tree if it has no parent
            parent = wrapped_by_id.get(comment.parent_id)
            if parent:
                if not hasattr(parent, 'child'):
                    add_child_listing(parent, comment)
                else:
                    parent.child.things.append(comment)
            else:
                final.append(comment)

        for parent_id, more_recursion in more_recursions.iteritems():
            if parent_id not in wrapped_by_id:
                continue

            parent = wrapped_by_id[parent_id]
            add_child_listing(parent, more_recursion)

        timer.intermediate("build_comments")

        if not self.load_more:
            timer.stop()
            return final

        # build MoreChildren for visible comments
        visible_comments = wrapped_by_id.keys()
        for visible_id in visible_comments:
            if visible_id in more_recursions:
                # don't add a MoreChildren if we already have a MoreRecursion
                continue

            children = cid_tree.get(visible_id, ())
            missing_children = [
                child for child in children if child not in visible_comments
            ]
            if missing_children:
                visible_children = (child for child in children
                                    if child in visible_comments)
                visible_count = sum(1 + num_children[child]
                                    for child in visible_children)
                missing_count = num_children[visible_id] - visible_count
                missing_depth = depth.get(visible_id, 0) + 1 - offset_depth

                if missing_depth < self.max_depth:
                    mc = MoreChildren(self.link,
                                      depth=missing_depth,
                                      parent_id=visible_id)
                    mc.children.extend(missing_children)
                    w = Wrapped(mc)
                    w.count = missing_count
                else:
                    mr = MoreRecursion(self.link,
                                       depth=missing_depth,
                                       parent_id=visible_id)
                    w = Wrapped(mr)

                # attach the MoreChildren
                parent = wrapped_by_id[visible_id]
                if hasattr(parent, 'child'):
                    parent.child.things.append(w)
                else:
                    add_child_listing(parent, w)

        # build MoreChildren for missing root level comments
        if top_level_candidates:
            mc = MoreChildren(self.link, depth=0, parent_id=None)
            mc.children.extend(top_level_candidates)
            w = Wrapped(mc)
            w.count = sum(1 + num_children[comment]
                          for comment in top_level_candidates)
            final.append(w)

        if isinstance(self.sort, operators.shuffled):
            shuffle(final)

        timer.intermediate("build_morechildren")
        timer.stop()
        return final
Example #2
0
def sponsor_wrapper(link):
    w = Wrapped(link)
    w.render_class = PromotedLink
    w.rowstyle = "promoted link"
    return w
Example #3
0
    def get_items(self):
        tree = self.get_tree()

        prev_item = next_item = None
        if not self.parent:
            if self.num is not None:
                if self.after:
                    if self.reverse:
                        tree = filter(self._tree_filter_reverse, tree)
                        next_item = self.after._id
                        if len(tree) > self.num:
                            first = tree[-(self.num + 1)]
                            prev_item = first[1][-1] if first[1] else first[0]
                            tree = tree[-self.num:]
                    else:
                        prev_item = self.after._id
                        tree = filter(self._tree_filter, tree)
                if len(tree) > self.num:
                    tree = tree[:self.num]
                    last = tree[-1]
                    next_item = last[1][-1] if last[1] else last[0]

        # generate the set of ids to look up and look them up
        message_ids = []
        for root, thread in tree:
            message_ids.append(root)
            message_ids.extend(thread)
        if prev_item:
            message_ids.append(prev_item)

        messages = Message._byID(message_ids, data=True, return_dict=False)
        wrapped = {m._id: m for m in self.wrap_items(messages)}

        if prev_item:
            prev_item = wrapped[prev_item]
        if next_item:
            next_item = wrapped[next_item]

        final = []
        for parent, children in tree:
            if parent not in wrapped:
                continue

            parent = wrapped[parent]

            if not self._viewable_message(parent):
                continue

            if children:
                # if no parent is specified, check if any of the messages are
                # uncollapsed, and truncate the thread
                children = [
                    wrapped[child] for child in children if child in wrapped
                ]
                add_child_listing(parent)
                # if the parent is new, uncollapsed, or focal we don't
                # want it to become a moremessages wrapper.
                if (self.skip and not self.parent and not parent.new
                        and parent.is_collapsed
                        and not (self.focal and self.focal._id == parent._id)):
                    for i, child in enumerate(children):
                        if (child.new or not child.is_collapsed or
                            (self.focal and self.focal._id == child._id)):
                            break
                    else:
                        i = -1
                    parent = Wrapped(MoreMessages(parent, parent.child))
                    children = children[i:]

                parent.child.parent_name = parent._fullname
                parent.child.things = []

                for child in children:
                    child.is_child = True
                    if self.focal and child._id == self.focal._id:
                        # focal message is never collapsed
                        child.collapsed = False
                        child.focal = True
                    else:
                        child.collapsed = child.is_collapsed

                    parent.child.things.append(child)
            parent.is_parent = True
            # the parent might be the focal message on a permalink page
            if self.focal and parent._id == self.focal._id:
                parent.collapsed = False
                parent.focal = True
            else:
                parent.collapsed = parent.is_collapsed
            final.append(parent)

        return (final, prev_item, next_item, len(final), len(final))
Example #4
0
def add_child_listing(parent, *things):
    l = Listing(None, nextprev=None)
    l.things = list(things)
    parent.child = Wrapped(l)
    parent_name = parent._fullname if not parent.deleted else "deleted"
    parent.child.parent_name = parent_name
Example #5
0
def wrap_promoted(link):
    if not isinstance(link, Wrapped):
        link = Wrapped(link)
    return link
Example #6
0
 def GET_about(self, vuser):
     """Return information about the user, including karma and gold status."""
     if not is_api() or not vuser:
         return self.abort404()
     return Reddit(content=Wrapped(vuser)).render()
Example #7
0
    def _get_comments(self):
        timer = g.stats.get_timer("CommentBuilder.get_items")
        timer.start()
        r = link_comments_and_sort(self.link, self.sort.col)
        cids, cid_tree, depth, parents, sorter = r
        timer.intermediate("load_storage")

        if self.comment and not self.comment._id in depth:
            g.log.error(
                "Hack - self.comment (%d) not in depth. Defocusing..." %
                self.comment._id)
            self.comment = None

        more_recursions = {}
        dont_collapse = []
        candidates = []
        offset_depth = 0

        if self.children:
            # requested specific child comments
            children = [cid for cid in self.children if cid in cids]
            self.update_candidates(candidates, sorter, children)
            dont_collapse.extend(comment for sort_val, comment in candidates)

        elif self.comment:
            # requested the tree from a specific comment

            # construct path back to top level from this comment, a maximum of
            # `context` levels
            comment = self.comment._id
            path = []
            while comment and len(path) <= self.context:
                path.append(comment)
                comment = parents[comment]

            dont_collapse.extend(path)

            # rewrite cid_tree so the parents lead only to the requested comment
            for comment in path:
                parent = parents[comment]
                cid_tree[parent] = [comment]

            # start building comment tree from earliest comment
            self.update_candidates(candidates, sorter, path[-1])

            # set offset_depth because we may not be at the top level and can
            # show deeper levels
            offset_depth = depth.get(path[-1], 0)

        else:
            # full tree requested, start with the top level comments
            top_level_comments = cid_tree.get(None, ())
            self.update_candidates(candidates, sorter, top_level_comments)

        timer.intermediate("pick_candidates")

        # choose which comments to show
        items = []
        while (self.num is None or len(items) < self.num) and candidates:
            sort_val, comment_id = heapq.heappop(candidates)
            if comment_id not in cids:
                continue

            comment_depth = depth[comment_id] - offset_depth
            if comment_depth < self.max_depth:
                items.append(comment_id)

                # add children
                if comment_id in cid_tree:
                    children = cid_tree[comment_id]
                    self.update_candidates(candidates, sorter, children)

            elif (self.continue_this_thread
                  and parents.get(comment_id) is not None):
                # the comment is too deep to add, so add a MoreRecursion for
                # its parent
                parent_id = parents[comment_id]
                if parent_id not in more_recursions:
                    w = Wrapped(
                        MoreRecursion(self.link, depth=0, parent_id=parent_id))
                else:
                    w = more_recursions[parent_id]
                w.children.append(comment_id)
                more_recursions[parent_id] = w

        timer.intermediate("pick_comments")

        self.top_level_candidates = [
            comment for sort_val, comment in candidates
            if depth.get(comment, 0) == 0
        ]
        self.comments = Comment._byID(items,
                                      data=True,
                                      return_dict=False,
                                      stale=self.stale)
        timer.intermediate("lookup_comments")

        self.timer = timer
        self.cid_tree = cid_tree
        self.depth = depth
        self.more_recursions = more_recursions
        self.offset_depth = offset_depth
        self.dont_collapse = dont_collapse
Example #8
0
    def _make_wrapped_tree(self):
        timer = self.timer
        comments = self.comments
        cid_tree = self.cid_tree
        top_level_candidates = self.top_level_candidates
        depth = self.depth
        more_recursions = self.more_recursions
        offset_depth = self.offset_depth
        dont_collapse = self.dont_collapse
        timer.intermediate("waiting")

        if not comments and not top_level_candidates:
            timer.stop()
            return []

        # retrieve num_children for the visible comments
        needs_num_children = [c._id for c in comments] + top_level_candidates
        num_children = get_num_children(needs_num_children, cid_tree)
        timer.intermediate("calc_num_children")

        wrapped = self.wrap_items(comments)
        timer.intermediate("wrap_comments")
        wrapped_by_id = {comment._id: comment for comment in wrapped}

        if self.children:
            # rewrite the parent links to use anchor tags
            for comment_id in self.children:
                if comment_id in wrapped_by_id:
                    item = wrapped_by_id[comment_id]
                    if item.parent_id:
                        item.parent_permalink = '#' + to36(item.parent_id)

        final = []

        # We have some special collapsing rules for the Q&A sort type.
        # However, we want to show everything when we're building a specific
        # set of children (like from "load more" links) or when viewing a
        # comment permalink.
        qa_sort_hiding = ((self.sort.col == '_qa') and not self.children
                          and self.comment is None)
        if qa_sort_hiding:
            special_responder_ids = self.link.responder_ids
        else:
            special_responder_ids = ()

        max_relation_walks = g.max_comment_parent_walk
        for comment in wrapped:
            # skip deleted comments with no children
            if (comment.deleted and not cid_tree.has_key(comment._id)
                    and not self.show_deleted):
                comment.hidden_completely = True
                continue

            comment.num_children = num_children[comment._id]
            comment.edits_visible = self.edits_visible

            parent = wrapped_by_id.get(comment.parent_id)
            if qa_sort_hiding:
                author_is_special = comment.author_id in special_responder_ids
            else:
                author_is_special = False

            # In the Q&A sort type, we want to collapse all comments other than
            # those that are:
            #
            # 1. Top-level comments,
            # 2. Responses from the OP(s),
            # 3. Responded to by the OP(s) (dealt with below),
            # 4. Within one level of an OP reply, or
            # 5. Otherwise normally prevented from collapse (eg distinguished
            #    comments).
            if (qa_sort_hiding and depth[comment._id] != 0 and  # (1)
                    not author_is_special and  # (2)
                    not (parent and parent.author_id in special_responder_ids
                         and feature.is_enabled('qa_show_replies')) and  # (4)
                    not comment.prevent_collapse):  # (5)
                comment.hidden = True

            if comment.collapsed:
                if comment._id in dont_collapse or author_is_special:
                    comment.collapsed = False
                    comment.hidden = False

            if parent:
                if author_is_special:
                    # Un-collapse parents as necessary.  It's a lot easier to
                    # do this here, upwards, than to check through all the
                    # children when we were iterating at the parent.
                    ancestor = parent
                    counter = 0
                    while (ancestor and not getattr(ancestor, 'walked', False)
                           and counter < max_relation_walks):
                        ancestor.hidden = False
                        # In case we haven't processed this comment yet.
                        ancestor.prevent_collapse = True
                        # This allows us to short-circuit when the rest of the
                        # tree has already been uncollapsed.
                        ancestor.walked = True

                        ancestor = wrapped_by_id.get(ancestor.parent_id)
                        counter += 1

        # One more time through to actually add things to the final list.  We
        # couldn't do that the first time because in the Q&A sort we don't know
        # if a comment should be visible until after we've processed all its
        # children.
        for comment in wrapped:
            if getattr(comment, 'hidden_completely', False):
                # Don't add it to the tree, don't put it in "load more", don't
                # acknowledge its existence at all.
                continue

            if getattr(comment, 'hidden', False):
                # Remove it from the list of visible comments so it'll
                # automatically be a candidate for the "load more" links.
                del wrapped_by_id[comment._id]
                # And don't add it to the tree.
                continue

            # add the comment as a child of its parent or to the top level of
            # the tree if it has no parent
            parent = wrapped_by_id.get(comment.parent_id)
            if parent:
                if not hasattr(parent, 'child'):
                    add_child_listing(parent, comment)
                else:
                    parent.child.things.append(comment)
            else:
                final.append(comment)

        for parent_id, more_recursion in more_recursions.iteritems():
            if parent_id not in wrapped_by_id:
                continue

            parent = wrapped_by_id[parent_id]
            add_child_listing(parent, more_recursion)

        timer.intermediate("build_comments")

        if not self.load_more:
            timer.stop()
            return final

        # build MoreChildren for visible comments
        visible_comments = wrapped_by_id.keys()
        for visible_id in visible_comments:
            if visible_id in more_recursions:
                # don't add a MoreChildren if we already have a MoreRecursion
                continue

            children = cid_tree.get(visible_id, ())
            missing_children = [
                child for child in children if child not in visible_comments
            ]
            if missing_children:
                visible_children = (child for child in children
                                    if child in visible_comments)
                visible_count = sum(1 + num_children[child]
                                    for child in visible_children)
                missing_count = num_children[visible_id] - visible_count
                missing_depth = depth.get(visible_id, 0) + 1 - offset_depth

                if missing_depth < self.max_depth:
                    mc = MoreChildren(self.link,
                                      self.sort,
                                      depth=missing_depth,
                                      parent_id=visible_id)
                    mc.children.extend(missing_children)
                    w = Wrapped(mc)
                    w.count = missing_count
                else:
                    mr = MoreRecursion(self.link,
                                       depth=missing_depth,
                                       parent_id=visible_id)
                    w = Wrapped(mr)

                # attach the MoreChildren
                parent = wrapped_by_id[visible_id]
                if hasattr(parent, 'child'):
                    parent.child.things.append(w)
                else:
                    add_child_listing(parent, w)

        # build MoreChildren for missing root level comments
        if top_level_candidates:
            mc = MoreChildren(self.link, self.sort, depth=0, parent_id=None)
            mc.children.extend(top_level_candidates)
            w = Wrapped(mc)
            w.count = sum(1 + num_children[comment]
                          for comment in top_level_candidates)
            final.append(w)

        if isinstance(self.sort, operators.shuffled):
            shuffle(final)

        timer.intermediate("build_morechildren")
        timer.stop()
        return final
Example #9
0
 def listing(self):
     res = Listing.listing(self)
     for t in res.things:
         t.num = ""
     return Wrapped(self)
Example #10
0
 def GET_about(self, vuser):
     if not is_api() or not vuser:
         return self.abort404()
     return Reddit(content = Wrapped(vuser)).render()
Example #11
0
    def __init__(self, a = None):
        if a:
            a = Wrapped(a)

        Wrapped.__init__(self, a = a, datefmt = datefmt)
Example #12
0
 def comment_permalink_wrapper(comment, link):
     wrapped = Wrapped(link, link_title=comment.make_permalink_title(link), for_comment_permalink=True)
     wrapped.render_class = CommentPermalink
     return wrapped
Example #13
0
    def get_items(self, num, nested=True, starting_depth=0):
        r = link_comments(self.link._id)
        cids, comment_tree, depth, num_children = r
        if cids:
            comments = set(Comment._byID(cids, data=True, return_dict=False))
        else:
            comments = ()

        def empty_listing(*things):
            parent_name = None
            for t in things:
                try:
                    parent_name = t.parent_name
                    break
                except AttributeError:
                    continue
            l = Listing(None, None, parent_name=parent_name)
            l.things = list(things)
            return Wrapped(l)

        comment_dict = dict((cm._id, cm) for cm in comments)

        #convert tree into objects
        for k, v in comment_tree.iteritems():
            comment_tree[k] = [comment_dict[cid] for cid in comment_tree[k]]

        items = []
        extra = {}
        top = None
        dont_collapse = []
        #loading a portion of the tree
        if isinstance(self.comment, utils.iters):
            candidates = []
            candidates.extend(self.comment)
            dont_collapse.extend(cm._id for cm in self.comment)
            #assume the comments all have the same parent
            # TODO: removed by Chris to get rid of parent being sent
            # when morecomments is used.
            #if hasattr(candidates[0], "parent_id"):
            #    parent = comment_dict[candidates[0].parent_id]
            #    items.append(parent)
        #if permalink
        elif self.comment:
            top = self.comment
            dont_collapse.append(top._id)
            #add parents for context
            while self.context > 0 and hasattr(top, 'parent_id'):
                self.context -= 1
                new_top = comment_dict[top.parent_id]
                comment_tree[new_top._id] = [top]
                num_children[new_top._id] = num_children[top._id] + 1
                dont_collapse.append(new_top._id)
                top = new_top
            candidates = [top]
        #else start with the root comments
        else:
            candidates = []
            candidates.extend(comment_tree.get(top, ()))

        #update the starting depth if required
        if top and depth[top._id] > 0:
            delta = depth[top._id]
            for k, v in depth.iteritems():
                depth[k] = v - delta

        def sort_candidates():
            candidates.sort(key=self.sort_key, reverse=self.rev_sort)

        #find the comments
        num_have = 0
        sort_candidates()
        while num_have < num and candidates:
            to_add = candidates.pop(0)
            comments.remove(to_add)
            if to_add._deleted and not comment_tree.has_key(to_add._id):
                pass
            elif depth[to_add._id] < MAX_RECURSION:
                #add children
                if comment_tree.has_key(to_add._id):
                    candidates.extend(comment_tree[to_add._id])
                    sort_candidates()
                items.append(to_add)
                num_have += 1
            else:
                #add the recursion limit
                p_id = to_add.parent_id
                w = Wrapped(MoreRecursion(self.link, 0, comment_dict[p_id]))
                w.children.append(to_add)
                extra[p_id] = w

        wrapped = self.wrap_items(items)

        cids = dict((cm._id, cm) for cm in wrapped)

        final = []
        #make tree

        for cm in wrapped:
            # don't show spam with no children
            if cm.deleted and not comment_tree.has_key(cm._id):
                continue
            cm.num_children = num_children[cm._id]
            if cm.collapsed and cm._id in dont_collapse:
                cm.collapsed = False
            parent = cids.get(cm.parent_id) \
                if hasattr(cm, 'parent_id') else None
            if parent:
                if not hasattr(parent, 'child'):
                    parent.child = empty_listing()
                parent.child.parent_name = parent._fullname
                parent.child.things.append(cm)
            else:
                final.append(cm)

        #put the extras in the tree
        for p_id, morelink in extra.iteritems():
            parent = cids[p_id]
            parent.child = empty_listing(morelink)
            parent.child.parent_name = parent._fullname

        #put the remaining comments into the tree (the show more comments link)
        more_comments = {}
        while candidates:
            to_add = candidates.pop(0)
            direct_child = True
            #ignore top-level comments for now
            if not hasattr(to_add, 'parent_id'):
                p_id = None
            else:
                #find the parent actually being displayed
                #direct_child is whether the comment is 'top-level'
                p_id = to_add.parent_id
                while p_id and not cids.has_key(p_id):
                    p = comment_dict[p_id]
                    if hasattr(p, 'parent_id'):
                        p_id = p.parent_id
                    else:
                        p_id = None
                    direct_child = False

            mc2 = more_comments.get(p_id)
            if not mc2:
                mc2 = MoreChildren(self.link,
                                   depth[to_add._id],
                                   parent=comment_dict.get(p_id))
                more_comments[p_id] = mc2
                w_mc2 = Wrapped(mc2)
                if p_id is None:
                    final.append(w_mc2)
                else:
                    parent = cids[p_id]
                    if hasattr(parent, 'child'):
                        parent.child.things.append(w_mc2)
                    else:
                        parent.child = empty_listing(w_mc2)
                        parent.child.parent_name = parent._fullname

            #add more children
            if comment_tree.has_key(to_add._id):
                candidates.extend(comment_tree[to_add._id])

            if direct_child:
                mc2.children.append(to_add)

            mc2.count += 1

        return final