Example #1
0
def posts(request, payload={}, short_id=None):
    """
    Posts endpoint of the example.com public api

    Request with an id parameter:

        /public_api/posts/1qkx8

    POST JSON in the following format:

        POST /public_api/posts/
        {"ids":["1qkx8","ma6fz"]}
    """
    Metrics.api_comment.record(request)
    ids = payload.get('ids')

    if short_id and not ids:
        try:
            comment = Comment.details_by_id(long_id(short_id), promoter=PublicAPICommentDetails)
            (comment,) = CachedCall.multicall([comment])
            return comment.to_client()
        except (ObjectDoesNotExist, util.Base36DecodeException):
            raise ServiceError("Post not found")

    elif ids:
        ids = [long_id(x) for x in set(ids)]
        calls = [Comment.details_by_id(id, ignore_not_found=True, promoter=PublicAPICommentDetails) for id in ids]
        comments = CachedCall.multicall(calls, skip_decorator=True)
        return {'posts': [x.to_client() for x in comments if x]}
Example #2
0
def create_comment(**kwargs):
    kwargs['author'] = kwargs.get('author', create_user())
    kwargs['timestamp'] = kwargs.get('timestamp', Services.time.time())
    kwargs['anonymous'] = kwargs.get('anonymous', False)
    if kwargs.get('parent_comment') is None:
        kwargs['title'] = kwargs.get('title', 'Sample title.')
    comment = Comment(**kwargs)
    comment.save()
    return comment
def create_comment(**kwargs):
    kwargs["author"] = kwargs.get("author", create_user())
    kwargs["timestamp"] = kwargs.get("timestamp", Services.time.time())
    kwargs["anonymous"] = kwargs.get("anonymous", False)
    if kwargs.get("parent_comment") is None:
        kwargs["title"] = kwargs.get("title", "Sample title.")
    comment = Comment(**kwargs)
    comment.save()
    return comment
Example #4
0
def _get_hot_slice_by_threads(rlbb, nav_slice):
    ops_and_scores = rlbb.with_scores[nav_slice] if rlbb else []

    ops = [Comment(id=id) for (id, score) in ops_and_scores]

    max_from_thread = 3

    # Got the OPs, now I need to bulk fetch the top replies.
    pipeline = redis.pipeline()
    for comment in ops:
        pipeline.get(comment.redis_score.key)

    for comment in ops:
        pipeline.zrevrange(comment.popular_replies.key, 0, max_from_thread - 1, withscores=True)

    results = pipeline.execute()

    op_scores, pop_reply_lists = results[:len(ops)], results[len(ops):]

    ids = []

    if ops_and_scores:
        # Lowest score sets the threshold, but replies get a "boost" factor
        cutoff = ops_and_scores[-1][1] / Config.get('reply_boost', 1)
        for op, op_score, pop_replies in zip(ops, op_scores, pop_reply_lists):
            items = [(int(id), float(score or 0)) for (id,score) in [(op.id, op_score)] + pop_replies]
            items.sort(key=lambda (id, score): -score)
            ids += [id for (id, score) in items if score >= cutoff][:max_from_thread]

    return ids
Example #5
0
def post_quest_idea(request, title, content, name, email):
    user = User.objects.get(username=settings.QUEST_IDEAS_USERNAME)

    text = "Name: {}\n\nEmail: {}".format(name, email)

    replied_comment, parent_comment, reply_content, external_content, category, title = validate_and_clean_comment(
        user,
        reply_text=text,
        reply_content=content,
        title=title,
    )

    comment = Comment.create_and_post(
        request,
        user,
        False,
        category,
        reply_content,
        parent_comment=parent_comment,
        reply_text=text,
        replied_comment=replied_comment,
        external_content=external_content,
        fact_metadata={},
        title=title,
    )

    return {'comment': comment.details()}
Example #6
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
Example #7
0
 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]
Example #8
0
 def brief_replies(self):
     ids = [x for x in self.replies.values_list('id', flat=True)]
     calls = [
         Comment.details_by_id(id, promoter=BriefPublicAPICommentDetails)
         for id in ids
     ]
     return CachedCall.multicall(calls, skip_decorator=True)
Example #9
0
def post_quest_idea(request, title, content, name, email):
    user = User.objects.get(username=settings.QUEST_IDEAS_USERNAME)

    text = "Name: {}\n\nEmail: {}".format(name, email)

    replied_comment, parent_comment, reply_content, external_content, category, title = validate_and_clean_comment(
        user,
        reply_text=text,
        reply_content=content,
        title=title,
    )

    comment = Comment.create_and_post(
        request,
        user,
        False,
        category,
        reply_content,
        parent_comment=parent_comment,
        reply_text=text,
        replied_comment=replied_comment,
        external_content=external_content,
        fact_metadata={},
        title=title,
    )

    return {'comment': comment.details()}
def news_img(url):
    token = os.path.basename(urlparse.urlparse(url).path)
    if "reply" in url:
        post_id = int(token)
    else:
        post_id = util.base36decode_or_404(token)
        
    img_url = Comment.details_by_id(post_id)()['reply_content']['thumbnail']['name']
    return "<a href='%s'><img src='http://example.com/ugc/%s'></a>" % (url, img_url)
Example #11
0
 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
Example #12
0
 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
Example #13
0
def news_img(url):
    token = os.path.basename(urlparse.urlparse(url).path)
    if "reply" in url:
        post_id = int(token)
    else:
        post_id = util.base36decode_or_404(token)

    img_url = Comment.details_by_id(
        post_id)()['reply_content']['thumbnail']['name']
    return "<a href='%s'><img src='http://example.com/ugc/%s'></a>" % (url,
                                                                       img_url)
Example #14
0
 def from_shared_op_details_with_viewer_stickers(cls, viewer, top_details, reply_details):
     """
     `top_details` is a single CommentDetails instance of the monster top.
     `reply_details` is a list of CommentDetails instances, of the monster replies/bottoms.
     """
     tiles = []
     for bottom in reply_details:
         tile = cls(bottom, top_details)
         tile.viewer_sticker = Comment.get_sticker_from_user_for_comment_id(bottom.id, viewer)
         tiles.append(tile)
     return tiles
def get_img_url(url, image_size="thumbnail"):
    token = os.path.basename(urlparse.urlparse(url).path)
    if "reply" in url:
        post_id = int(token)
    else:
        post_id = util.base36decode_or_404(token)

    try:
        img_url = Comment.details_by_id(post_id)().reply_content[image_size]['name']
    except (KeyError, Comment.DoesNotExist):
        img_url = ""
    return img_url
Example #16
0
    def is_pinned(self, viewer):
        """
        `viewer` is the user who will see these comments. It *must* come from a request object,
        in order for `is_authenticated` to make sense. If they're logged out, they won't see pins.
        """
        if not viewer.is_authenticated():
            return False

        if self.pins is None:
            self.pins = Comment.pins_by_id(self._comment_details.thread_op_comment_id)()

        return viewer.id in self.pins
Example #17
0
 def from_shared_op_details_with_viewer_stickers(cls, viewer, top_details,
                                                 reply_details):
     """
     `top_details` is a single CommentDetails instance of the monster top.
     `reply_details` is a list of CommentDetails instances, of the monster replies/bottoms.
     """
     tiles = []
     for bottom in reply_details:
         tile = cls(bottom, top_details)
         tile.viewer_sticker = Comment.get_sticker_from_user_for_comment_id(
             bottom.id, viewer)
         tiles.append(tile)
     return tiles
Example #18
0
def suggested_users(request):
    user_list = []
    users = sample(SUGGESTED_USERS, 5)
    users = list(User.objects.filter(username__in=users, is_active=True))
    for user in users:
        if user.userinfo.profile_image is not None:
            avatar_comment = Comment.details_by_id(
                user.userinfo.profile_image.id)()
        else:
            avatar_comment = None

        is_following = False
        try:
            is_following = request.user.is_following(user)
        except AttributeError:
            pass

        user_list.append({
            'user': user,
            'avatar_comment': avatar_comment,
            'is_following': is_following,
            'is_self': request.user == user,
        })

    topics = sample(SUGGESTED_TOPICS, 5)
    topics = [{'name': topic} for topic in topics]
    topic_previews = Content.all_objects.filter(
        id__in=SUGGESTED_TOPIC_PREVIEWS.values())

    topic_previews = CachedCall.multicall(
        [preview.details for preview in topic_previews])

    preview_mapping = dict([(content['id'], content)
                            for content in topic_previews])

    try:
        followed_tags = request.user.redis.followed_tags
    except AttributeError:
        followed_tags = []

    for topic in topics:
        topic['preview'] = preview_mapping.get(
            SUGGESTED_TOPIC_PREVIEWS[topic['name']])
        topic['is_following'] = topic['name'] in followed_tags

    ctx = {
        'request': request,
        'users': user_list,
        'topics': topics,
    }
    return r2r_jinja('onboarding/suggested_users.html', ctx, request)
Example #19
0
def post_comment(request, user, post_data, persist_url=True):
    reply_text = post_data.get('reply_text', '')

    try:
        replied_comment, parent_comment, reply_content, external_content, category, title = (
            validate_and_clean_comment(
                user,
                reply_text=reply_text,
                parent_comment=post_data.get('parent_comment'),
                replied_comment=post_data.get('replied_comment'),
                reply_content=post_data.get('reply_content'),
                category=post_data.get('category'),
                external_content=post_data.get('external_content'),
                title=post_data.get('title'),
            ))

        post_anon = True
        if category and category == MONSTER_GROUP:
            post_anon = False

        comment = Comment.create_and_post(
            request,
            user,
            post_anon,  # Anonymous.
            category,
            reply_content,
            parent_comment=parent_comment,
            reply_text=reply_text,
            replied_comment=replied_comment,
            external_content=external_content,
            title=title,
        )

        post_pending_url = comment.details().url

        if persist_url:
            if category and category.name == MONSTER_GROUP:
                post_pending_url = '/monster/{0}'.format(
                    base36encode(comment.thread.op.id))
            user.kv.post_pending_signup_url.set(post_pending_url)

        return comment

    except ServiceError, e:
        # Silently drop the post if an error occurs.
        # We tried validating it prior to posting, but something went wrong between then and now
        # and it no longer validates. Should be rare.
        Metrics.logged_out_reply_dropped.record(request,
                                                extra_info=extra_info,
                                                service_error=e)
def news_img(url):
    token = os.path.basename(urlparse.urlparse(url).path)
    if "reply" in url:
        post_id = int(token)
    else:
        post_id = util.base36decode_or_404(token)

    try:
        comment_details = Comment.details_by_id(post_id)()
        img_url = comment_details.reply_content['thumbnail']['name']
        url = comment_details.url
    except (KeyError, Comment.DoesNotExist):
        img_url = ""

    return "<a href='%s'><img class='content' src='%s'></a>" % (url, img_url)
Example #21
0
def suggested_users(request):
    user_list = []
    users = sample(SUGGESTED_USERS, 5)
    users = list(User.objects.filter(username__in=users, is_active=True))
    for user in users:
        if user.userinfo.profile_image is not None:
            avatar_comment = Comment.details_by_id(user.userinfo.profile_image.id)()
        else:
            avatar_comment = None

        is_following = False
        try:
            is_following = request.user.is_following(user)
        except AttributeError:
            pass

        user_list.append({
            'user'              : user,
            'avatar_comment'    : avatar_comment,
            'is_following'      : is_following,
            'is_self'           : request.user == user,
        })

    topics = sample(SUGGESTED_TOPICS, 5)
    topics = [{'name': topic} for topic in topics]
    topic_previews = Content.all_objects.filter(id__in=SUGGESTED_TOPIC_PREVIEWS.values())

    topic_previews = CachedCall.multicall([preview.details for preview in topic_previews])

    preview_mapping = dict([(content['id'], content) for content in topic_previews])

    try:
        followed_tags = request.user.redis.followed_tags
    except AttributeError:
        followed_tags = []

    for topic in topics:
        topic['preview'] = preview_mapping.get(SUGGESTED_TOPIC_PREVIEWS[topic['name']])
        topic['is_following'] = topic['name'] in followed_tags

    ctx = {
        'request': request,
        'users': user_list,
        'topics': topics,
    }
    return r2r_jinja('onboarding/suggested_users.html', ctx, request)
Example #22
0
def post_comment(request, user, post_data, persist_url=True):
    reply_text = post_data.get("reply_text", "")

    try:
        replied_comment, parent_comment, reply_content, external_content, category, title = validate_and_clean_comment(
            user,
            reply_text=reply_text,
            parent_comment=post_data.get("parent_comment"),
            replied_comment=post_data.get("replied_comment"),
            reply_content=post_data.get("reply_content"),
            category=post_data.get("category"),
            external_content=post_data.get("external_content"),
            title=post_data.get("title"),
        )

        post_anon = True
        if category and category == MONSTER_GROUP:
            post_anon = False

        comment = Comment.create_and_post(
            request,
            user,
            post_anon,  # Anonymous.
            category,
            reply_content,
            parent_comment=parent_comment,
            reply_text=reply_text,
            replied_comment=replied_comment,
            external_content=external_content,
            title=title,
        )

        post_pending_url = comment.details().url

        if persist_url:
            if category and category.name == MONSTER_GROUP:
                post_pending_url = "/monster/{0}".format(base36encode(comment.thread.op.id))
            user.kv.post_pending_signup_url.set(post_pending_url)

        return comment

    except ServiceError, e:
        # Silently drop the post if an error occurs.
        # We tried validating it prior to posting, but something went wrong between then and now
        # and it no longer validates. Should be rare.
        Metrics.logged_out_reply_dropped.record(request, extra_info=extra_info, service_error=e)
def get_info(user):
    comment_id = user.kv.last_sticker_comment_id.get()
    if comment_id:
        from canvas.models import Comment
        details = Comment.details_by_id(comment_id)()
        url = details.url
    else:
        url = None
    
    level = user.kv.sticker_level.get()
    schedule = economy.sticker_schedule(level)
    
    return {
        'type_id': user.kv.last_sticker_type_id.get(),
        'timestamp': user.kv.last_sticker_timestamp.get(),
        'comment_id': comment_id,
        'url': url,
        'level': level, 
        'level_progress': user.kv.sticker_inbox.get(),
        'level_total': schedule,            
    }
Example #24
0
def get_info(user):
    comment_id = user.kv.last_sticker_comment_id.get()
    if comment_id:
        from canvas.models import Comment
        details = Comment.details_by_id(comment_id)()
        url = details.url
    else:
        url = None

    level = user.kv.sticker_level.get()
    schedule = economy.sticker_schedule(level)

    return {
        'type_id': user.kv.last_sticker_type_id.get(),
        'timestamp': user.kv.last_sticker_timestamp.get(),
        'comment_id': comment_id,
        'url': url,
        'level': level,
        'level_progress': user.kv.sticker_inbox.get(),
        'level_total': schedule,
    }
    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
Example #26
0
 def brief_replies(self):
     ids = [x for x in self.replies.values_list('id', flat=True)]
     calls = [Comment.details_by_id(id, promoter=BriefPublicAPICommentDetails) for id in ids]
     return CachedCall.multicall(calls, skip_decorator=True)
Example #27
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)
Example #28
0
 def from_id(cls, comment_id):
     """ Does not include user pins. """
     from canvas.models import Comment
     return Comment.details_by_id(comment_id)()
 def from_id(cls, comment_id):
     """ Does not include user pins. """
     from canvas.models import Comment
     return Comment.details_by_id(comment_id)()
 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]
Example #31
0
 def from_comment_id(cls, comment_id):
     details = Comment.details_by_id(comment_id)()
     return cls(details)