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 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
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)
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
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
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)
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
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)
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)
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 )
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)
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
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!')
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)
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 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)