Example #1
0
    def test_replied_tells_author_and_op_author(self):
        # Some dude starts a thread
        author = create_user()
        content = create_content()
        op = create_comment(author=author, reply_content=content)

        # Another dude posts a reply
        guest_author = create_user()
        reply = create_comment(replied_comment=op, author=guest_author, parent_comment=op)
        
        self.assertEqual(reply.thread.op, op)

        # A third dude replies to the guest author
        guest_author_2 = create_user()
        reply_2 = create_comment(replied_comment=reply, author=guest_author_2, parent_comment=op)

        self.assertTrue(reply_2.thread.op.author, author)

        # Issue the action
        pn = Actions.replied(guest_author_2, reply_2)

        notifications = expander.expand(pn)
        print notifications
        # Now, we should tell both the OP author, and the guest author.
        notifications = filter(lambda n: n.channel == 'EmailChannel', notifications)
        self.assertEqual(len(notifications), 2)
        n1 = notifications[0]
        n2 = notifications[1]

        self.assertEqual(n1.action, 'replied')
        self.assertEqual(n1.recipient, guest_author)

        self.assertEqual(n2.action, 'thread_replied')
        self.assertEqual(n2.recipient, author)
Example #2
0
    def test_details_repost_zero_for_text_posts(self):
        # These will have the same reply_content_id (None), but we should handle that and not count them as reposts.
        cmt = create_comment()
        cmt2 = create_comment()

        self.assertEqual(cmt.details().repost_count, 0)
        self.assertEqual(cmt2.details().repost_count, 0)
Example #3
0
    def test_details_repost_zero_for_text_posts(self):
        # These will have the same reply_content_id (None), but we should handle that and not count them as reposts.
        cmt = create_comment()
        cmt2 = create_comment()

        self.assertEqual(cmt.details().repost_count, 0)
        self.assertEqual(cmt2.details().repost_count, 0)
    def test_replies_have_no_op_class(self):
        comment = create_comment(reply_content=create_content())
        reply = create_comment(reply_content=create_content(), parent_comment=comment)
        reply2 = create_comment(reply_content=create_content(), parent_comment=comment)
        posts = self._render_replies([reply, reply2])

        self.assertNumCssMatches(0, posts, '.op.expanded')
Example #5
0
    def test_details_repost_one_for_repost(self):
        content = create_content()
        cmt = create_comment(reply_content=content)
        cmt2 = create_comment(reply_content=content)

        self.assertEqual(cmt.details.force().repost_count, 0)
        self.assertEqual(cmt2.details().repost_count, 1)
Example #6
0
    def test_details_repost_one_for_repost(self):
        content = create_content()
        cmt = create_comment(reply_content=content)
        cmt2 = create_comment(reply_content=content)

        self.assertEqual(cmt.details.force().repost_count, 0)
        self.assertEqual(cmt2.details().repost_count, 1)
Example #7
0
    def test_replies_have_no_op_class(self):
        comment = create_comment(reply_content=create_content(), reply_text="hello friends")
        reply = create_comment(reply_text="reply!", parent_comment=comment)
        reply2 = create_comment(reply_text="another", parent_comment=comment)
        posts = self._render_replies([reply, reply2])

        self.assertNumCssMatches(0, posts, '.op.expanded')
Example #8
0
    def test_page_loads_with_feed_item(self):
        user = create_staff()
        other = create_user()
        user.follow(other)

        create_comment(author=other, reply_content=create_content())

        self.assertStatus(200, '/feed', user=user)
Example #9
0
    def test_replies_have_no_op_class(self):
        comment = create_comment(reply_content=create_content(),
                                 reply_text="hello friends")
        reply = create_comment(reply_text="reply!", parent_comment=comment)
        reply2 = create_comment(reply_text="another", parent_comment=comment)
        posts = self._render_replies([reply, reply2])

        self.assertNumCssMatches(0, posts, '.op.expanded')
Example #10
0
    def test_text_reply(self):
        comment = create_comment(reply_content=create_content(), reply_text="hello friends")
        reply = create_comment(reply_text="reply!", parent_comment=comment)
        post = self._render_post(reply, fullsize=False)

        self.assertNumCssMatches(0, post, '.has_content')
        self.assertNumCssMatches(0, post, '.op.expanded')
        self.assertNumCssMatches(0, post, 'div.reply_content')
Example #11
0
    def test_details_replies_two_replies(self):
        cmt = create_comment()
        first = create_comment(parent_comment=cmt, timestamp=1)
        second = create_comment(parent_comment=cmt, timestamp=2)
        d = cmt.details()

        self.assertEqual(d.reply_count, 2)
        self.assertEqual(d.last_reply_id, second.id)
        self.assertEqual(d.last_reply_time, second.timestamp)
Example #12
0
    def test_actor(self):
        author = create_user()
        op = create_comment(author=author, reply_content=create_content())

        replier = create_user()
        reply = create_comment(replied_comment=op, author=replier)

        activity = ThreadReplyActivity.from_comment(replier, reply)
        self.assertEqual(activity.actor['id'], replier.id)
Example #13
0
    def test_actor(self):
        author = create_user()
        op = create_comment(author=author, reply_content=create_content())

        replier = create_user()
        reply = create_comment(replied_comment=op, author=replier)

        activity = ThreadReplyActivity.from_comment(replier, reply)
        self.assertEqual(activity.actor['id'], replier.id)
Example #14
0
    def test_text_reply(self):
        comment = create_comment(reply_content=create_content(),
                                 reply_text="hello friends")
        reply = create_comment(reply_text="reply!", parent_comment=comment)
        post = self._render_post(reply, fullsize=False)

        self.assertNumCssMatches(0, post, '.has_content')
        self.assertNumCssMatches(0, post, '.op.expanded')
        self.assertNumCssMatches(0, post, 'div.reply_content')
Example #15
0
    def test_details_replies_two_replies(self):
        cmt = create_comment()
        first = create_comment(parent_comment=cmt, timestamp=1)
        second = create_comment(parent_comment=cmt, timestamp=2)
        d = cmt.details()

        self.assertEqual(d.reply_count, 2)
        self.assertEqual(d.last_reply_id, second.id)
        self.assertEqual(d.last_reply_time, second.timestamp)
    def test_replies_have_no_op_class(self):
        comment = create_comment(reply_content=create_content())
        reply = create_comment(reply_content=create_content(),
                               parent_comment=comment)
        reply2 = create_comment(reply_content=create_content(),
                                parent_comment=comment)
        posts = self._render_replies([reply, reply2])

        self.assertNumCssMatches(0, posts, '.op.expanded')
Example #17
0
    def test_details_replies_one_reply(self):
        with override_service('time', FakeTimeProvider):
            cmt = create_comment()
            Services.time.step()
            child = create_comment(parent_comment=cmt)
            d = cmt.details()

            self.assertEqual(d.reply_count, 1)
            self.assertEqual(d.last_reply_id, child.id)
            self.assertAlmostEqual(d.last_reply_time, child.timestamp, places=4)
Example #18
0
    def test_anonymous_actor(self):
        author = create_user()
        op = create_comment(author=author, reply_content=create_content())

        for anon in [True, False]:
            replier = create_user()
            reply = create_comment(replied_comment=op, author=replier, anonymous=anon)

            activity = ThreadReplyActivity.from_comment(replier, reply)
            self.assertEqual(activity.is_actor_anonymous, anon)
Example #19
0
 def test_get_multiple_posts_by_post(self):
     comment = create_comment()
     comment1 = create_comment()
     id_list = [short_id(x.id) for x in [comment, comment1]]
     req = {'ids': id_list}
     result = self.post('/public_api/posts/', data=req)
     self.assertAPISuccess(result)
     json = util.loads(result.content)
     self.assertEqual(2, len(json['posts']))
     self.check_comment(comment.details(), json['posts'][0])
Example #20
0
 def test_get_multiple_posts_by_post(self):
     comment = create_comment()
     comment1 = create_comment()
     id_list = [short_id(x.id) for x in [comment, comment1]]
     req = {'ids': id_list}
     result = self.post('/public_api/posts/', data=req)
     self.assertAPISuccess(result)
     json = util.loads(result.content)
     self.assertEqual(2, len(json['posts']))
     self.check_comment(comment.details(), json['posts'][0])
Example #21
0
    def test_details_repost_zero_for_different_content(self):
        content = create_content()
        cmt = create_comment(reply_content=content)

        self.assertEqual(cmt.details().repost_count, 0)

        content2 = create_content()
        cmt2 = create_comment(reply_content=content2)

        self.assertEqual(cmt.details.force().repost_count, 0)
        self.assertEqual(cmt2.details().repost_count, 0)
Example #22
0
    def test_details_repost_zero_for_different_content(self):
        content = create_content()
        cmt = create_comment(reply_content=content)

        self.assertEqual(cmt.details().repost_count, 0)

        content2 = create_content()
        cmt2 = create_comment(reply_content=content2)

        self.assertEqual(cmt.details.force().repost_count, 0)
        self.assertEqual(cmt2.details().repost_count, 0)
Example #23
0
    def test_details_replies_two_replies_last_curated(self):
        # The last reply should include curated replies to prevent "stuck" active/pinned curated OPs auto-curating
        # their replies.
        cmt = create_comment()
        first = create_comment(parent_comment=cmt, timestamp=1)
        second = create_comment(parent_comment=cmt, timestamp=2)
        second.moderate_and_save(Visibility.CURATED, second.author)
        d = cmt.details()

        self.assertEqual(d.reply_count, 2)
        self.assertEqual(d.last_reply_id, second.id)
        self.assertEqual(d.last_reply_time, second.timestamp)
Example #24
0
    def test_details_replies_two_replies_last_curated(self):
        # The last reply should include curated replies to prevent "stuck" active/pinned curated OPs auto-curating
        # their replies.
        cmt = create_comment()
        first = create_comment(parent_comment=cmt, timestamp=1)
        second = create_comment(parent_comment=cmt, timestamp=2)
        second.moderate_and_save(Visibility.CURATED, second.author)
        d = cmt.details()

        self.assertEqual(d.reply_count, 2)
        self.assertEqual(d.last_reply_id, second.id)
        self.assertEqual(d.last_reply_time, second.timestamp)
Example #25
0
    def test_anonymous_actor(self):
        author = create_user()
        op = create_comment(author=author, reply_content=create_content())

        for anon in [True, False]:
            replier = create_user()
            reply = create_comment(replied_comment=op,
                                   author=replier,
                                   anonymous=anon)

            activity = ThreadReplyActivity.from_comment(replier, reply)
            self.assertEqual(activity.is_actor_anonymous, anon)
Example #26
0
    def test_details_replies_one_reply(self):
        with override_service('time', FakeTimeProvider):
            cmt = create_comment()
            Services.time.step()
            child = create_comment(parent_comment=cmt)
            d = cmt.details()

            self.assertEqual(d.reply_count, 1)
            self.assertEqual(d.last_reply_id, child.id)
            self.assertAlmostEqual(d.last_reply_time,
                                   child.timestamp,
                                   places=4)
Example #27
0
    def test_details_repost_zero_for_audio_remix(self):
        content = create_content()
        cmt = create_comment(reply_content=content)
        cmt2 = create_comment(reply_content=content)
        external_content = ExternalContent.from_dict(
            dict(type="yt",
                 end_time=10.0,
                 start_time=0.0,
                 source_url="123445555"))
        external_content.parent_comment = cmt2
        external_content.save()

        self.assertEqual(cmt2.details().repost_count, 0)
    def test_delivering_email_records_email_sent_metric(self):
        with override_service('experiment_placer', FakeExperimentPlacer, kwargs={'email_notifications': 'experimental'}):
            user = create_user(email="*****@*****.**")

            comment = create_comment()
            comment2 = create_comment(author=user, replied_comment=comment)

            pn = Actions.replied(user, comment2)
            notification = Notification.from_pending_notification(pn, user, "EmailChannel")
            channel = EmailChannel()

            with override_service('metrics', FakeMetrics):
                channel.deliver(notification)
                self.assertEqual(1, len(Services.metrics.email_sent.records))
Example #29
0
    def test_details_repost_zero_for_audio_remix(self):
        content = create_content()
        cmt = create_comment(reply_content=content)
        cmt2 = create_comment(reply_content=content)
        external_content = ExternalContent.from_dict(dict(
            type="yt",
            end_time=10.0,
            start_time=0.0,
            source_url="123445555"
        ))
        external_content.parent_comment = cmt2
        external_content.save()

        self.assertEqual(cmt2.details().repost_count, 0)
    def test_replied_to_own_thread_does_not_email_self(self):
        user = create_user()
        comment = create_comment(author=user)

        assert comment.author == user

        reply = create_comment(replied_comment=comment)
        assert comment == reply.replied_comment

        pn = Actions.replied(user, reply)
        notifications = expander.expand(pn)

        recipients = [n.recipient for n in notifications]
        self.assertNotIn(user, recipients)
Example #31
0
    def test_delivering_email_records_email_sent_metric(self):
        with override_service('experiment_placer', FakeExperimentPlacer, kwargs={'email_notifications': 'experimental'}):
            user = create_user(email="*****@*****.**")

            comment = create_comment()
            comment2 = create_comment(author=user, replied_comment=comment)

            pn = Actions.replied(user, comment2)
            notification = Notification.from_pending_notification(pn, user, "EmailChannel")
            channel = EmailChannel()

            with override_service('metrics', FakeMetrics):
                channel.deliver(notification)
                self.assertEqual(1, len(Services.metrics.email_sent.records))
Example #32
0
    def test_replied_to_own_thread_does_not_email_self(self):
        user = create_user()
        comment = create_comment(author=user)

        assert comment.author == user

        reply = create_comment(replied_comment=comment)
        assert comment == reply.replied_comment

        pn = Actions.replied(user, reply)
        notifications = expander.expand(pn)

        recipients = [n.recipient for n in notifications]
        self.assertNotIn(user, recipients)
Example #33
0
 def test_get_post_and_replies(self):
     comment = create_comment(anonymous=True)
     rep1 = create_comment(parent_comment=comment, replied_comment=comment)
     rep2 = create_comment(parent_comment=comment, replied_comment=rep1)
     rep3 = create_comment(parent_comment=comment, replied_comment=rep1)
     rep4 = create_comment(parent_comment=comment)
     reply_ids = [short_id(x.id) for x in [rep1, rep2, rep3, rep4]]
     req = {'ids': [short_id(comment.id)]}
     result = self.post('/public_api/posts/', data=req)
     self.assertAPISuccess(result)
     json = util.loads(result.content)
     self.assertEqual(1, len(json['posts']))
     post = json['posts'][0]
     for id in reply_ids:
         self.assertIn(id, [x['id'] for x in post['replies']])
Example #34
0
 def test_get_post_and_replies(self):
     comment = create_comment(anonymous=True)
     rep1 = create_comment(parent_comment=comment, replied_comment=comment)
     rep2 = create_comment(parent_comment=comment, replied_comment=rep1)
     rep3 = create_comment(parent_comment=comment, replied_comment=rep1)
     rep4 = create_comment(parent_comment=comment)
     reply_ids = [short_id(x.id) for x in [rep1, rep2, rep3, rep4]]
     req = {'ids': [short_id(comment.id)]}
     result = self.post('/public_api/posts/', data=req)
     self.assertAPISuccess(result)
     json = util.loads(result.content)
     self.assertEqual(1, len(json['posts']))
     post = json['posts'][0]
     for id in reply_ids:
         self.assertIn(id, [x['id'] for x in post['replies']])
Example #35
0
 def test_mute_thread_via_api(self):
     comment = create_comment()
     user = create_user()
     assert comment.id not in user.redis.muted_threads
     self.api_post('/api/comment/mute', {'comment_id': comment.id},
                   user=user)
     assert comment.id in user.redis.muted_threads
    def test_standard_reply(self):
        comment = create_comment(reply_content=create_content())
        post = self._render_post(comment, fullsize=False)

        self.assertNumCssMatches(1, post, '.has_content')
        self.assertNumCssMatches(0, post, '.op.expanded')
        self.assertNumCssMatches(1, post, 'div.reply_content')
Example #37
0
    def test_properties_dont_get_serialized(self):
        # CommentDetails should only serialize its dict contents, not any of its member properties.
        cmt = create_comment().details()
        cmt.test_foo_property = 1

        d = util.loads(util.dumps(cmt))
        self.assertFalse('test_foo_property' in d)
Example #38
0
 def after_setUp(self):
     self.inviter = create_user()
     self.invitee = create_user()
     self.comment = create_comment(
         author=self.inviter,
         reply_content=create_content(),
         category=create_group(name=MONSTER_MOBILE_GROUP))
Example #39
0
    def test_sticker_from_user(self):
        user = create_user()
        comment = create_comment()
        self.assertFalse(CommentSticker.get_sticker_from_user(comment.id, user))

        self.api_post('/api/sticker/comment', {'type_id': '1', 'comment_id': comment.id}, user=user)
        self.assertTrue(CommentSticker.get_sticker_from_user(comment.id, user))
    def test_sticker_from_user(self):
        user = create_user()
        comment = create_comment()
        self.assertFalse(CommentSticker.get_sticker_from_user(comment.id, user))

        self.api_post("/api/sticker/comment", {"type_id": "1", "comment_id": comment.id}, user=user)
        self.assertTrue(CommentSticker.get_sticker_from_user(comment.id, user))
Example #41
0
    def test_no_reply_notifications_for_inactive_users(self):
        author = create_user()

        comment = create_comment(author=author)

        action_func_list = [Actions.remixed,
                            Actions.replied,
                            Actions.thread_replied]

        # First make sure that the user can receive all possible "comment"
        # related notifications, like replied, remixed ... etc
        for func in action_func_list:
            pending_notification = func(author, comment)
            self.assertTrue(EmailChannel.enabled_for_recipient_action(pending_notification.action,
                                                             author,
                                                             pending_notification))

        author.is_active = False

        author.save()
        for func in action_func_list:
            pending_notification = func(author, comment)
            self.assertFalse(EmailChannel.enabled_for_recipient_action(pending_notification.action,
                                                             author,
                                                             pending_notification))
Example #42
0
    def test_properties_dont_get_serialized(self):
        # CommentDetails should only serialize its dict contents, not any of its member properties.
        cmt = create_comment().details()
        cmt.test_foo_property = 1

        d = util.loads(util.dumps(cmt))
        self.assertFalse('test_foo_property' in d)
    def test_standard_reply(self):
        comment = create_comment(reply_content=create_content())
        post = self._render_post(comment, fullsize=False)

        self.assertNumCssMatches(1, post, '.has_content')
        self.assertNumCssMatches(0, post, '.op.expanded')
        self.assertNumCssMatches(1, post, 'div.reply_content')
    def test_no_reply_notifications_for_inactive_users(self):
        author = create_user()

        comment = create_comment(author=author)

        action_func_list = [Actions.remixed,
                            Actions.replied,
                            Actions.thread_replied]

        # First make sure that the user can receive all possible "comment"
        # related notifications, like replied, remixed ... etc
        for func in action_func_list:
            pending_notification = func(author, comment)
            self.assertTrue(EmailChannel.enabled_for_recipient_action(pending_notification.action,
                                                             author,
                                                             pending_notification))

        author.is_active = False

        author.save()
        for func in action_func_list:
            pending_notification = func(author, comment)
            self.assertFalse(EmailChannel.enabled_for_recipient_action(pending_notification.action,
                                                             author,
                                                             pending_notification))
Example #45
0
 def test_get_user_by_username(self):
     user = create_user()
     comments = [create_comment(author=user) for x in range(3)]
     anon_comments = [create_comment(author=user, anonymous=True) for x in range(3)]
     username = user.username
     result = self.get('/public_api/users/{0}'.format(username))
     self.assertAPISuccess(result)
     result = self.post('/public_api/users/{0}'.format(username))
     self.assertAPISuccess(result)
     json = util.loads(result.content)
     self.check_user(user, json)
     returned_posts = [x['id'] for x in json['posts']]
     for c in comments:
         self.assertIn(short_id(c.id), returned_posts)
     for c in anon_comments:
         self.assertNotIn(short_id(c.id), returned_posts)
Example #46
0
 def test_title_in_metadata(self):
     user = self.user
     title = "here's a title for this comment"
     cmt = create_comment(title=title)
     ctx = self._ctx(user, cmt)
     cmt = TemplateComment(cmt.details(), request_context=ctx)
     self.assertTrue(strip_template_chars(title) in cmt.shareable_metadata()['title'])
Example #47
0
    def test_expander_honors_unsubscribe_per_channel(self):
        author = utils.create_user()
        comment = utils.create_comment()
        comment.author = author
        comment.save()

        self.assertEqual(comment.thread.op.author, author)

        another_user = utils.create_user()

        pn = Actions.replied(another_user, comment)

        notifications = expander.expand(pn)
        self.assertTrue(notifications)

        notification = filter(lambda n: n.channel == 'EmailChannel',
                              notifications)[0]
        self.assertEqual(author, notification.recipient)

        # Now, let the user unsubscribe to the reply action.
        author.kv.subscriptions.unsubscribe('thread_replied')
        pn = Actions.replied(another_user, comment)
        notifications = expander.expand(pn)
        recipients = [
            n.recipient for n in notifications if n.channel == 'EmailChannel'
        ]
        self.assertFalse(author in recipients)
Example #48
0
    def test_standard_op(self):
        comment = create_comment(reply_content=create_content(), reply_text="hello friends")
        post = self._render_post(comment, fullsize=True)

        self.assertNumCssMatches(1, post, '.has_content')
        self.assertNumCssMatches(1, post, '.op.expanded')
        self.assertNumCssMatches(1, post, 'div.reply_content')
Example #49
0
 def test_has_stickered(self):
     result = self.api_post('/api/sticker/comment', {
         'type_id': str(1),
         'comment_id': create_comment().id,
     },
                            user=self.user)
     self.assertTrue(self.user.has_stickered())
Example #50
0
    def test_standard_op(self):
        comment = create_comment(reply_content=create_content(),
                                 reply_text="hello friends")
        post = self._render_post(comment, fullsize=True)

        self.assertNumCssMatches(1, post, '.has_content')
        self.assertNumCssMatches(1, post, '.op.expanded')
        self.assertNumCssMatches(1, post, 'div.reply_content')
Example #51
0
 def test_handle_bad_post_id(self):
     comment = create_comment()
     result = self.get('/public_api/posts/{0}'.format(
         short_id(comment.id + 10000)))
     self.assertAPIFailure(result)
     result = self.post('/public_api/posts/{0}'.format(
         short_id(comment.id + 10000)))
     self.assertAPIFailure(result)
    def test_queryset_details(self):
        comments = [create_comment(reply_content=create_content()) for _ in xrange(10)]
        details1 = CachedCall.multicall([cmt.details for cmt in comments])

        queryset = Comment.objects.filter(id__in=[cmt.id for cmt in comments])
        details2 = CachedCall.queryset_details(queryset)

        self.assertEquals(details1, details2)
Example #53
0
 def test_get_post_by_short_id(self):
     comment = create_comment()
     result = self.get('/public_api/posts/{0}'.format(short_id(comment.id)))
     self.assertAPISuccess(result)
     result = self.post('/public_api/posts/{0}'.format(short_id(comment.id)))
     self.assertAPISuccess(result)
     json = util.loads(result.content)
     self.check_comment(comment.details(), json)
 def test_anonymous_user_url_when_staff(self):
     user = self.user
     author = create_user()
     cmt = create_comment(author=author, anonymous=True)
     ctx = self._ctx(user, cmt)
     cmt = TemplateComment(cmt.details(), request_context=ctx)
     url = cmt.get_user_url()
     self.assertEqual(url, reverse('user_link', args=[author.username]))
Example #55
0
 def test_trim_max_request(self):
     comments = [
         create_comment() for x in range(knobs.PUBLIC_API_MAX_ITEMS + 2)
     ]
     id_list = [short_id(x.id) for x in comments]
     req = {'ids': id_list}
     result = self.post('/public_api/posts/', data=req)
     self.assertAPIFailure(result)
Example #56
0
    def test_details_replies_no_replies(self):
        cmt = create_comment(timestamp=123)
        d = cmt.details()

        self.assertEqual(d.reply_count, 0)
        self.assertEqual(d.last_reply_id, None)
        # This should be the timestamp of the OP in this case.
        self.assertEqual(d.last_reply_time, 123)
Example #57
0
 def _sticker(self, user, sticker_type_id=1, sender=None):
     comment = create_comment(author=user)
     sender = sender or create_user()
     result = self.api_post('/api/sticker/comment', {
         'type_id': str(sticker_type_id),
         'comment_id': comment.id
     },
                            user=sender)
 def test_anonymous_user_url_when_staff(self):
     user = self.user
     author = create_user()
     cmt = create_comment(author=author, anonymous=True)
     ctx = self._ctx(user, cmt)
     cmt = TemplateComment(cmt.details(), request_context=ctx)
     url = cmt.get_user_url()
     self.assertEqual(url, reverse('user_link', args=[author.username]))