summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomasz Zielinski <tomasz.zielinski@pyconsultant.eu>2011-12-02 01:10:22 +0100
committerTomasz Zielinski <tomasz.zielinski@pyconsultant.eu>2011-12-02 01:10:22 +0100
commit13574985075aa595384f6d24e871f27510221a5f (patch)
tree309a81b4d300931bab87a14a14d218d228d8aec6
parent031c87d57a00a41a63e771dcdfb0f09fb647afbb (diff)
downloadaskbot-13574985075aa595384f6d24e871f27510221a5f.tar.gz
askbot-13574985075aa595384f6d24e871f27510221a5f.tar.bz2
askbot-13574985075aa595384f6d24e871f27510221a5f.zip
Tickets 104, 107: Major transplant of most Question methods
-rw-r--r--askbot/management/commands/fix_question_tags.py2
-rw-r--r--askbot/models/__init__.py2
-rw-r--r--askbot/models/content.py32
-rw-r--r--askbot/models/question.py238
-rw-r--r--askbot/tests/db_api_tests.py4
-rw-r--r--askbot/views/readers.py6
-rw-r--r--askbot/views/writers.py2
7 files changed, 147 insertions, 139 deletions
diff --git a/askbot/management/commands/fix_question_tags.py b/askbot/management/commands/fix_question_tags.py
index 47fc4102..aec3de3b 100644
--- a/askbot/management/commands/fix_question_tags.py
+++ b/askbot/management/commands/fix_question_tags.py
@@ -59,7 +59,7 @@ class Command(NoArgsCommand):
tagnames = forms.TagNamesField().clean(question.tagnames)
- question.update_tags(
+ question.thread.update_tags(
tagnames = tagnames,
user = user,
timestamp = timestamp
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index d711dac6..c9c49ae9 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -1011,7 +1011,7 @@ def user_retag_question(
silent = False
):
self.assert_can_retag_question(question)
- question.retag(
+ question.thread.retag(
retagged_by = self,
retagged_at = timestamp,
tagnames = tags,
diff --git a/askbot/models/content.py b/askbot/models/content.py
index adb9d428..5ce9682e 100644
--- a/askbot/models/content.py
+++ b/askbot/models/content.py
@@ -492,6 +492,34 @@ class Content(models.Model):
comment.save()
return new_question
+ def _repost_as_answer(self, question = None):
+ """posts question as answer to another question,
+ but does not delete the question,
+ but moves all the comments to the new answer"""
+ if not self.is_question():
+ raise NotImplementedError
+ revisions = self.revisions.all().order_by('revised_at')
+ rev0 = revisions[0]
+ new_answer = rev0.author.post_answer(
+ question = question,
+ body_text = rev0.text,
+ wiki = self.wiki,
+ timestamp = rev0.revised_at
+ )
+ if len(revisions) > 1:
+ for rev in revisions:
+ rev.author.edit_answer(
+ answer = new_answer,
+ body_text = rev.text,
+ revision_comment = rev.summary,
+ timestamp = rev.revised_at
+ )
+ for comment in self.comments.all():
+ comment.content_object = new_answer
+ comment.save()
+ return new_answer
+
+
def swap_with_question(self, new_title = None):
"""swaps answer with the question it belongs to and
sets the title of question to ``new_title``
@@ -504,7 +532,7 @@ class Content(models.Model):
new_question = self._repost_as_question(new_title = new_title)
#2) post question (all revisions and comments) as answer
- new_answer = self.question.repost_as_answer(question = new_question)
+ new_answer = self.question._repost_as_answer(question = new_question)
#3) assign all remaining answers to the new question
self.question.answers.update(question = new_question)
@@ -678,7 +706,7 @@ class Content(models.Model):
# Update the Question tag associations
if latest_revision.tagnames != tags:
- self.update_tags(tagnames = tags, user = edited_by, timestamp = edited_at)
+ self.thread.update_tags(tagnames = tags, user = edited_by, timestamp = edited_at)
# Create a new revision
self.add_revision(
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 52600313..84816b18 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -112,7 +112,7 @@ class QuestionQuerySet(models.query.QuerySet):
question.wikified_at = added_at
question.parse_and_save(author = author)
- question.update_tags(tagnames = tagnames, user = author, timestamp = added_at)
+ question.thread.update_tags(tagnames = tagnames, user = author, timestamp = added_at)
question.add_revision(
author = author,
@@ -482,9 +482,34 @@ class Thread(models.Model):
self.save()
def update_answer_count(self):
- self.answer_count = self._question().get_answers().count()
+ self.answer_count = self.get_answers().count()
self.save()
+ def get_answers(self, user=None):
+ """returns query set for answers to this question
+ that may be shown to the given user
+ """
+ thread_question = self._question()
+
+ if user is None or user.is_anonymous():
+ return thread_question.answers.filter(deleted=False)
+ else:
+ if user.is_administrator() or user.is_moderator():
+ return thread_question.answers.all()
+ else:
+ return thread_question.answers.filter(
+ models.Q(deleted = False) | models.Q(author = user) \
+ | models.Q(deleted_by = user)
+ )
+
+
+ def get_similarity(self, other_question = None):
+ """return number of tags in the other question
+ that overlap with the current question (self)
+ """
+ my_tags = set(self._question().get_tag_names())
+ others_tags = set(other_question.get_tag_names())
+ return len(my_tags & others_tags)
def get_similar_questions(self):
"""
@@ -507,59 +532,13 @@ class Thread(models.Model):
similar_questions = list(similar_questions)
for question in similar_questions:
- question.similarity = thread_question.get_similarity(other_question=question)
+ question.similarity = self.get_similarity(other_question=question)
similar_questions.sort(key=operator.attrgetter('similarity'), reverse=True)
return similar_questions[:10]
return LazyList(get_data)
-
-
-class Question(content.Content):
- # TODO: Eventually move this into Content/Post model
- thread = models.ForeignKey('Thread', unique=True, related_name='questions')
-
- #todo: this really becomes thread,
- #except property post_type goes to Post
- post_type = 'question'
- title = models.CharField(max_length=300)
- tags = models.ManyToManyField('Tag', related_name='questions')
- #todo: answer accepted will be replaced with
- #accepted_answer foreign key (nullable)
- answer_accepted = models.BooleanField(default=False)
- closed = models.BooleanField(default=False)
- closed_by = models.ForeignKey(User, null=True, blank=True, related_name='closed_questions')
- closed_at = models.DateTimeField(null=True, blank=True)
- close_reason = models.SmallIntegerField(
- choices=const.CLOSE_REASONS,
- null=True,
- blank=True
- )
- followed_by = models.ManyToManyField(User, related_name='followed_questions')
-
- # Denormalised data
- view_count = models.PositiveIntegerField(default=0)
- last_activity_at = models.DateTimeField(default=datetime.datetime.now)
- last_activity_by = models.ForeignKey(User, related_name='last_active_in_questions')
- tagnames = models.CharField(max_length=125)
- summary = models.CharField(max_length=180)
-
- favorited_by = models.ManyToManyField(User, through='FavoriteQuestion', related_name='favorite_questions')
- #note: anonymity here applies to question only, but
- #the field will still go to thread
- #maybe we should rename it to is_question_anonymous
- #we might have to duplicate the is_anonymous on the Post,
- #if we are to allow anonymous answers
- #the reason is that the title and tags belong to thread,
- #but the question body to Post
- is_anonymous = models.BooleanField(default=False)
-
- objects = QuestionManager()
-
- class Meta(content.Content.Meta):
- db_table = u'question'
-
def remove_author_anonymity(self):
"""removes anonymous flag from the question
and all its revisions
@@ -569,23 +548,16 @@ class Question(content.Content):
#note: see note for the is_anonymous field
#it is important that update method is called - not save,
#because we do not want the signals to fire here
- Question.objects.filter(id = self.id).update(is_anonymous = False)
- self.revisions.all().update(is_anonymous = False)
-
- def get_similarity(self, other_question = None):
- """return number of tags in the other question
- that overlap with the current question (self)
- """
- my_tags = set(self.get_tag_names())
- others_tags = set(other_question.get_tag_names())
- return len(my_tags & others_tags)
+ thread_question = self._question()
+ Question.objects.filter(id=thread_question.id).update(is_anonymous=False)
+ thread_question.revisions.all().update(is_anonymous=False)
def update_tags(self, tagnames = None, user = None, timestamp = None):
"""
Updates Tag associations for a question to match the given
tagname string.
- When tags are removed and their use count hits 0 - the tag is
+ When tags are removed and their use count hits 0 - the tag is
automatically deleted.
When an added tag does not exist - it is created
@@ -594,8 +566,9 @@ class Question(content.Content):
A signal tags updated is sent
"""
+ thread_question = self._question()
- previous_tags = list(self.tags.all())
+ previous_tags = list(thread_question.tags.all())
previous_tagnames = set([tag.name for tag in previous_tags])
updated_tagnames = set(t for t in tagnames.split(' '))
@@ -607,7 +580,7 @@ class Question(content.Content):
#remove tags from the question's tags many2many relation
if removed_tagnames:
removed_tags = [tag for tag in previous_tags if tag.name in removed_tagnames]
- self.tags.remove(*removed_tags)
+ thread_question.tags.remove(*removed_tags)
#if any of the removed tags reached use count == 1 that means they must be deleted
for tag in removed_tags:
@@ -647,14 +620,14 @@ class Question(content.Content):
added_tags = reused_tags
#finally add tags to the relation and extend the modified list
- self.tags.add(*added_tags)
+ thread_question.tags.add(*added_tags)
modified_tags.extend(added_tags)
#if there are any modified tags, update their use counts
if modified_tags:
Tag.objects.update_use_counts(modified_tags)
signals.tags_updated.send(None,
- question = self,
+ question = thread_question,
tags = modified_tags,
user = user,
timestamp = timestamp
@@ -663,67 +636,28 @@ class Question(content.Content):
return False
- def repost_as_answer(self, question = None):
- """posts question as answer to another question,
- but does not delete the question,
- but moves all the comments to the new answer"""
- #todo: goes to Thread.
- revisions = self.revisions.all().order_by('revised_at')
- rev0 = revisions[0]
- new_answer = rev0.author.post_answer(
- question = question,
- body_text = rev0.text,
- wiki = self.wiki,
- timestamp = rev0.revised_at
- )
- if len(revisions) > 1:
- for rev in revisions:
- rev.author.edit_answer(
- answer = new_answer,
- body_text = rev.text,
- revision_comment = rev.summary,
- timestamp = rev.revised_at
- )
- for comment in self.comments.all():
- comment.content_object = new_answer
- comment.save()
- return new_answer
-
- def get_answers(self, user = None):
- """returns query set for answers to this question
- that may be shown to the given user
- """
-
- if user is None or user.is_anonymous():
- return self.answers.filter(deleted=False)
- else:
- if user.is_administrator() or user.is_moderator():
- return self.answers.all()
- else:
- return self.answers.filter(
- models.Q(deleted = False) | models.Q(author = user) \
- | models.Q(deleted_by = user)
- )
-
def retag(self, retagged_by=None, retagged_at=None, tagnames=None, silent=False):
if None in (retagged_by, retagged_at, tagnames):
raise Exception('arguments retagged_at, retagged_by and tagnames are required')
+
+ thread_question = self._question()
+
# Update the Question itself
- self.tagnames = tagnames
+ thread_question.tagnames = tagnames
if silent == False:
- self.last_edited_at = retagged_at
- #self.last_activity_at = retagged_at
- self.last_edited_by = retagged_by
- #self.last_activity_by = retagged_by
- self.save()
+ thread_question.last_edited_at = retagged_at
+ #thread_question.last_activity_at = retagged_at
+ thread_question.last_edited_by = retagged_by
+ #thread_question.last_activity_by = retagged_by
+ thread_question.save()
# Update the Question's tag associations
- self.update_tags(tagnames = tagnames, user = retagged_by, timestamp = retagged_at)
+ self.update_tags(tagnames=tagnames, user=retagged_by, timestamp=retagged_at)
# Create a new revision
- latest_revision = self.get_latest_revision()
+ latest_revision = thread_question.get_latest_revision()
PostRevision.objects.create_question_revision(
- question = self,
+ question = thread_question,
title = latest_revision.title,
author = retagged_by,
revised_at = retagged_at,
@@ -732,30 +666,76 @@ class Question(content.Content):
text = latest_revision.text
)
- def _get_slug(self):
- return slugify(self.title)
-
- slug = property(_get_slug)
-
def has_favorite_by_user(self, user):
if not user.is_authenticated():
return False
- return FavoriteQuestion.objects.filter(question=self, user=user).count() > 0
+ return FavoriteQuestion.objects.filter(question=self._question(), user=user).exists()
def get_last_update_info(self):
- when, who = self.post_get_last_update_info()
+ thread_question = self._question()
- answers = self.answers.all()
- if len(answers) > 0:
- for a in answers:
- a_when, a_who = a.post_get_last_update_info()
- if a_when > when:
- when = a_when
- who = a_who
+ when, who = thread_question.post_get_last_update_info()
+
+ answers = thread_question.answers.all()
+ for a in answers:
+ a_when, a_who = a.post_get_last_update_info()
+ if a_when > when:
+ when = a_when
+ who = a_who
return when, who
+
+class Question(content.Content):
+ # TODO: Eventually move this into Content/Post model
+ thread = models.ForeignKey('Thread', unique=True, related_name='questions')
+
+ #todo: this really becomes thread,
+ #except property post_type goes to Post
+ post_type = 'question'
+ title = models.CharField(max_length=300)
+ tags = models.ManyToManyField('Tag', related_name='questions')
+ #todo: answer accepted will be replaced with
+ #accepted_answer foreign key (nullable)
+ answer_accepted = models.BooleanField(default=False)
+ closed = models.BooleanField(default=False)
+ closed_by = models.ForeignKey(User, null=True, blank=True, related_name='closed_questions')
+ closed_at = models.DateTimeField(null=True, blank=True)
+ close_reason = models.SmallIntegerField(
+ choices=const.CLOSE_REASONS,
+ null=True,
+ blank=True
+ )
+ followed_by = models.ManyToManyField(User, related_name='followed_questions')
+
+ # Denormalised data
+ view_count = models.PositiveIntegerField(default=0)
+ last_activity_at = models.DateTimeField(default=datetime.datetime.now)
+ last_activity_by = models.ForeignKey(User, related_name='last_active_in_questions')
+ tagnames = models.CharField(max_length=125)
+ summary = models.CharField(max_length=180)
+
+ favorited_by = models.ManyToManyField(User, through='FavoriteQuestion', related_name='favorite_questions')
+ #note: anonymity here applies to question only, but
+ #the field will still go to thread
+ #maybe we should rename it to is_question_anonymous
+ #we might have to duplicate the is_anonymous on the Post,
+ #if we are to allow anonymous answers
+ #the reason is that the title and tags belong to thread,
+ #but the question body to Post
+ is_anonymous = models.BooleanField(default=False)
+
+ objects = QuestionManager()
+
+ class Meta(content.Content.Meta):
+ db_table = u'question'
+
+ def _get_slug(self):
+ return slugify(self.title)
+
+ slug = property(_get_slug)
+
if getattr(settings, 'USE_SPHINX_SEARCH', False):
from djangosphinx.models import SphinxSearch
Question.add_to_class(
diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py
index 0d7fd666..ea13bce1 100644
--- a/askbot/tests/db_api_tests.py
+++ b/askbot/tests/db_api_tests.py
@@ -85,7 +85,7 @@ class DBApiTests(AskbotTestCase):
tags = 'aoeuaoeu',
revision_comment = 'hahahah'
)
- q.remove_author_anonymity()
+ q.thread.remove_author_anonymity()
q = self.reload_object(q)
self.assertFalse(q.is_anonymous)
for rev in q.revisions.all():
@@ -127,7 +127,7 @@ class DBApiTests(AskbotTestCase):
self.post_answer(user = self.other_user)
self.user.delete_question(self.question)
self.assert_post_is_deleted(self.question)
- answer_count = self.question.get_answers(user = self.user).count()
+ answer_count = self.question.thread.get_answers(user = self.user).count()
answer = self.question.answers.all()[0]
self.assert_post_is_not_deleted(answer)
self.assertTrue(answer_count == 1)
diff --git a/askbot/views/readers.py b/askbot/views/readers.py
index 5b4a2bc8..4580a406 100644
--- a/askbot/views/readers.py
+++ b/askbot/views/readers.py
@@ -460,7 +460,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
logging.debug('answer_sort_method=' + unicode(answer_sort_method))
#load answers
- answers = question.get_answers(user = request.user)
+ answers = question.thread.get_answers(user = request.user)
answers = answers.select_related(depth=1)
user_answer_votes = {}
@@ -506,7 +506,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
request.session['question_view_times'] = {}
last_seen = request.session['question_view_times'].get(question.id, None)
- updated_when, updated_who = question.get_last_update_info()
+ updated_when, updated_who = question.thread.get_last_update_info()
if updated_who != request.user:
if last_seen:
@@ -548,7 +548,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
}
paginator_context = extra_tags.cnprog_paginator(paginator_data)
- favorited = question.has_favorite_by_user(request.user)
+ favorited = question.thread.has_favorite_by_user(request.user)
user_question_vote = 0
if request.user.is_authenticated():
votes = question.votes.select_related().filter(user=request.user)
diff --git a/askbot/views/writers.py b/askbot/views/writers.py
index 5f073b37..1f51cd3c 100644
--- a/askbot/views/writers.py
+++ b/askbot/views/writers.py
@@ -392,7 +392,7 @@ def edit_question(request, id):
if form.has_changed():
if form.cleaned_data['reveal_identity']:
- question.remove_author_anonymity()
+ question.thread.remove_author_anonymity()
is_anon_edit = form.cleaned_data['stay_anonymous']
is_wiki = form.cleaned_data.get('wiki', question.wiki)