Example #1
0
    def setUp(self):
        # Disable haystack logger (testing will raise errors on more_like_this field in templates).
        haystack_logger.setLevel(logging.CRITICAL)

        # Create a user.
        self.user = User.objects.create(email='*****@*****.**', password='******')

        # Create a post.
        title = "Post 1, title needs to be sufficiently long"
        content = (
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod'
            'tempor incididunt ut labore et dolore magna aliqua.')
        post_type = Post.QUESTION
        tag_val = 'tag_val'
        post = Post(title=title,
                    content=content,
                    tag_val=tag_val,
                    author=self.user,
                    type=post_type)
        post.save()

        # Create a vote.
        self.vote = Vote.objects.create(author=self.user,
                                        post=post,
                                        type=Vote.UP)
Example #2
0
    def get_object(self):
        user = self.request.user

        obj = super(PostDetails, self).get_object()

        # Update the post views.
        Post.update_post_views(obj, request=self.request)

        # Adds the permissions
        obj = post_permissions(request=self.request, post=obj)

        # This will be piggybacked on the main object.
        obj.sub = Subscription.get_sub(post=obj, user=user)

        # Just a sanity check to start at top level.
        if obj != obj.root:
            obj = obj.root

        # Populate the object to build a tree that contains all posts in the thread.
        # Answers sorted before comments.
        thread = [post_permissions(request=self.request, post=post) for post in Post.objects.get_thread(obj)]

        # Do a little preprocessing.
        answers = [p for p in thread if p.type == Post.ANSWER]

        tree = OrderedDict()
        for post in thread:

            if post.type == Post.COMMENT:
                tree.setdefault(post.parent_id, []).append(post)

        store = {Vote.UP: set(), Vote.BOOKMARK: set()}

        if user.is_authenticated():
            pids = [p.id for p in thread]
            votes = Vote.objects.filter(post_id__in=pids, author=user).values_list("post_id", "type")

            for post_id, vote_type in votes:
                store.setdefault(vote_type, set()).add(post_id)

        # Shortcuts to each storage.
        bookmarks = store[Vote.BOOKMARK]
        upvotes = store[Vote.UP]

        def decorate(post):
            post.has_bookmark = post.id in bookmarks
            post.has_upvote = post.id in upvotes

        # Add attributes by mutating the objects
        map(decorate, thread + [obj])

        # Additional attributes used during rendering
        obj.tree = tree
        obj.answers = answers

        # Add the more like this field
        post = super(PostDetails, self).get_object()

        return obj
Example #3
0
class ApiPostTest(TestCase):
    def setUp(self):
        # Disable haystack logger (testing will raise errors on more_like_this field in templates).
        haystack_logger.setLevel(logging.CRITICAL)

        # Create a user.
        self.user = User.objects.create(email='*****@*****.**', password='******')

        # Create tags.
        t1 = Tag.objects.create(name='mytag1')
        t2 = Tag.objects.create(name='mytag2')

        # Create a post.
        title = "Post 1, title needs to be sufficiently long"
        content = (
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod'
            'tempor incididunt ut labore et dolore magna aliqua.')
        post_type = Post.QUESTION
        self.post = Post(title=title,
                         content=content,
                         author=self.user,
                         type=post_type)
        self.post.save()
        self.post.tag_set.add(t1)
        self.post.tag_set.add(t2)
        self.post.save()

    def test_home_page(self):
        """
        Ensure that the new post in the home page uses the right tags and links.
        """
        r = self.client.get(reverse('home'))
        self.assertIn('<a class="tag" href="/t/mytag1/">mytag1</a>', r.content)
        self.assertIn('<a class="tag" href="/t/mytag2/">mytag2</a>', r.content)

    def test_topic_page_single_tag(self):
        """
        Ensure that the new post in the topic page uses the right tags and links when only 1 tag
        is selected.
        """
        r = self.client.get(reverse('topic-list', kwargs={'topic': 'mytag1'}))
        self.assertIn('<a class="tag" href="/t/mytag1/">mytag1</a>', r.content)
        self.assertIn('<a class="tag" href="/t/mytag1+mytag2/">mytag2</a>',
                      r.content)

    def test_topic_page_multiple_tags(self):
        """
        Ensure that the new post in the topic page uses the right tags and links when multiple
        tags are selected.
        """
        r = self.client.get(
            reverse('topic-list', kwargs={'topic': 'mytag1+mytag2'}))
        self.assertIn('<a class="tag" href="/t/mytag1+mytag2/">mytag1</a>',
                      r.content)
        self.assertIn('<a class="tag" href="/t/mytag1+mytag2/">mytag2</a>',
                      r.content)
        self.assertIn('Filtering by tags: mytag1 OR mytag2', r.content)
Example #4
0
def create_post(b, author, root=None, parent=None, tag_val=''):
    title = b.subj
    body = b.body
    if not parent:
        title = title.strip()
        title = ' '.join(title.splitlines())
        title = ' '.join(title.split())
        title = title.title()
        post = Post(title=title,
                    type=Post.QUESTION,
                    content=body,
                    tag_val=tag_val,
                    author=author)
    else:
        post_type = Post.ANSWER if parent.is_toplevel else Post.COMMENT
        post = Post(type=post_type,
                    content=body,
                    tag_val="galaxy",
                    author=author,
                    parent=parent)

    post.creation_date = post.lastedit_date = b.datetime

    post.save()

    if tag_val:
        post.add_tags(tag_val)

    logger.info("--- creating %s: %s" % (post.get_type_display(), title))

    return post
Example #5
0
def create_post(b, author, root=None, parent=None, tag_val=''):
    from biostar.apps.posts.models import Post

    title = b.subj
    body = b.body
    if not parent:
        title = title.strip()
        title = ' '.join(title.splitlines())
        title = ' '.join(title.split())
        title = title[:180]
        post = Post(title=title, type=Post.QUESTION, content=body, tag_val=tag_val, author=author)
    else:
        post_type = Post.ANSWER if parent.is_toplevel else Post.COMMENT
        post = Post(type=post_type, content=body, tag_val="galaxy", author=author, parent=parent)

    post.creation_date = post.lastedit_date = b.date

    if not DRY_RUN:
        post.save()

    tag_val = guess_tags(post.content, tag_val)

    if tag_val and not DRY_RUN:
        post.add_tags(tag_val)

    logger.info("--- creating %s: %s" % (post.get_type_display(), title))

    return post
Example #6
0
 def create_post(self, post_type, parent=None, days=3):
     # Create a post.
     title = "Post 1, title needs to be sufficiently long"
     content = ('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod'
                'tempor incididunt ut labore et dolore magna aliqua.')
     tag_val = 'tag_val'
     post = Post(title=title, content=content, tag_val=tag_val, author=self.user,
                 type=post_type, parent=parent)
     #post.save()
     post.creation_date = datetime.today() - timedelta(days=days)
     post.save()
     return post
    def create_a_post(user):
        title = "Post 1, title needs to be sufficiently long"
        content = ('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod'
                   'tempor incididunt ut labore et dolore magna aliqua.')
        post_type = Post.QUESTION
        tag_val = 'tag_val'

        post = Post(title=title, content=content, tag_val=tag_val, author=user, type=post_type)
        post.save()

        # Triggers a new post save.
        post.add_tags(post.tag_val)

        return post
class ApiPostTest(TestCase):
    def setUp(self):
        # Disable haystack logger (testing will raise errors on more_like_this field in templates).
        haystack_logger.setLevel(logging.CRITICAL)

        # Create a user.
        self.user = User.objects.create(email='*****@*****.**', password='******')

        # Create tags.
        t1 = Tag.objects.create(name='mytag1')
        t2 = Tag.objects.create(name='mytag2')

        # Create a post.
        title = "Post 1, title needs to be sufficiently long"
        content = ('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod'
                   'tempor incididunt ut labore et dolore magna aliqua.')
        post_type = Post.QUESTION
        self.post = Post(title=title, content=content, author=self.user, type=post_type)
        self.post.save()
        self.post.tag_set.add(t1)
        self.post.tag_set.add(t2)
        self.post.save()

    def test_home_page(self):
        """
        Ensure that the new post in the home page uses the right tags and links.
        """
        r = self.client.get(reverse('home'))
        self.assertIn('<a class="tag" href="/t/mytag1/">mytag1</a>', r.content)
        self.assertIn('<a class="tag" href="/t/mytag2/">mytag2</a>', r.content)

    def test_topic_page_single_tag(self):
        """
        Ensure that the new post in the topic page uses the right tags and links when only 1 tag
        is selected.
        """
        r = self.client.get(reverse('topic-list', kwargs={'topic': 'mytag1'}))
        self.assertIn('<a class="tag" href="/t/mytag1/">mytag1</a>', r.content)
        self.assertIn('<a class="tag" href="/t/mytag1+mytag2/">mytag2</a>', r.content)

    def test_topic_page_multiple_tags(self):
        """
        Ensure that the new post in the topic page uses the right tags and links when multiple
        tags are selected.
        """
        r = self.client.get(reverse('topic-list', kwargs={'topic': 'mytag1+mytag2'}))
        self.assertIn('<a class="tag" href="/t/mytag1+mytag2/">mytag1</a>', r.content)
        self.assertIn('<a class="tag" href="/t/mytag1+mytag2/">mytag2</a>', r.content)
        self.assertIn('Filtering by tags: mytag1 OR mytag2', r.content)
Example #9
0
 def create_post(self, post_type, parent=None, days=3):
     # Create a post.
     title = "Post 1, title needs to be sufficiently long"
     content = (
         'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod'
         'tempor incididunt ut labore et dolore magna aliqua.')
     tag_val = 'tag_val'
     post = Post(title=title,
                 content=content,
                 tag_val=tag_val,
                 author=self.user,
                 type=post_type,
                 parent=parent)
     #post.save()
     post.creation_date = datetime.today() - timedelta(days=days)
     post.save()
     return post
def create_post(b, author, root=None, parent=None, tag_val=''):
    from biostar.apps.posts.models import Post


    title = b.subj
    body = b.body
    if not parent:
        title = title.strip()
        title = ' '.join(title.splitlines())
        title = ' '.join(title.split())
        title = title.title()
        post = Post(title=title, type=Post.QUESTION, content=body, tag_val=tag_val, author=author)
    else:
        post_type = Post.ANSWER if parent.is_toplevel else Post.COMMENT
        post = Post(type=post_type, content=body, tag_val="galaxy", author=author, parent=parent)

    post.creation_date = post.lastedit_date = b.datetime

    post.save()

    tag_val = guess_tags(post.content, tag_val)

    if tag_val:
        post.add_tags(tag_val)

    logger.info("--- creating %s: %s" % (post.get_type_display(), title))

    return post
Example #11
0
    def setUp(self):
        # Disable haystack logger (testing will raise errors on more_like_this field in templates).
        haystack_logger.setLevel(logging.CRITICAL)

        # Create a user.
        self.user = User.objects.create(email='*****@*****.**', password='******')

        # Create a post.
        title = "Post 1, title needs to be sufficiently long"
        content = ('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod'
                   'tempor incididunt ut labore et dolore magna aliqua.')
        post_type = Post.QUESTION
        tag_val = 'tag_val'
        post = Post(title=title, content=content, tag_val=tag_val, author=self.user,
                    type=post_type)
        post.save()

        # Create a vote.
        self.vote = Vote.objects.create(author=self.user, post=post, type=Vote.UP)
    def test_post_creation(self):
        "Testing post creation."
        eq = self.assertEqual

        # Create an admin user and a post.
        title = "Hello Posts!"
        email = "*****@*****.**"
        jane = User.objects.create(email=email)
        html = "<b>Hello World!</b>"
        post = Post(title=title, author=jane, type=Post.FORUM, content=html)
        post.save()

        # Get the object fresh.
        post = Post.objects.get(pk=post.id)

        eq(post.type, Post.FORUM)
        eq(post.root, post)
        eq(post.parent, post)

        # Subscriptions are automatically created
        sub = Subscription.objects.get(user=jane)
        eq(sub.user, jane)
        eq(sub.post, post)

        # A new post triggers a message to the author.
        email = "*****@*****.**"
        john = User.objects.create(email=email)
        answer = Post(author=john, parent=post, type=Post.ANSWER)
        answer.save()

        eq(answer.root, post)
        eq(answer.parent, post)
        eq(answer.type, Post.ANSWER)

        # Add comment. The parent will override the post type.
        email = "*****@*****.**"
        bob = User.objects.create(email=email)
        comment = Post(author=bob, type=Post.FORUM, parent=answer)
        comment.save()

        eq(comment.root, post)
        eq(comment.parent, answer)
        eq(comment.type, Post.COMMENT)

        # Everyone posting in a thread gets a subscription to the root post of the
        subs = Subscription.objects.filter(post=post)
        eq(len(subs), 3)
Example #13
0
    def test_note_creation(self):
        "Testing notifications"

        eq = self.assertEqual

        # Create some users
        title = "Test"
        emails = [
            "*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**",
            "*****@*****.**", "*****@*****.**"
        ]

        email_count = len(emails)

        users, posts, user = [], [], None
        parent = None
        for email in emails:
            # Create users.
            user = User.objects.create(email=email)
            users.append(user)

        # A welcome message for each user.
        eq(note_count(), email_count)

        # Create a question.
        first = users[0]
        post = Post(title=title, author=first, type=Post.QUESTION)
        post.save()

        answers = []
        for user in users:
            # Every user adds an answer.
            answ = Post(author=user, type=Post.ANSWER, parent=post)
            answ.save()
            answers.append(answ)

        # Total number of posts
        eq(note_count(), 21)

        # Every user has one subscription to the main post
        eq(email_count, Subscription.objects.all().count())

        # Each user has a messages for content posted after
        # they started following the thread.
        for index, user in enumerate(users):
            mesg_c = Message.objects.filter(user=user).count()
            eq(mesg_c, email_count - index)
Example #14
0
    def create_a_post(user):
        title = "Post 1, title needs to be sufficiently long"
        content = ('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod'
                   'tempor incididunt ut labore et dolore magna aliqua.')
        post_type = Post.QUESTION
        tag_val = 'tag_val'

        post = Post(title=title, content=content, tag_val=tag_val, author=user, type=post_type)
        post.save()

        # Triggers a new post save.
        post.add_tags(post.tag_val)

        return post
Example #15
0
    def test_note_creation(self):
        "Testing notifications"

        eq = self.assertEqual

        # Create some users
        title = "Test"
        emails = ["*****@*****.**", "*****@*****.**", "*****@*****.**", "*****@*****.**",
                  "*****@*****.**", "*****@*****.**" ]

        email_count = len(emails)

        users, posts, user = [], [], None
        parent = None
        for email in emails:
            # Create users.
            user = User.objects.create(email=email)
            users.append(user)

        # A welcome message for each user.
        eq(note_count(), email_count)

        # Create a question.
        first = users[0]
        post = Post(title=title, author=first, type=Post.QUESTION)
        post.save()

        answers = []
        for user in users:
            # Every user adds an answer.
            answ = Post(author=user, type=Post.ANSWER, parent=post)
            answ.save()
            answers.append(answ)


        # Total number of posts
        eq(note_count(), 21)

        # Every user has one subscription to the main post
        eq(email_count, Subscription.objects.all().count())

        # Each user has a messages for content posted after
        # they started following the thread.
        for index, user in enumerate(users):
            mesg_c = Message.objects.filter(user=user).count()
            eq (mesg_c, email_count - index )
Example #16
0
    def get_object(self):
        user = self.request.user

        obj = super(PostDetails, self).get_object()

        # Update the post views.
        Post.update_post_views(obj, request=self.request)

        # Adds the permissions
        obj = post_permissions(request=self.request, post=obj)

        # This will be piggybacked on the main object.
        obj.sub = Subscription.get_sub(post=obj, user=user)

        # Bail out if not at top level.
        if not obj.is_toplevel:
            return obj

        # Populate the object to build a tree that contains all posts in the thread.
        # Answers sorted before comments.
        thread = [
            post_permissions(request=self.request, post=post)
            for post in Post.objects.get_thread(obj, user)
        ]

        # Do a little preprocessing.
        answers = [p for p in thread if p.type == Post.ANSWER]

        tree = OrderedDict()
        for post in thread:

            if post.type == Post.COMMENT:
                tree.setdefault(post.parent_id, []).append(post)

        store = {Vote.UP: set(), Vote.DOWN: set(), Vote.BOOKMARK: set()}

        if user.is_authenticated():
            pids = [p.id for p in thread]
            votes = Vote.objects.filter(post_id__in=pids,
                                        author=user).values_list(
                                            "post_id", "type")

            for post_id, vote_type in votes:
                store.setdefault(vote_type, set()).add(post_id)

        # Shortcuts to each storage.
        bookmarks = store[Vote.BOOKMARK]
        upvotes = store[Vote.UP]
        downvotes = store[Vote.DOWN]

        # Can the current user accept answers
        can_accept = obj.author == user

        def decorate(post):
            post.has_bookmark = post.id in bookmarks
            post.has_upvote = post.id in upvotes
            post.has_downvote = post.id in downvotes
            post.can_accept = can_accept or post.has_accepted

        # Add attributes by mutating the objects
        map(decorate, thread + [obj])

        # Additional attributes used during rendering
        obj.tree = tree
        obj.answers = answers

        # Add the more like this field
        post = super(PostDetails, self).get_object()

        return obj
    def test_tagging(self):
        "Testing tagging."
        eq = self.assertEqual

        eq(0, Tag.objects.all().count() )

        # Create an admin user and a post.
        title = "Hello Posts!"
        email = "*****@*****.**"
        jane = User.objects.create(email=email)
        html = "<b>Hello World!</b>"
        post = Post(title=title, author=jane, type=Post.FORUM, content=html)
        post.save()
        post.add_tags("t1,t2, t3")

        eq(3, Tag.objects.all().count())

        post = Post(title=title, author=jane, type=Post.FORUM, content=html)
        post.save()
        post.add_tags("t1, t2, t3, t2, t1, t1")

        t1 = Tag.objects.get(name="t1")
        t3 = Tag.objects.get(name="t3")

        eq(2, t1.count)
        eq(2, t3.count)

        post.add_tags("t2 t4")

        t1 = Tag.objects.get(name="t1")
        t3 = Tag.objects.get(name="t3")

        eq(1, t1.count)
        eq(1, t3.count)
Example #18
0
class ApiPostTest(TestCase):
    def setUp(self):
        # Disable haystack logger (testing will raise errors on more_like_this field in templates).
        haystack_logger.setLevel(logging.CRITICAL)

        # Create a user.
        self.user = User.objects.create(email='*****@*****.**', password='******')

        # Create a post.
        title = "Post 1, title needs to be sufficiently long"
        content = ('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod'
                   'tempor incididunt ut labore et dolore magna aliqua.')
        post_type = Post.QUESTION
        tag_val = 'tag_val'
        self.post = Post(title=title, content=content, tag_val=tag_val, author=self.user,
                         type=post_type)
        self.post.save()

        # Create a vote.
        self.vote = Vote.objects.create(author=self.user, post=self.post, type=Vote.UP)

    def test_invalid_post_id(self):
        r = self.client.get(reverse('api-post', kwargs={'id': 1115}))
        self.assertEqual(r.status_code, 404)

    def test_naked_post(self):
        """
        A naked post with no replies, no bookmarks, no comments, ...
        """
        r = self.client.get(reverse('api-post', kwargs={'id': self.post.id}))
        content = json.loads(r.content)

        expected_post = {
            "answer_count": self.post.root.reply_count,
            "author": "test",
            "author_id": self.user.id,
            "book_count": self.post.book_count,
            "comment_count": self.post.comment_count,
            "creation_date": datetime_to_iso(self.post.creation_date),
            "has_accepted": self.post.has_accepted,
            "id": self.post.id,
            "lastedit_date": datetime_to_iso(self.post.lastedit_date),
            "lastedit_user_id": self.user.id,
            "parent_id": self.post.id,
            "rank": float(self.post.rank),
            "reply_count": self.post.reply_count,
            "root_id": self.post.id,
            "status": self.post.get_status_display(),
            "status_id": self.post.status,
            "subs_count": 1,
            "tag_val": "tag_val",
            "thread_score": self.post.thread_score,
            "title": self.post.title,
            "type": self.post.get_type_display(),
            "type_id": self.post.type,
            "url": 'http://example.com{}'.format(self.post.get_absolute_url()),
            "view_count": self.post.view_count,
            "vote_count": self.post.vote_count,
            "xhtml": self.post.content
        }
        self.assertDictEqual(content, expected_post)

    def test_post(self):
        """
        Regular post with replies, bookmarks, comments, ...
        """
        self.post.reply_count = 3
        self.post.book_count = 4
        self.post.comment_count = 5
        self.post.subs_count = 6
        self.post.view_count = 9
        self.post.vote_count = 7
        self.post.thread_score = 8
        self.post.has_accepted = True
        self.post.rank = 5.5
        self.post.save()

        expected_post = {
            "answer_count": self.post.root.reply_count,
            "author": "test",
            "author_id": self.user.id,
            "book_count": self.post.book_count,
            "comment_count": self.post.comment_count,
            "creation_date": datetime_to_iso(self.post.creation_date),
            "has_accepted": self.post.has_accepted,
            "id": self.post.id,
            "lastedit_date": datetime_to_iso(self.post.lastedit_date),
            "lastedit_user_id": self.user.id,
            "parent_id": self.post.id,
            "rank": float(self.post.rank),
            "reply_count": self.post.reply_count,
            "root_id": self.post.id,
            "status": self.post.get_status_display(),
            "status_id": self.post.status,
            "subs_count": self.post.subs_count,
            "tag_val": "tag_val",
            "thread_score": self.post.thread_score,
            "title": self.post.title,
            "type": self.post.get_type_display(),
            "type_id": self.post.type,
            "url": 'http://example.com{}'.format(self.post.get_absolute_url()),
            "view_count": self.post.view_count,
            "vote_count": self.post.vote_count,
            "xhtml": self.post.content
        }

        r = self.client.get(reverse('api-post', kwargs={'id': self.post.id}))
        content = json.loads(r.content)

        self.assertDictEqual(content, expected_post)
    def post(self, request, *args, **kwargs):
        user = request.user

        post = self.get_obj()
        post = post_permissions(request, post)

        # The default return url
        response = HttpResponseRedirect(post.root.get_absolute_url())

        if not post.is_editable:
            messages.warning(request, "You may not moderate this post")
            return response

        # Initialize the form class.
        form = self.form_class(request.POST, pk=post.id)

        # Bail out on errors.
        if not form.is_valid():
            messages.error(request, "%s" % form.errors)
            return response

        # A shortcut to the clean form data.
        get = form.cleaned_data.get

        # These will be used in updates, will bypasses signals.
        query = Post.objects.filter(pk=post.id)
        root  = Post.objects.filter(pk=post.root_id)

        action = get('action')
        if action == (OPEN, TOGGLE_ACCEPT) and not user.is_moderator:
            messages.error(request, "Only a moderator may open or toggle a post")
            return response

        if action == TOGGLE_ACCEPT and post.type == Post.ANSWER:
            # Toggle post acceptance.
            post.has_accepted=not post.has_accepted
            post.save()
            has_accepted = Post.objects.filter(root=post.root, type=Post.ANSWER, has_accepted=True).count()
            root.update(has_accepted=has_accepted)
            return response

        if action == MOVE_TO_ANSWER and post.type == Post.COMMENT:
            # This is a valid action only for comments.
            messages.success(request, "Moved post to answer")
            query.update(type=Post.ANSWER, parent=post.root)
            root.update(reply_count=F("reply_count") + 1)
            return response

        if action == MOVE_TO_COMMENT and post.type == Post.ANSWER:
            # This is a valid action only for answers.
            messages.success(request, "Moved post to answer")
            query.update(type=Post.COMMENT, parent=post.root)
            root.update(reply_count=F("reply_count") - 1)
            return response

        # Some actions are valid on top level posts only.
        if action in (CLOSE_OFFTOPIC, DUPLICATE) and not post.is_toplevel:
            messages.warning(request, "You can only close or open a top level post")
            return response

        if action == OPEN:
            query.update(status=Post.OPEN)
            messages.success(request, "Opened post: %s" % post.title)
            return response

        if action in CLOSE_OFFTOPIC:
            query.update(status=Post.CLOSED)
            messages.success(request, "Closed post: %s" % post.title)
            content = html.render(name="messages/offtopic_posts.html", user=post.author, comment=get("comment"), post=post)
            comment = Post(content=content, type=Post.COMMENT, parent=post, author=user)
            comment.save()
            return response

        if action == CROSSPOST:
            content = html.render(name="messages/crossposted.html", user=post.author, comment=get("comment"), post=post)
            comment = Post(content=content, type=Post.COMMENT, parent=post, author=user)
            comment.save()
            return response

        if action == DUPLICATE:
            query.update(status=Post.CLOSED)
            posts = Post.objects.filter(id__in=get("dupe"))
            content = html.render(name="messages/duplicate_posts.html", user=post.author, comment=get("comment"), posts=posts)
            comment = Post(content=content, type=Post.COMMENT, parent=post, author=user)
            comment.save()
            return response

        if action == DELETE:

            # Delete marks a post deleted but does not remove it.
            # Remove means to delete the post from the database with no trace.

            # Posts with children or older than some value can only be deleted not removed

            # The children of a post.
            children = Post.objects.filter(parent_id=post.id).exclude(pk=post.id)

            # The condition where post can only be deleted.
            delete_only = children or post.age_in_days > 7 or post.vote_count > 1 or (post.author != user)

            if delete_only:
                # Deleted posts can be undeleted by re-opening them.
                query.update(status=Post.DELETED)
                messages.success(request, "Deleted post: %s" % post.title)
                response = HttpResponseRedirect(post.root.get_absolute_url())
            else:
                # This will remove the post. Redirect depends on the level of the post.
                url = "/" if post.is_toplevel else post.parent.get_absolute_url()
                post.delete()
                messages.success(request, "Removed post: %s" % post.title)
                response = HttpResponseRedirect(url)

            # Recompute post reply count
            post.update_reply_count()

            return response

        # By this time all actions should have been performed
        messages.warning(request, "That seems to be an invalid action for that post. \
                It is probably ok! Actions may be shown even when not valid.")
        return response
Example #20
0
def import_posts(fname, uname):

    logger.info('Extracting posts from file...')
    allposts = get_posts(fname)

    logger.info('Extracting users from file...')
    allusers = get_all_users(uname)

    post_count = 0
    for single in allposts:
        uid = int(single[0])
        title = single[1]
        body = single[2]
        date = single[3]
        logger.info('Fetched post : %s' % title)

        #Getting the post user
        user = get_user(uid, allusers)

        #List to hold answer posts which could not be matched
        orphans = []

        if title.startswith('Re:'):
            try:
                ptitle = title[4:]
                parent = Post.objects.get(title=ptitle)
                post = Post(title=title,
                            content=body,
                            author=user,
                            type=Post.ANSWER,
                            creation_date=date)
                post.parent = parent
                post.root = parent
                post.save()
                post_count += 1
            except:
                post = Post(title=title,
                            author=user,
                            type=Post.ANSWER,
                            content=body,
                            creation_date=date)
                orphans.append(post)
        else:
            post = Post(title=title,
                        content=body,
                        author=user,
                        type=Post.QUESTION,
                        creation_date=date)
            post.save()
            post_count += 1

    #Now try to match posts which could not be matched before
    if orphans:
        logger.info('Matching posts which could not be matched earlier')
        for post in orphans:
            try:
                title = post.title
                ptitle = title[4:]
                parent = Post.objects.get(title__startswith=ptitle)
                post.parent = parent
                post.root = parent
                post.save()
                post_count += 1
            except:
                pass

    print post_count, ' posts created'
    logger.info('DONE!')
def import_posts(fname, uname):

    logger.info('Extracting posts from file...')
    allposts = get_posts(fname)

    logger.info('Extracting users from file...')
    allusers = get_all_users(uname)

    post_count=0
    for single in allposts:
        uid = int(single[0])
        title = single[1]
        body = single[2]
        date = single[3]
        logger.info('Fetched post : %s' % title)

        #Getting the post user
        user = get_user(uid,allusers)

        #List to hold answer posts which could not be matched
        orphans = []

        if title.startswith('Re:'):
            try:
                ptitle = title[4:]
                parent = Post.objects.get(title=ptitle)
                post = Post(title=title, content=body, author=user, type= Post.ANSWER, creation_date=date)
                post.parent=parent
                post.root=parent
                post.save()
                post_count+=1
            except:
                post = Post(title=title, author=user, type= Post.ANSWER, content=body, creation_date=date)
                orphans.append(post)
        else:
            post = Post(title=title, content=body, author=user, type = Post.QUESTION, creation_date=date)
            post.save()
            post_count+=1


    #Now try to match posts which could not be matched before
    if orphans:
        logger.info('Matching posts which could not be matched earlier')
        for post in orphans:
            try:
                title = post.title
                ptitle = title[4:]
                parent = Post.objects.get(title__startswith=ptitle)
                post.parent=parent
                post.root=parent
                post.save()
                post_count+=1
            except:
                pass

    print post_count, ' posts created'
    logger.info('DONE!')
Example #22
0
class ApiPostTest(TestCase):
    def setUp(self):
        # Disable haystack logger (testing will raise errors on more_like_this field in templates).
        haystack_logger.setLevel(logging.CRITICAL)

        # Create a user.
        self.user = User.objects.create(pubkey='*****@*****.**', )

        # Create a post.
        title = "Post 1, title needs to be sufficiently long"
        content = (
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod'
            'tempor incididunt ut labore et dolore magna aliqua.')
        post_type = Post.QUESTION
        tag_val = 'tag_val'
        self.post = Post(title=title,
                         content=content,
                         tag_val=tag_val,
                         author=self.user,
                         type=post_type)
        self.post.save()

        # Create a vote.
        self.vote = Vote.objects.create(author=self.user,
                                        post=self.post,
                                        type=Vote.UP)

        self.maxDiff = None

    def test_invalid_post_id(self):
        r = self.client.get(reverse('api-post', kwargs={'id': 1115}))
        self.assertEqual(r.status_code, 404)

    def test_naked_post(self):
        """
        A naked post with no replies, no bookmarks, no comments, ...
        """
        r = self.client.get(reverse('api-post', kwargs={'id': self.post.id}))
        content = json.loads(r.content)

        expected_post = {
            "answer_count": self.post.root.reply_count,
            "author": "test",
            "author_id": self.user.id,
            "book_count": self.post.book_count,
            "comment_count": self.post.comment_count,
            "creation_date": datetime_to_iso(self.post.creation_date),
            "has_accepted": self.post.has_accepted,
            "id": self.post.id,
            "lastedit_date": datetime_to_iso(self.post.lastedit_date),
            "lastedit_user_id": self.user.id,
            "parent_id": self.post.id,
            "rank": float(self.post.rank),
            "reply_count": self.post.reply_count,
            "root_id": self.post.id,
            "status": self.post.get_status_display(),
            "status_id": self.post.status,
            "subs_count": 1,
            "tag_val": "tag_val",
            "thread_score": self.post.thread_score,
            "title": self.post.title,
            "type": self.post.get_type_display(),
            "type_id": self.post.type,
            "url": 'http://example.com{}'.format(self.post.get_absolute_url()),
            "view_count": self.post.view_count,
            "vote_count": self.post.vote_count,
            "xhtml": self.post.html
        }
        expected_post = to_json_and_back(expected_post)

        self.assertDictEqual(content, expected_post)

    def test_post(self):
        """
        Regular post with replies, bookmarks, comments, ...
        """
        self.post.reply_count = 3
        self.post.book_count = 4
        self.post.comment_count = 5
        self.post.subs_count = 6
        self.post.view_count = 9
        self.post.vote_count = 7
        self.post.thread_score = 8
        self.post.has_accepted = True
        self.post.rank = 5.5
        self.post.save()

        r = self.client.get(reverse('api-post', kwargs={'id': self.post.id}))
        content = json.loads(r.content)

        expected_post = {
            "answer_count": self.post.root.reply_count,
            "author": "test",
            "author_id": self.user.id,
            "book_count": self.post.book_count,
            "comment_count": self.post.comment_count,
            "creation_date": datetime_to_iso(self.post.creation_date),
            "has_accepted": self.post.has_accepted,
            "id": self.post.id,
            "lastedit_date": datetime_to_iso(self.post.lastedit_date),
            "lastedit_user_id": self.user.id,
            "parent_id": self.post.id,
            "rank": float(self.post.rank),
            "reply_count": self.post.reply_count,
            "root_id": self.post.id,
            "status": self.post.get_status_display(),
            "status_id": self.post.status,
            "subs_count": self.post.subs_count,
            "tag_val": "tag_val",
            "thread_score": self.post.thread_score,
            "title": self.post.title,
            "type": self.post.get_type_display(),
            "type_id": self.post.type,
            "url": 'http://example.com{}'.format(self.post.get_absolute_url()),
            "view_count": self.post.view_count,
            "vote_count": self.post.vote_count,
            "xhtml": self.post.html
        }
        expected_post = to_json_and_back(expected_post)

        self.assertDictEqual(content, expected_post)
Example #23
0
    def get_object(self):
        user = self.request.user

        obj = super(PostDetails, self).get_object()

        # Raise 404 if a deleted post is viewed by an anonymous user
        if (obj.status == Post.DELETED) and not self.request.user.is_moderator:
            raise Http404()

        # Update the post views.
        Post.update_post_views(obj, request=self.request)

        # Adds the permissions
        obj = post_permissions(request=self.request, post=obj)

        # This will be piggybacked on the main object.
        obj.sub = Subscription.get_sub(post=obj, user=user)

        # Bail out if not at top level.
        if not obj.is_toplevel:
            return obj

        # Populate the object to build a tree that contains all posts in the thread.
        # Answers sorted before comments.
        thread = [post_permissions(request=self.request, post=post) for post in Post.objects.get_thread(obj, user)]

        # Do a little preprocessing.
        answers = [p for p in thread if p.type == Post.ANSWER]

        tree = OrderedDict()
        for post in thread:

            if post.type == Post.COMMENT:
                tree.setdefault(post.parent_id, []).append(post)

        store = {Vote.UP: set(), Vote.BOOKMARK: set()}

        if user.is_authenticated():
            pids = [p.id for p in thread]
            votes = Vote.objects.filter(post_id__in=pids, author=user).values_list("post_id", "type")

            for post_id, vote_type in votes:
                store.setdefault(vote_type, set()).add(post_id)
        
        
        # Shortcuts to each storage.
        bookmarks = store[Vote.BOOKMARK]
        upvotes = store[Vote.UP]
        
        # Can the current user accept answers
        can_accept = obj.author == user
        
        def decorate(post):
            post.has_bookmark = post.id in bookmarks
            post.has_upvote = post.id in upvotes
            post.can_accept = can_accept or post.has_accepted

        # Add attributes by mutating the objects
        decorate(obj)
        for post in thread:
            decorate(post)
        

        # Additional attributes used during rendering
        obj.tree = tree
        obj.answers = answers

        # Add the more like this field
        post = super(PostDetails, self).get_object()

        return obj
def get_post(row, users):
    uid = get(row, 'id', func=int)
    root_id = get(row, 'root_id', func=int)
    parent_id = get(row, 'parent_id', func=int)

    title = get(row, 'title').title()
    tag_val = get(row, 'tag_val').strip()

    author_id = get(row, 'author_id', func=int)
    author = users.get(author_id)

    if not author:
        print("*** author %s not found for post %s" % (author_id, uid))
        return None

    post_type = get(row, 'post_type')
    post_type = POST_TYPE_MAP.get(post_type, Post.FORUM)
    post_status = Post.OPEN if get(row,
                                   'post_status') == "Open" else Post.CLOSED

    post = Post(id=uid,
                title=title,
                author=author,
                lastedit_user=author,
                parent_id=parent_id,
                root_id=root_id)

    post.status = post_status
    post.type = post_type
    post.tag_val = ",".join(tag_val.split())
    post.creation_date = localize_time(get(row, 'creation_date'))
    post.lastedit_date = localize_time(get(row, 'lastedit_date'))
    post.view_count = get(row, "views", func=int)
    post.reply_count = get(row, "answer_count", func=int)
    post.book_count = get(row, "book_count", func=int)
    post.thread_score = get(row, "full_score", func=int)
    post.vote_count = get(row, "score", func=int)

    post_file = path_join(MIGRATE_DIR, 'posts', str(uid))
    post.content = file(post_file, 'rt').read()

    return post
def get_post(row, users):
    uid = get(row, 'id', func=int)
    root_id = get(row, 'root_id', func=int)
    parent_id = get(row, 'parent_id', func=int)

    title = get(row, 'title').title()
    tag_val = get(row, 'tag_val').strip()

    author_id = get(row, 'author_id', func=int)
    author = users.get(author_id)

    if not author:
        print("*** author %s not found for post %s" % (author_id, uid))
        return None

    post_type = get(row, 'post_type')
    post_type = POST_TYPE_MAP.get(post_type, Post.FORUM)
    post_status = Post.OPEN if get(row, 'post_status') == "Open" else Post.CLOSED

    post = Post(id=uid, title=title, author=author, lastedit_user=author,
                parent_id=parent_id, root_id=root_id)

    post.status = post_status
    post.type = post_type
    post.tag_val = ",".join(tag_val.split())
    post.creation_date = localize_time(get(row, 'creation_date'))
    post.lastedit_date = localize_time(get(row, 'lastedit_date'))
    post.view_count = get(row, "views", func=int)
    post.reply_count = get(row, "answer_count", func=int)
    post.book_count = get(row, "book_count", func=int)
    post.thread_score = get(row, "full_score", func=int)
    post.vote_count = get(row, "score", func=int)

    post_file = path_join(MIGRATE_DIR, 'posts', str(uid))
    post.content = file(post_file, 'rt').read()

    return post
    def test_tagging(self):
        "Testing tagging."
        eq = self.assertEqual

        eq(0, Tag.objects.all().count())

        # Create an admin user and a post.
        title = "Hello Posts!"
        email = "*****@*****.**"
        jane = User.objects.create(email=email)
        html = "<b>Hello World!</b>"
        post = Post(title=title, author=jane, type=Post.FORUM, content=html)
        post.save()
        post.add_tags("t1,t2, t3")

        eq(3, Tag.objects.all().count())

        post = Post(title=title, author=jane, type=Post.FORUM, content=html)
        post.save()
        post.add_tags("t1, t2, t3, t2, t1, t1")

        t1 = Tag.objects.get(name="t1")
        t3 = Tag.objects.get(name="t3")

        eq(2, t1.count)
        eq(2, t3.count)

        post.add_tags("t2 t4")

        t1 = Tag.objects.get(name="t1")
        t3 = Tag.objects.get(name="t3")

        eq(1, t1.count)
        eq(1, t3.count)
    def test_post_creation(self):
        "Testing post creation."
        eq = self.assertEqual

        # Create an admin user and a post.
        title = "Hello Posts!"
        email = "*****@*****.**"
        jane = User.objects.create(email=email)
        html = "<b>Hello World!</b>"
        post = Post(title=title, author=jane, type=Post.FORUM, content=html)
        post.save()

        # Get the object fresh.
        post = Post.objects.get(pk=post.id)

        eq(post.type, Post.FORUM)
        eq(post.root, post)
        eq(post.parent, post)

        # Subscriptions are automatically created
        sub = Subscription.objects.get(user=jane)
        eq(sub.user, jane)
        eq(sub.post, post)

        # A new post triggers a message to the author.
        email = "*****@*****.**"
        john = User.objects.create(email=email)
        answer = Post(author=john, parent=post, type=Post.ANSWER)
        answer.save()

        eq(answer.root, post)
        eq(answer.parent, post)
        eq(answer.type, Post.ANSWER)

        # Add comment. The parent will override the post type.
        email = "*****@*****.**"
        bob = User.objects.create(email=email)
        comment = Post(author=bob, type=Post.FORUM, parent=answer)
        comment.save()

        eq(comment.root, post)
        eq(comment.parent, answer)
        eq(comment.type, Post.COMMENT)

        # Everyone posting in a thread gets a subscription to the root post of the
        subs = Subscription.objects.filter(post=post)
        eq(len(subs), 3)