diff options
-rw-r--r-- | askbot/management/commands/send_accept_answer_reminders.py | 1 | ||||
-rw-r--r-- | askbot/management/commands/send_email_alerts.py | 7 | ||||
-rw-r--r-- | askbot/management/commands/send_unanswered_question_reminders.py | 6 | ||||
-rw-r--r-- | askbot/models/question.py | 624 | ||||
-rw-r--r-- | askbot/tests/email_alert_tests.py | 55 |
5 files changed, 344 insertions, 349 deletions
diff --git a/askbot/management/commands/send_accept_answer_reminders.py b/askbot/management/commands/send_accept_answer_reminders.py index 537e8814..e53dbcdc 100644 --- a/askbot/management/commands/send_accept_answer_reminders.py +++ b/askbot/management/commands/send_accept_answer_reminders.py @@ -52,7 +52,6 @@ class Command(NoArgsCommand): if question_count == 0: continue - #tag_summary = get_tag_summary_from_questions(final_question_list) subject_line = _( 'Accept the best answer for %(question_count)d of your questions' ) % {'question_count': question_count} diff --git a/askbot/management/commands/send_email_alerts.py b/askbot/management/commands/send_email_alerts.py index 994d1209..9f600126 100644 --- a/askbot/management/commands/send_email_alerts.py +++ b/askbot/management/commands/send_email_alerts.py @@ -3,10 +3,9 @@ from django.core.management.base import NoArgsCommand from django.core.urlresolvers import reverse from django.db import connection from django.db.models import Q, F -from askbot.models import User, Question, Answer, Tag, PostRevision +from askbot.models import User, Question, Answer, Tag, PostRevision, Thread from askbot.models import Activity, EmailFeedSetting from askbot.models import Comment -from askbot.models.question import get_tag_summary_from_questions from django.utils.translation import ugettext as _ from django.utils.translation import ungettext from django.conf import settings as django_settings @@ -404,7 +403,9 @@ class Command(NoArgsCommand): if num_q > 0: url_prefix = askbot_settings.APP_URL - tag_summary = get_tag_summary_from_questions(q_list.keys()) + threads = Thread.objects.filter(id__in=[qq.thread_id for qq in q_list.keys()]) + tag_summary = Thread.objects.get_tag_summary_from_threads(threads) + question_count = len(q_list.keys()) subject_line = ungettext( diff --git a/askbot/management/commands/send_unanswered_question_reminders.py b/askbot/management/commands/send_unanswered_question_reminders.py index 3d5236dc..a57f79b8 100644 --- a/askbot/management/commands/send_unanswered_question_reminders.py +++ b/askbot/management/commands/send_unanswered_question_reminders.py @@ -5,7 +5,7 @@ from askbot.conf import settings as askbot_settings from django.utils.translation import ungettext from askbot.utils import mail from askbot.utils.classes import ReminderSchedule -from askbot.models.question import get_tag_summary_from_questions +from askbot.models.question import Thread DEBUG_THIS_COMMAND = False @@ -51,7 +51,9 @@ class Command(NoArgsCommand): if question_count == 0: continue - tag_summary = get_tag_summary_from_questions(final_question_list) + threads = Thread.objects.filter(id__in=[qq.thread_id for qq in final_question_list]) + tag_summary = Thread.objects.get_tag_summary_from_threads(threads) + subject_line = ungettext( '%(question_count)d unanswered question about %(topics)s', '%(question_count)d unanswered questions about %(topics)s', diff --git a/askbot/models/question.py b/askbot/models/question.py index e4602aeb..0e47bcf4 100644 --- a/askbot/models/question.py +++ b/askbot/models/question.py @@ -34,40 +34,315 @@ QUESTION_ORDER_BY_MAP = { 'relevance-desc': None#this is a special case for postges only } -def get_tag_summary_from_questions(questions): - """returns a humanized string containing up to - five most frequently used - unique tags coming from the ``questions``. - Variable ``questions`` is an iterable of - :class:`~askbot.models.Question` model objects. - - This is not implemented yet as a query set method, - because it is used on a list. - """ - #todo: in python 2.6 there is collections.Counter() thing - #which would be very useful here - tag_counts = dict() - for question in questions: - tag_names = question.get_tag_names() - for tag_name in tag_names: - if tag_name in tag_counts: - tag_counts[tag_name] += 1 +class ThreadManager(models.Manager): + def get_tag_summary_from_threads(self, threads): + """returns a humanized string containing up to + five most frequently used + unique tags coming from the ``threads``. + Variable ``threads`` is an iterable of + :class:`~askbot.models.Thread` model objects. + + This is not implemented yet as a query set method, + because it is used on a list. + """ + # TODO: In Python 2.6 there is collections.Counter() thing which would be very useful here + # TODO: In Python 2.5 there is `defaultdict` which already would be an improvement + tag_counts = dict() + for thread in threads: + for tag_name in thread.get_tag_names(): + if tag_name in tag_counts: + tag_counts[tag_name] += 1 + else: + tag_counts[tag_name] = 1 + tag_list = tag_counts.keys() + tag_list.sort(key=lambda t: tag_counts[t], reverse=True) + + #note that double quote placement is important here + if len(tag_list) == 1: + last_topic = '"' + elif len(tag_list) <= 5: + last_topic = _('" and "%s"') % tag_list.pop() + else: + tag_list = tag_list[:5] + last_topic = _('" and more') + + return '"' + '", "'.join(tag_list) + last_topic + + +class Thread(models.Model): + title = models.CharField(max_length=300) + + tags = models.ManyToManyField('Tag', related_name='threads') + + # Denormalised data, transplanted from Question + tagnames = models.CharField(max_length=125) + view_count = models.PositiveIntegerField(default=0) + favourite_count = models.PositiveIntegerField(default=0) + answer_count = models.PositiveIntegerField(default=0) + last_activity_at = models.DateTimeField(default=datetime.datetime.now) + last_activity_by = models.ForeignKey(User, related_name='unused_last_active_in_threads') + + followed_by = models.ManyToManyField(User, related_name='followed_threads') + favorited_by = models.ManyToManyField(User, through='FavoriteQuestion', related_name='unused_favorite_threads') + + 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 + ) + + accepted_answer = models.ForeignKey('askbot.Answer', null=True, blank=True) + answer_accepted_at = models.DateTimeField(null=True, blank=True) + + objects = ThreadManager() + + class Meta: + app_label = 'askbot' + + def _question(self): + return Question.objects.get(thread=self) + + def update_favorite_count(self): + self.favourite_count = FavoriteQuestion.objects.filter(thread=self).count() + self.save() + + def update_answer_count(self): + self.answer_count = self.get_answers().count() + self.save() + + def increase_view_count(self, increment=1): + qset = Thread.objects.filter(id=self.id) + qset.update(view_count=models.F('view_count') + increment) + self.view_count = qset.values('view_count')[0]['view_count'] # get the new view_count back because other pieces of code relies on such behaviour + + def set_closed_status(self, closed, closed_by, closed_at, close_reason): + self.closed = closed + self.closed_by = closed_by + self.closed_at = closed_at + self.close_reason = close_reason + self.save() + + def set_accepted_answer(self, answer, timestamp): + if answer and answer.question.thread != self: + raise ValueError("Answer doesn't belong to this thread") + self.accepted_answer = answer + self.answer_accepted_at = timestamp + self.save() + + def set_last_activity(self, last_activity_at, last_activity_by): + self.last_activity_at = last_activity_at + self.last_activity_by = last_activity_by + self.save() + + def get_tag_names(self): + "Creates a list of Tag names from the ``tagnames`` attribute." + return self.tagnames.split(u' ') + + 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): + """ + Get 10 similar questions for given one. + Questions with the individual tags will be added to list if above questions are not full. + + This function has a limitation that it will + retrieve only 100 records then select 10 most similar + from that list as querying entire database may + be very expensive - this function will benefit from + some sort of optimization + """ + + def get_data(): + thread_question = self._question() + + tags_list = self.tags.all() + similar_questions = Question.objects.filter(thread__tags__in=tags_list).\ + exclude(id = self.id).exclude(deleted = True).distinct()[:100] + + similar_questions = list(similar_questions) + for question in similar_questions: + 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) + + def remove_author_anonymity(self): + """removes anonymous flag from the question + and all its revisions + the function calls update method to make sure that + signals are not called + """ + #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 + 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 + automatically deleted. + + When an added tag does not exist - it is created + + Tag use counts are recalculated + + A signal tags updated is sent + """ + thread_question = self._question() + + previous_tags = list(self.tags.all()) + + previous_tagnames = set([tag.name for tag in previous_tags]) + updated_tagnames = set(t for t in tagnames.split(' ')) + + removed_tagnames = previous_tagnames - updated_tagnames + added_tagnames = updated_tagnames - previous_tagnames + + modified_tags = list() + #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) + + #if any of the removed tags reached use count == 1 that means they must be deleted + for tag in removed_tags: + if tag.used_count == 1: + #we won't modify used count b/c it's done below anyway + removed_tags.remove(tag) + #todo - do we need to use fields deleted_by and deleted_at? + tag.delete()#auto-delete tags whose use count dwindled + + #remember modified tags, we'll need to update use counts on them + modified_tags = removed_tags + + #add new tags to the relation + if added_tagnames: + #find reused tags + reused_tags = Tag.objects.filter(name__in = added_tagnames) + #undelete them, because we are using them + reused_count = reused_tags.update( + deleted = False, + deleted_by = None, + deleted_at = None + ) + #if there are brand new tags, create them and finalize the added tag list + if reused_count < len(added_tagnames): + added_tags = list(reused_tags) + + reused_tagnames = set([tag.name for tag in reused_tags]) + new_tagnames = added_tagnames - reused_tagnames + for name in new_tagnames: + new_tag = Tag.objects.create( + name = name, + created_by = user, + used_count = 1 + ) + added_tags.append(new_tag) else: - tag_counts[tag_name] = 1 - tag_list = tag_counts.keys() - #sort in descending order - tag_list.sort(lambda x, y: cmp(tag_counts[y], tag_counts[x])) + added_tags = reused_tags + + #finally add tags to the relation and extend the modified list + self.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 = thread_question, + tags = modified_tags, + user = user, + timestamp = timestamp + ) + return True + + return False + + 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() + + self.tagnames = tagnames + self.save() + + # Update the Question itself + if silent == False: + thread_question.last_edited_at = retagged_at + #thread_question.thread.last_activity_at = retagged_at + thread_question.last_edited_by = retagged_by + #thread_question.thread.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) + + # Create a new revision + latest_revision = thread_question.get_latest_revision() + PostRevision.objects.create_question_revision( + question = thread_question, + title = latest_revision.title, + author = retagged_by, + revised_at = retagged_at, + tagnames = tagnames, + summary = const.POST_STATUS['retagged'], + text = latest_revision.text + ) - #note that double quote placement is important here - if len(tag_list) == 1: - last_topic = '"' - elif len(tag_list) <= 5: - last_topic = _('" and "%s"') % tag_list.pop() - else: - tag_list = tag_list[:5] - last_topic = _('" and more') + def has_favorite_by_user(self, user): + if not user.is_authenticated(): + return False + + return FavoriteQuestion.objects.filter(thread=self, user=user).exists() + + def get_last_update_info(self): + thread_question = self._question() + + 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 '"' + '", "'.join(tag_list) + last_topic + return when, who class QuestionQuerySet(models.query.QuerySet): @@ -119,16 +394,16 @@ class QuestionQuerySet(models.query.QuerySet): return question def get_by_text_query(self, search_query): - """returns a query set of questions, + """returns a query set of questions, matching the full text query """ #todo - goes to thread - we search whole threads if getattr(settings, 'USE_SPHINX_SEARCH', False): matching_questions = Question.sphinx_search.query(search_query) - question_ids = [q.id for q in matching_questions] + question_ids = [q.id for q in matching_questions] return Question.objects.filter(deleted = False, id__in = question_ids) if settings.DATABASE_ENGINE == 'mysql' and mysql.supports_full_text_search(): - return self.filter( + return self.filter( models.Q(thread__title__search = search_query) \ | models.Q(text__search = search_query) \ | models.Q(thread__tagnames__search = search_query) \ @@ -170,7 +445,7 @@ class QuestionQuerySet(models.query.QuerySet): author_selector = search_state.author sort_method = getattr( - search_state, + search_state, 'sort', const.DEFAULT_POST_SORT_METHOD ) @@ -201,7 +476,7 @@ class QuestionQuerySet(models.query.QuerySet): if len(query_users) > 0: qs = qs.filter(author__in = query_users) - if tag_selector: + if tag_selector: for tag in tag_selector: qs = qs.filter(thread__tags__name = tag) @@ -227,7 +502,7 @@ class QuestionQuerySet(models.query.QuerySet): favorite_filter |= models.Q(author__in = followed_users) favorite_filter |= models.Q(answers__author__in = followed_users) qs = qs.filter(favorite_filter) - + #user contributed questions & answers if author_selector: try: @@ -268,7 +543,7 @@ class QuestionQuerySet(models.query.QuerySet): #filter by interesting tags only interesting_tag_filter = models.Q(thread__tags__in = interesting_tags) if request_user.has_interesting_wildcard_tags(): - interesting_wildcards = request_user.interesting_tags.split() + interesting_wildcards = request_user.interesting_tags.split() extra_interesting_tags = Tag.objects.get_by_wildcards( interesting_wildcards ) @@ -300,7 +575,7 @@ class QuestionQuerySet(models.query.QuerySet): #exclude ignored tags if the user wants to qs = qs.exclude(thread__tags__in=ignored_tags) if request_user.has_ignored_wildcard_tags(): - ignored_wildcards = request_user.ignored_tags.split() + ignored_wildcards = request_user.ignored_tags.split() extra_ignored_tags = Tag.objects.get_by_wildcards( ignored_wildcards ) @@ -446,282 +721,13 @@ class QuestionQuerySet(models.query.QuerySet): class QuestionManager(BaseQuerySetManager): - """chainable custom query set manager for + """chainable custom query set manager for questions """ #todo: becomes thread manager def get_query_set(self): return QuestionQuerySet(self.model) -class Thread(models.Model): - title = models.CharField(max_length=300) - - tags = models.ManyToManyField('Tag', related_name='threads') - - # Denormalised data, transplanted from Question - tagnames = models.CharField(max_length=125) - view_count = models.PositiveIntegerField(default=0) - favourite_count = models.PositiveIntegerField(default=0) - answer_count = models.PositiveIntegerField(default=0) - last_activity_at = models.DateTimeField(default=datetime.datetime.now) - last_activity_by = models.ForeignKey(User, related_name='unused_last_active_in_threads') - - followed_by = models.ManyToManyField(User, related_name='followed_threads') - favorited_by = models.ManyToManyField(User, through='FavoriteQuestion', related_name='unused_favorite_threads') - - 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 - ) - - accepted_answer = models.ForeignKey('askbot.Answer', null=True, blank=True) - answer_accepted_at = models.DateTimeField(null=True, blank=True) - - class Meta: - app_label = 'askbot' - - def _question(self): - return Question.objects.get(thread=self) - - def update_favorite_count(self): - self.favourite_count = FavoriteQuestion.objects.filter(thread=self).count() - self.save() - - def update_answer_count(self): - self.answer_count = self.get_answers().count() - self.save() - - def increase_view_count(self, increment=1): - qset = Thread.objects.filter(id=self.id) - qset.update(view_count=models.F('view_count') + increment) - self.view_count = qset.values('view_count')[0]['view_count'] # get the new view_count back because other pieces of code relies on such behaviour - - def set_closed_status(self, closed, closed_by, closed_at, close_reason): - self.closed = closed - self.closed_by = closed_by - self.closed_at = closed_at - self.close_reason = close_reason - self.save() - - def set_accepted_answer(self, answer, timestamp): - if answer and answer.question.thread != self: - raise ValueError("Answer doesn't belong to this thread") - self.accepted_answer = answer - self.answer_accepted_at = timestamp - self.save() - - def set_last_activity(self, last_activity_at, last_activity_by): - self.last_activity_at = last_activity_at - self.last_activity_by = last_activity_by - 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): - """ - Get 10 similar questions for given one. - Questions with the individual tags will be added to list if above questions are not full. - - This function has a limitation that it will - retrieve only 100 records then select 10 most similar - from that list as querying entire database may - be very expensive - this function will benefit from - some sort of optimization - """ - - def get_data(): - thread_question = self._question() - - tags_list = self.tags.all() - similar_questions = Question.objects.filter(thread__tags__in=tags_list).\ - exclude(id = self.id).exclude(deleted = True).distinct()[:100] - - similar_questions = list(similar_questions) - for question in similar_questions: - 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) - - def remove_author_anonymity(self): - """removes anonymous flag from the question - and all its revisions - the function calls update method to make sure that - signals are not called - """ - #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 - 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 - automatically deleted. - - When an added tag does not exist - it is created - - Tag use counts are recalculated - - A signal tags updated is sent - """ - thread_question = self._question() - - previous_tags = list(self.tags.all()) - - previous_tagnames = set([tag.name for tag in previous_tags]) - updated_tagnames = set(t for t in tagnames.split(' ')) - - removed_tagnames = previous_tagnames - updated_tagnames - added_tagnames = updated_tagnames - previous_tagnames - - modified_tags = list() - #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) - - #if any of the removed tags reached use count == 1 that means they must be deleted - for tag in removed_tags: - if tag.used_count == 1: - #we won't modify used count b/c it's done below anyway - removed_tags.remove(tag) - #todo - do we need to use fields deleted_by and deleted_at? - tag.delete()#auto-delete tags whose use count dwindled - - #remember modified tags, we'll need to update use counts on them - modified_tags = removed_tags - - #add new tags to the relation - if added_tagnames: - #find reused tags - reused_tags = Tag.objects.filter(name__in = added_tagnames) - #undelete them, because we are using them - reused_count = reused_tags.update( - deleted = False, - deleted_by = None, - deleted_at = None - ) - #if there are brand new tags, create them and finalize the added tag list - if reused_count < len(added_tagnames): - added_tags = list(reused_tags) - - reused_tagnames = set([tag.name for tag in reused_tags]) - new_tagnames = added_tagnames - reused_tagnames - for name in new_tagnames: - new_tag = Tag.objects.create( - name = name, - created_by = user, - used_count = 1 - ) - added_tags.append(new_tag) - else: - added_tags = reused_tags - - #finally add tags to the relation and extend the modified list - self.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 = thread_question, - tags = modified_tags, - user = user, - timestamp = timestamp - ) - return True - - return False - - 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() - - self.tagnames = tagnames - self.save() - - # Update the Question itself - if silent == False: - thread_question.last_edited_at = retagged_at - #thread_question.thread.last_activity_at = retagged_at - thread_question.last_edited_by = retagged_by - #thread_question.thread.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) - - # Create a new revision - latest_revision = thread_question.get_latest_revision() - PostRevision.objects.create_question_revision( - question = thread_question, - title = latest_revision.title, - author = retagged_by, - revised_at = retagged_at, - tagnames = tagnames, - summary = const.POST_STATUS['retagged'], - text = latest_revision.text - ) - - def has_favorite_by_user(self, user): - if not user.is_authenticated(): - return False - - return FavoriteQuestion.objects.filter(thread=self, user=user).exists() - - def get_last_update_info(self): - thread_question = self._question() - - 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): post_type = 'question' diff --git a/askbot/tests/email_alert_tests.py b/askbot/tests/email_alert_tests.py index f4f84c00..bd2e5780 100644 --- a/askbot/tests/email_alert_tests.py +++ b/askbot/tests/email_alert_tests.py @@ -14,7 +14,7 @@ from askbot import models from askbot.utils import mail from askbot.conf import settings as askbot_settings from askbot import const -from askbot.models.question import get_tag_summary_from_questions +from askbot.models.question import Thread TO_JSON = functools.partial(serializers.serialize, 'json') @@ -680,39 +680,26 @@ class InstantQAnsEmailAlertTests(EmailAlertTests): class DelayedAlertSubjectLineTests(TestCase): def test_topics_in_subject_line(self): - q1 = models.Question(id=1, thread=models.Thread(tagnames='one two three four five')) - q2 = models.Question(id=2, thread=models.Thread(tagnames='two three four five')) - q3 = models.Question(id=3, thread=models.Thread(tagnames='three four five')) - q4 = models.Question(id=4, thread=models.Thread(tagnames='four five')) - q5 = models.Question(id=5, thread=models.Thread(tagnames='five')) - q6 = models.Question(id=6, thread=models.Thread(tagnames='six')) - q7 = models.Question(id=7, thread=models.Thread(tagnames='six')) - q8 = models.Question(id=8, thread=models.Thread(tagnames='six')) - q9 = models.Question(id=9, thread=models.Thread(tagnames='six')) - q10 = models.Question(id=10, thread=models.Thread(tagnames='six')) - q11 = models.Question(id=11, thread=models.Thread(tagnames='six')) - q_dict = { - q1:'', q2:'', q3:'', q4:'', q5:'', q6:'', q7:'', - q8:'', q9:'', q10:'', q11:'', - } - subject = get_tag_summary_from_questions(q_dict.keys()) - - self.assertTrue('one' not in subject) - self.assertTrue('two' in subject) - self.assertTrue('three' in subject) - self.assertTrue('four' in subject) - self.assertTrue('five' in subject) - self.assertTrue('six' in subject) - i2 = subject.index('two') - i3 = subject.index('three') - i4 = subject.index('four') - i5 = subject.index('five') - i6 = subject.index('six') - order = [i6, i5, i4, i3, i2] - self.assertEquals( - order, - sorted(order) - ) + threads = [ + models.Thread(tagnames='one two three four five'), + models.Thread(tagnames='two three four five'), + models.Thread(tagnames='three four five'), + models.Thread(tagnames='four five'), + models.Thread(tagnames='five'), + ] + subject = Thread.objects.get_tag_summary_from_threads(threads) + self.assertEqual('"five", "four", "three", "two" and "one"', subject) + + threads += [ + models.Thread(tagnames='six'), + models.Thread(tagnames='six'), + models.Thread(tagnames='six'), + models.Thread(tagnames='six'), + models.Thread(tagnames='six'), + models.Thread(tagnames='six'), + ] + subject = Thread.objects.get_tag_summary_from_threads(threads) + self.assertEqual('"six", "five", "four", "three", "two" and more', subject) class FeedbackTests(utils.AskbotTestCase): def setUp(self): |