def from_ids(cls, comment_ids): """ Returns a list of CommentDetails instances. Does not include user pins. """ from canvas.models import Comment details = [ Comment.details_by_id(comment_id) for comment_id in comment_ids ] return CachedCall.many_multicall(details)[0]
def api_monster_details(request, short_id, payload={}): view_data = CommentViewData(request, short_id) main_comment = view_data.op_comment replies = [Comment.details_by_id(cid) for cid in view_data.reply_ids] has_replies = len(replies) > 0 ( (main_comment,), replies ) = CachedCall.many_multicall( [main_comment], replies, ) treplies = [] made_bottom = False for reply in replies: cur = reply.to_client() if reply.real_author == request.user.username: cur['current_user_authored'] = made_bottom = True treplies.append(cur) ctx = { 'top': main_comment, 'bottoms': treplies, 'current_user_made_bottom': made_bottom, 'current_user_made_top': main_comment.real_author == request.user.username, 'start_content': Content.all_objects.get(id=Content.SMALL_DRAW_FROM_SCRATCH_PK).details(), } return ctx
def from_queryset(cls, comments): bottoms, tops = CachedCall.many_multicall([cmt.details for cmt in comments], [cmt.thread.op.details for cmt in comments]) tiles = [] for bottom, top in zip(bottoms, tops): tile = cls(bottom, top) tiles.append(tile) return tiles
def from_queryset_with_viewer_stickers(cls, viewer, comments): bottoms, tops = CachedCall.many_multicall([cmt.details for cmt in comments], [cmt.thread.op.details for cmt in comments]) tiles = [] for bottom, top in zip(bottoms, tops): tile = cls(bottom, top) tile.viewer_sticker = Comment.get_sticker_from_user_for_comment_id(bottom.id, viewer) tiles.append(tile) return tiles
def test_many_multicall(self): f1, f2, f3, f4, f5, f6 = [ CachedCall('key_%s' % n, CB(retvalue=n)) for n in [1, 2, 3, 4, 5, 6] ] self.assertEquals([[1], [2, 3], [4, 5, 6]], CachedCall.many_multicall([f1], [f2, f3], [f4, f5, f6]))
def get_from_comment(cls, comment): candidates = Comment.public.in_bulk_list([comment.id] + comment.top_replies[:10]) candidates = [post for post in candidates if post.reply_content] candidates = sorted(candidates, key=lambda comment: -comment.get_score()[0]) thread = [comment] + candidates[:5] (posts,) = CachedCall.many_multicall([post.details for post in thread]) return cls(*posts)
def from_queryset(cls, comments): bottoms, tops = CachedCall.many_multicall( [cmt.details for cmt in comments], [cmt.thread.op.details for cmt in comments]) tiles = [] for bottom, top in zip(bottoms, tops): tile = cls(bottom, top) tiles.append(tile) return tiles
def mobile_details_from_queryset(comments): bottoms, tops = CachedCall.many_multicall([cmt.details for cmt in comments], [cmt.thread.op.details for cmt in comments]) tiles = [] for bottom, top in zip(bottoms, tops): tile = { 'top': top, 'bottom': bottom, } tiles.append(tile) return tiles
def from_queryset_with_viewer_stickers(cls, viewer, comments): bottoms, tops = CachedCall.many_multicall( [cmt.details for cmt in comments], [cmt.thread.op.details for cmt in comments]) tiles = [] for bottom, top in zip(bottoms, tops): tile = cls(bottom, top) tile.viewer_sticker = Comment.get_sticker_from_user_for_comment_id( bottom.id, viewer) tiles.append(tile) return tiles
def get_from_comment(cls, comment): candidates = Comment.public.in_bulk_list([comment.id] + comment.top_replies[:10]) candidates = [post for post in candidates if post.reply_content] candidates = sorted(candidates, key=lambda comment: -comment.get_score()[0]) thread = [comment] + candidates[:5] (posts, ) = CachedCall.many_multicall( [post.details for post in thread]) return cls(*posts)
def mobile_details_from_queryset(comments): bottoms, tops = CachedCall.many_multicall( [cmt.details for cmt in comments], [cmt.thread.op.details for cmt in comments]) tiles = [] for bottom, top in zip(bottoms, tops): tile = { 'top': top, 'bottom': bottom, } tiles.append(tile) return tiles
def from_queryset_with_pins(cls, comments): """ Returns a list of tile details. This will preload this details object with pins, which is more efficient than loading them on demand. """ # Grab the pin data for this user and these comments. details, pins = CachedCall.many_multicall([cmt.details for cmt in comments], [cmt.thread.op.pins for cmt in comments]) tiles = [] for cmt, pins in zip(details, pins): tile = cls(cmt) tile.pins = pins tiles.append(tile) return tiles
def thread_context(self): top_reply_ids = self.top_reply_ids(force_show=features.thread_new(self.request)) ctx = { 'short_id': self.short_id, 'page': self.page, 'gotoreply': self.gotoreply, 'viewer_is_staff': self.request.user.is_authenticated() and self.request.user.is_staff, 'viewer_is_thread_author': self.is_author, 'root': '/p/', 'op_content': self.op_content, 'op_category': self.op_category, 'page': self.page, 'per_page': self.per_page, 'num_replies': self.num_replies, 'reply_ids': self.reply_ids, 'pages': self.pages, 'page_reply_ids': self.page_reply_ids, 'page_current': self.page_current, 'page_next': self.page_next, 'page_last': self.page_last, 'page_penultimate': self.page_penultimate, 'explicit_page_view': self.explicit_page_view, # Recent replies. 'recent_reply_ids': self.recent_reply_ids, 'top_reply_ids': top_reply_ids, } # Get all the replies in one query, then create the appropriate lists. _all_replies = Comment.visible.in_bulk(self.page_reply_ids + self.recent_reply_ids + top_reply_ids) _recent_replies = [_all_replies[cid] for cid in self.recent_reply_ids] _top_replies = filter(bool, [_all_replies.get(cid) for cid in top_reply_ids]) _replies = [_all_replies[cid] for cid in self.page_reply_ids] replyable = [self._op_comment] + _replies + _recent_replies + _top_replies # Get all comment ids (ids so 0 queries) that any of these comments are replies to, that aren't in this # page, so we can render them on hover. replied_ids = ([reply.replied_comment_id for reply in replyable if (reply.replied_comment_id and reply.replied_comment_id not in [r.id for r in replyable])]) ctx.update({ 'replied_ids': replied_ids, 'replyable': replyable, }) recent_replies = [reply.details for reply in _recent_replies] replies = [Comment.details_by_id(reply.id) for reply in _replies] replied = [Comment.details_by_id(cid) for cid in replied_ids] top_replies = [reply.details for reply in _top_replies] if self.request.user.is_authenticated(): ctx['user_infos'] = {'pinned': self.request.user.id in self._op_comment.pins()} if self.request.user.is_staff: ctx['admin_infos'] = {self._op_comment.id: self._op_comment.admin_info} # For replies we only use the username, so grab those all in one query and put them in admin_infos. ctx['usernames'] = Comment.visible.filter(id__in=_all_replies.keys()).values('id', 'author__username') for reply_dict in ctx['usernames']: ctx['admin_infos'][reply_dict['id']] = {'username': reply_dict['author__username']} ctx['replies_channel'] = self._op_comment.replies_channel.sync() # Get relevant sidebar data remix_of, reply_to = [], [] # Remix of if self._op_content and self._op_content.remix_of: op_remix_of_caption = self._op_content.remix_of.first_caption if op_remix_of_caption: remix_of = [op_remix_of_caption.details] ctx['op_remix_of_caption'] = op_remix_of_caption # Reply to if self._op_comment.parent_comment and self._op_comment.parent_comment.is_visible(): reply_to = [self._op_comment.parent_comment.details] ( (op_comment,), (linked_comment,), remix_of, reply_to, replies, recent_replies, top_replies, replied, ) = CachedCall.many_multicall( [self.op_comment], [self.linked_comment], remix_of, reply_to, replies, recent_replies, top_replies, replied, ) op_comment.is_my_post = bool(self._op_comment.author == self.request.user) op_comment.moderated = op_comment.visibility not in Visibility.public_choices linked_comment = TemplateComment(linked_comment, is_op=(linked_comment.id == op_comment.id), request=self.request, title=op_comment.title) if self.page_current == 1 and op_comment.has_content(): first_comment_with_content = op_comment else: first_comment_with_content = None for reply in replies: if reply.has_content(): first_comment_with_content = reply break last_comment_with_content = None for reply in reversed(replies): if reply.has_content(): last_comment_with_content = reply break comment_to_expand = first_comment_with_content if self.gotoreply: comment_to_expand = linked_comment ctx.update({ 'op_comment': op_comment, 'linked_comment': linked_comment, 'remix_of': remix_of, 'reply_to': reply_to, 'replies': replies, 'recent_replies': recent_replies, 'top_replies': top_replies, 'top_remixes': [reply for reply in top_replies if reply.has_content()], 'replied': replied, 'linked_comment': linked_comment, 'large_thread_view': len(replies) >= 50, 'title': getattr(op_comment, 'title', None), 'first_comment_with_content': first_comment_with_content, 'last_comment_with_content': last_comment_with_content, 'comment_to_expand': comment_to_expand, }) return ctx
def test_many_multicall(self): f1, f2, f3, f4, f5, f6 = [CachedCall('key_%s' % n, CB(retvalue=n)) for n in [1,2,3,4,5,6]] self.assertEquals([[1], [2,3], [4,5,6]], CachedCall.many_multicall([f1], [f2, f3], [f4, f5, f6]))
def from_ids(cls, comment_ids): """ Returns a list of CommentDetails instances. Does not include user pins. """ from canvas.models import Comment details = [Comment.details_by_id(comment_id) for comment_id in comment_ids] return CachedCall.many_multicall(details)[0]
def view(request, short_id, option=None): from apps.monster.jinja_tags import monster_image_tile view_data = CommentViewData(request, short_id) main_comment = view_data.op_comment replies = [Comment.details_by_id(cid) for cid in view_data.reply_ids] has_replies = len(replies) > 0 complete_link = option and (option == 'complete') if complete_link and request.user.is_anonymous(): fact.record('monster_start_flow', request, {'monster_id': short_id}) reply_id = None if option: try: reply_id = int(option) except ValueError: pass ( (main_comment,), replies ) = CachedCall.many_multicall( [main_comment], replies, ) replies = [reply for reply in replies if not reply.is_collapsed] monster_part = MonsterPart.get_by_comment(main_comment) main_comment_details = main_comment main_comment = TileDetails(main_comment) made_bottom = False made_top = main_comment.comment.real_author == request.user.username linked_monster_footer_image = "" current_monster_index = 0 for i in range(len(replies)): reply = replies[i] if reply_id is not None and reply.id == int(reply_id): current_monster_index = i elif reply.real_author == request.user.username and reply_id is None: current_monster_index = i made_bottom = True try: if (has_replies and replies[current_monster_index].reply_content and replies[current_monster_index].reply_content.footer): linked_monster_footer_image = replies[current_monster_index].reply_content.footer['name'] except (AttributeError, IndexError): pass made_part = made_top or made_bottom if made_part: CompletedMonsterSet(request.user).sadd(main_comment.comment.id) can_make_bottom = (not made_part) and complete_link can_invite = made_top # incomplete monster without an invite link, send to monster index if not has_replies and not complete_link and not can_invite: return HttpResponseRedirect('/monster') ctx = { 'can_invite': can_invite, 'can_make_bottom': can_make_bottom, 'current_monster_index': current_monster_index, 'domain': settings.DOMAIN, 'made_bottom': made_bottom, 'made_part': made_part, 'made_top': made_top, 'main_comment': main_comment, 'monster_content': main_comment.comment.reply_content, 'og_image_url': linked_monster_footer_image.replace("https", "http", 1), 'monster_group': MONSTER_GROUP, 'monster_name': main_comment.comment.title, 'replies': MonsterTileDetails.from_shared_op_details_with_viewer_stickers(request.user, main_comment_details, replies), 'request': request, 'short_id': main_comment.comment.short_id(), 'start_content': Content.all_objects.get(id=Content.SMALL_DRAW_FROM_SCRATCH_PK).details(), } return r2r_jinja('monster/view.html', ctx)