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
def sponsor_wrapper(link): w = Wrapped(link) w.render_class = PromotedLink w.rowstyle = "promoted link" return w
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))
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
def wrap_promoted(link): if not isinstance(link, Wrapped): link = Wrapped(link) return link
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()
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
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
def listing(self): res = Listing.listing(self) for t in res.things: t.num = "" return Wrapped(self)
def GET_about(self, vuser): if not is_api() or not vuser: return self.abort404() return Reddit(content = Wrapped(vuser)).render()
def __init__(self, a = None): if a: a = Wrapped(a) Wrapped.__init__(self, a = a, datefmt = datefmt)
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
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