summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-12-03 01:52:36 -0500
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-12-03 01:52:36 -0500
commit60bc97227cf407e7bf12c5f249c6d7f73cef5276 (patch)
tree364b5bb12d302f007a00dee5c0075fd70f1aabbe
parent7598052c9e7183d4d383290e3a71aba6d1d54187 (diff)
downloadaskbot-60bc97227cf407e7bf12c5f249c6d7f73cef5276.tar.gz
askbot-60bc97227cf407e7bf12c5f249c6d7f73cef5276.tar.bz2
askbot-60bc97227cf407e7bf12c5f249c6d7f73cef5276.zip
added several more badges
-rw-r--r--askbot/conf/badges.py67
-rw-r--r--askbot/models/__init__.py6
-rw-r--r--askbot/models/badges.py239
-rw-r--r--askbot/tests/badge_tests.py159
-rw-r--r--askbot/views/readers.py12
5 files changed, 441 insertions, 42 deletions
diff --git a/askbot/conf/badges.py b/askbot/conf/badges.py
index b23f35e2..d5f8313a 100644
--- a/askbot/conf/badges.py
+++ b/askbot/conf/badges.py
@@ -54,7 +54,7 @@ settings.register(
BADGES,
'GOOD_ANSWER_BADGE_MIN_UPVOTES',
default=3,
- description=_('Good answer: minimum upvotes for the answer')
+ description=_('Good Answer: minimum upvotes for the answer')
)
)
@@ -62,7 +62,70 @@ settings.register(
IntegerValue(
BADGES,
'GREAT_ANSWER_BADGE_MIN_UPVOTES',
+ default=5,
+ description=_('Great Answer: minimum upvotes for the answer')
+ )
+)
+
+settings.register(
+ IntegerValue(
+ BADGES,
+ 'NICE_QUESTION_BADGE_MIN_UPVOTES',
+ default=2,
+ description=_('Nice Question: minimum upvotes for the question')
+ )
+)
+
+settings.register(
+ IntegerValue(
+ BADGES,
+ 'GOOD_QUESTION_BADGE_MIN_UPVOTES',
default=3,
- description=_('Great answer: minimum upvotes for the answer')
+ description=_('Good Question: minimum upvotes for the question')
+ )
+)
+
+settings.register(
+ IntegerValue(
+ BADGES,
+ 'GREAT_QUESTION_BADGE_MIN_UPVOTES',
+ default=5,
+ description=_('Great Question: minimum upvotes for the question')
+ )
+)
+
+settings.register(
+ IntegerValue(
+ BADGES,
+ 'POPULAR_QUESTION_BADGE_MIN_VIEWS',
+ default=5,
+ description=_('Popular Question: minimum views')
+ )
+)
+
+settings.register(
+ IntegerValue(
+ BADGES,
+ 'NOTABLE_QUESTION_BADGE_MIN_VIEWS',
+ default=150,
+ description=_('Notable Question: minimum views')
+ )
+)
+
+settings.register(
+ IntegerValue(
+ BADGES,
+ 'FAMOUS_QUESTION_BADGE_MIN_VIEWS',
+ default=500,
+ description=_('Famous Question: minimum views')
+ )
+)
+
+settings.register(
+ IntegerValue(
+ BADGES,
+ 'SELF_LEARNER_BADGE_MIN_UPVOTES',
+ default=500,
+ description=_('Self-Learner: minimum answer upvotes')
)
)
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index e494c592..43982e37 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -1322,9 +1322,9 @@ def toggle_favorite_question(self, question, timestamp=None, cancel=False):
VOTES_TO_EVENTS = {
(Vote.VOTE_UP, 'answer'): 'upvote_answer',
-# (Vote.VOTE_UP, 'question'): 'upvote_question',
-# (Vote.VOTE_DOWN, 'answer'): 'downvote_answer',
-# (Vote.VOTE_DOWN, 'question'): 'downvote_question'
+ (Vote.VOTE_UP, 'question'): 'upvote_question',
+ (Vote.VOTE_DOWN, 'question'): 'downvote',
+ (Vote.VOTE_DOWN, 'answer'): 'downvote'
}
@auto_now_timestamp
def _process_vote(user, post, timestamp=None, cancel=False, vote_type=None):
diff --git a/askbot/models/badges.py b/askbot/models/badges.py
index cc02045e..99a10ef2 100644
--- a/askbot/models/badges.py
+++ b/askbot/models/badges.py
@@ -179,10 +179,72 @@ class Teacher(Badge):
return self.award(context_object.author, context_object, timestamp)
return False
+class FirstVote(Badge):
+ """this badge is not awarded directly, but through
+ Supporter and Critic, which must provide
+ * key, name and description properties through __new__ call
+ """
+ def __init__(self):
+ super(FirstVote, self).__init__(
+ key = self.key,
+ name = self.name,
+ description = self.description,
+ level = const.BRONZE_BADGE,
+ multiple = False
+ )
+
+ def consider_award(self, actor = None,
+ context_object = None, timestamp = None):
+ if context_object.post_type not in ('question', 'answer'):
+ return False
+ return self.award(actor, context_object, timestamp)
+
+class Supporter(FirstVote):
+ """first upvote"""
+ def __new__(cls):
+ self = super(Supporter, cls).__new__(cls)
+ self.key = 'supporter'
+ self.name = _('Supporter')
+ self.description = _('First upvote')
+ return self
+
+class Critic(FirstVote):
+ """like supporter, but for downvote"""
+ def __new__(cls):
+ self = super(Critic, cls).__new__(cls)
+ self.key = 'critic'
+ self.name = _('Critic')
+ self.description = _('First downvote')
+ return self
+
+class SelfLearner(Badge):
+ def __init__(self):
+ description = _('Answered own question with at least %(num)s up votes')
+ min_votes = askbot_settings.SELF_LEARNER_BADGE_MIN_UPVOTES
+ super(SelfLearner, self).__init__(
+ key = 'self-learner',
+ name = _('Self-Learner'),
+ description = description % {'num': min_votes},
+ level = const.BRONZE_BADGE,
+ multiple = True
+ )
+
+ def consider_award(self, actor = None,
+ context_object = None, timestamp = None):
+ if context_object.post_type != 'answer':
+ return False
+
+ min_upvotes = askbot_settings.SELF_LEARNER_BADGE_MIN_UPVOTES
+ question = context_object.question
+ answer = context_object
+
+ if question.author == answer.author and answer.score >= min_upvotes:
+ self.award(context_object.author, context_object, timestamp)
+
class QualityPost(Badge):
"""Generic Badge for Nice/Good/Great Question or Answer
this badge is not used directly but is instantiated
- via subclasses
+ via subclasses created via __new__() method definitions
The subclass has a responsibility to specify properties:
* min_votes - a value from live settings
@@ -200,7 +262,7 @@ class QualityPost(Badge):
def consider_award(self, actor = None,
context_object = None, timestamp = None):
- if context_object.post_type != 'answer':
+ if context_object.post_type not in ('answer', 'question'):
return False
if context_object.score >= self.min_votes:
return self.award(context_object.author, context_object, timestamp)
@@ -242,52 +304,181 @@ class GreatAnswer(QualityPost):
self.post_type = 'answer'
return self
+class NiceQuestion(QualityPost):
+ def __new__(cls):
+ self = super(NiceQuestion, cls).__new__(cls)
+ self.name = _('Nice Question')
+ self.key = 'nice-question'
+ self.level = const.BRONZE_BADGE
+ self.multiple = True
+ self.min_votes = askbot_settings.NICE_QUESTION_BADGE_MIN_UPVOTES
+ self.description = _('Question voted up %(num)s times') % {'num': self.min_votes}
+ self.post_type = 'question'
+ return self
+
+class GoodQuestion(QualityPost):
+ def __new__(cls):
+ self = super(GoodQuestion, cls).__new__(cls)
+ self.name = _('Good Question')
+ self.key = 'good-question'
+ self.level = const.SILVER_BADGE
+ self.multiple = True
+ self.min_votes = askbot_settings.GOOD_QUESTION_BADGE_MIN_UPVOTES
+ self.description = _('Question voted up %(num)s times') % {'num': self.min_votes}
+ self.post_type = 'question'
+ return self
+
+class GreatQuestion(QualityPost):
+ def __new__(cls):
+ self = super(GreatQuestion, cls).__new__(cls)
+ self.name = _('Great Question')
+ self.key = 'great-question'
+ self.level = const.GOLD_BADGE
+ self.multiple = True
+ self.min_votes = askbot_settings.GREAT_QUESTION_BADGE_MIN_UPVOTES
+ self.description = _('Question voted up %(num)s times') % {'num': self.min_votes}
+ self.post_type = 'question'
+ return self
+
+class Student(QualityPost):
+ def __new__(cls):
+ self = super(Student , cls).__new__(cls)
+ self.name = _('Student')
+ self.key = 'student'
+ self.level = const.BRONZE_BADGE
+ self.multiple = False
+ self.min_votes = 1
+ self.description = _('Asked first question with at least one up vote')
+ self.post_type = 'question'
+ return self
+
+class FrequentedQuestion(Badge):
+ """this badge is not awarded directly
+ but must be subclassed by Popular, Notable and Famous Question
+ badges via __new__() method definitions
+
+ The subclass has a responsibility to specify properties:
+ * min_views - a value from live settings
+ * key, name, description and level and multiple - as intended in the Badge
+ """
+ def __init__(self):
+ super(FrequentedQuestion, self).__init__(
+ key = self.key,
+ name = self.name,
+ description = self.description,
+ level = self.level,
+ multiple = True
+ )
+
+ def consider_award(self, actor = None,
+ context_object = None, timestamp = None):
+ if context_object.post_type != 'question':
+ return False
+ if context_object.view_count >= self.min_views:
+ return self.award(context_object.author, context_object, timestamp)
+ return False
+
+class PopularQuestion(FrequentedQuestion):
+ def __new__(cls):
+ self = super(PopularQuestion, cls).__new__(cls)
+ self.name = _('Popular Question')
+ self.key = 'popular-question'
+ self.level = const.BRONZE_BADGE
+ self.min_views = askbot_settings.POPULAR_QUESTION_BADGE_MIN_VIEWS
+ self.description = _('Asked a question with %(views)s views') \
+ % {'views' : self.min_views}
+ return self
+
+class NotableQuestion(FrequentedQuestion):
+ def __new__(cls):
+ self = super(NotableQuestion, cls).__new__(cls)
+ self.name = _('Notable Question')
+ self.key = 'notable-question'
+ self.level = const.SILVER_BADGE
+ self.min_views = askbot_settings.NOTABLE_QUESTION_BADGE_MIN_VIEWS
+ self.description = _('Asked a question with %(views)s views') \
+ % {'views' : self.min_views}
+ return self
+
+class FamousQuestion(FrequentedQuestion):
+ def __new__(cls):
+ self = super(FamousQuestion, cls).__new__(cls)
+ self.name = _('Famous Question')
+ self.key = 'famous-question'
+ self.level = const.GOLD_BADGE
+ self.multiple = True
+ self.min_views = askbot_settings.FAMOUS_QUESTION_BADGE_MIN_VIEWS
+ self.description = _('Asked a question with %(views)s views') \
+ % {'views' : self.min_views}
+ return self
ORIGINAL_DATA = """
- (_('Nice Question'), 3, _('nice-question'), _('Question voted up 10 times'), True, 0),
+ (_('Civic duty'), 2, _('civic-duty'), _('Voted 300 times'), False, 0),
+
+ (_('Enlightened'), 2, _('enlightened'), _('First answer was accepted with at least 10 up votes'), False, 0),
+ (_('Guru'), 2, _('guru'), _('Accepted answer and voted up 40 times'), True, 0),
+
+ (_('Necromancer'), 2, _('necromancer'), _('Answered a question more than 60 days later with at least 5 votes'), True, 0),
+
+ (_('Scholar'), 3, _('scholar'), _('First accepted answer on your own question'), False, 0),
(_('Pundit'), 3, _('pundit'), _('Left 10 comments with score of 10 or more'), False, 0),
- (_('Popular Question'), 3, _('popular-question'), _('Asked a question with 1,000 views'), True, 0),
(_('Citizen patrol'), 3, _('citizen-patrol'), _('First flagged post'), False, 0),
+
(_('Cleanup'), 3, _('cleanup'), _('First rollback'), False, 0),
- (_('Critic'), 3, _('critic'), _('First down vote'), False, 0),
+
(_('Editor'), 3, _('editor'), _('First edit'), False, 0),
+ (_('Strunk & White'), 2, _('strunk-and-white'), _('Edited 100 entries'), False, 0),
(_('Organizer'), 3, _('organizer'), _('First retag'), False, 0),
- (_('Scholar'), 3, _('scholar'), _('First accepted answer on your own question'), False, 0),
- (_('Student'), 3, _('student'), _('Asked first question with at least one up vote'), False, 0),
- (_('Supporter'), 3, _('supporter'), _('First up vote'), False, 0),
+
(_('Autobiographer'), 3, _('autobiographer'), _('Completed all user profile fields'), False, 0),
- (_('Self-Learner'), 3, _('self-learner'), _('Answered your own question with at least 3 up votes'), True, 0),
- (_('Great Question'), 1, _('great-question'), _('Question voted up 100 times'), True, 0),
+
(_('Stellar Question'), 1, _('stellar-question'), _('Question favorited by 100 users'), True, 0),
- (_('Famous question'), 1, _('famous-question'), _('Asked a question with 10,000 views'), True, 0),
- (_('Alpha'), 2, _('alpha'), _('Actively participated in the private alpha'), False, 0),
- (_('Good Question'), 2, _('good-question'), _('Question voted up 25 times'), True, 0),
(_('Favorite Question'), 2, _('favorite-question'), _('Question favorited by 25 users'), True, 0),
- (_('Civic duty'), 2, _('civic-duty'), _('Voted 300 times'), False, 0),
- (_('Strunk & White'), 2, _('strunk-and-white'), _('Edited 100 entries'), False, 0),
+
+ (_('Alpha'), 2, _('alpha'), _('Actively participated in the private alpha'), False, 0),
+
(_('Generalist'), 2, _('generalist'), _('Active in many different tags'), False, 0),
(_('Expert'), 2, _('expert'), _('Very active in one tag'), False, 0),
+ (_('Taxonomist'), 2, _('taxonomist'), _('Created a tag used by 50 questions'), True, 0)
+
(_('Yearling'), 2, _('yearling'), _('Active member for a year'), False, 0),
- (_('Notable Question'), 2, _('notable-question'), _('Asked a question with 2,500 views'), True, 0),
- (_('Enlightened'), 2, _('enlightened'), _('First answer was accepted with at least 10 up votes'), False, 0),
(_('Beta'), 2, _('beta'), _('Actively participated in the private beta'), False, 0),
- (_('Guru'), 2, _('guru'), _('Accepted answer and voted up 40 times'), True, 0),
- (_('Necromancer'), 2, _('necromancer'), _('Answered a question more than 60 days later with at least 5 votes'), True, 0),
- (_('Taxonomist'), 2, _('taxonomist'), _('Created a tag used by 50 questions'), True, 0)
"""
BADGES = {
'disciplined': Disciplined,
'peer-pressure': PeerPressure,
'teacher': Teacher,
+ 'student': Student,
+ 'supporter': Supporter,
+ 'self-learner': SelfLearner,
'nice-answer': NiceAnswer,
'good-answer': GoodAnswer,
'great-answer': GreatAnswer,
+ 'nice-question': NiceQuestion,
+ 'good-question': GoodQuestion,
+ 'great-question': GreatQuestion,
+ 'popular-question': PopularQuestion,
+ 'notable-question': NotableQuestion,
+ 'famous-question': FamousQuestion,
+ 'critic': Critic,
}
+#events are sent as a parameter via signal award_badges_signal
+#from appropriate locations in the code of askbot application
+#most likely - from manipulator functions that are added to the User objects
EVENTS_TO_BADGES = {
- 'upvote_answer': (Teacher, NiceAnswer, GoodAnswer, GreatAnswer),
+ 'upvote_answer': (
+ Teacher, NiceAnswer, GoodAnswer,
+ GreatAnswer, Supporter, SelfLearner
+ ),
+ 'upvote_question': (
+ NiceQuestion, GoodQuestion,
+ GreatQuestion, Student, Supporter
+ ),
+ 'downvote': (Critic,),#no regard for question or answer for now
'delete_post': (Disciplined, PeerPressure,),
+ 'view_question': (PopularQuestion, NotableQuestion, FamousQuestion,),
}
def get_badge(name = None):
@@ -303,6 +494,8 @@ def init_badges():
`BADGES` dictionary
"""
#todo: maybe better to redo individual badges
+ #so that get_stored_data() is called implicitly
+ #from the __init__ function?
for key in BADGES.keys():
get_badge(key).get_stored_data()
@@ -320,13 +513,13 @@ def award_badges(event = None, actor = None,
context_object = None, timestamp = None, **kwargs):
"""function that is called when signal `award_badges_signal` is sent
"""
-
try:
consider_badges = EVENTS_TO_BADGES[event]
except KeyError:
raise NotImplementedError('event "%s" is not implemented' % event)
for badge in consider_badges:
- badge().consider_award(actor, context_object, timestamp)
+ badge_instance = badge()
+ badge_instance.consider_award(actor, context_object, timestamp)
award_badges_signal.connect(award_badges)
diff --git a/askbot/tests/badge_tests.py b/askbot/tests/badge_tests.py
index c74ea461..be7e604f 100644
--- a/askbot/tests/badge_tests.py
+++ b/askbot/tests/badge_tests.py
@@ -1,3 +1,4 @@
+from django.test.client import Client
from askbot.tests.utils import AskbotTestCase
from askbot.conf import settings
from askbot import models
@@ -8,17 +9,19 @@ class BadgeTests(AskbotTestCase):
self.u1 = self.create_user(username = 'user1')
self.u2 = self.create_user(username = 'user2')
self.u3 = self.create_user(username = 'user3')
+ self.client = Client()
- def assert_have_badge(self, badge_key, expected_count = 1):
- count = models.Award.objects.filter(badge__slug = badge_key).count()
+ def assert_have_badge(self, badge_key, recipient = None, expected_count = 1):
+ filters = {'badge__slug': badge_key, 'user': recipient}
+ count = models.Award.objects.filter(**filters).count()
self.assertEquals(count, expected_count)
- def assert_voted_answer_badge_works(self,
+ def assert_upvoted_answer_badge_works(self,
badge_key = None,
min_score = None,
multiple = False
):
- """test answer badge where answer author
+ """test answer badge where answer author is the recipient
where badge award is triggered by upvotes
* min_score - minimum # of upvotes required
* multiple - multiple award or not
@@ -26,16 +29,16 @@ class BadgeTests(AskbotTestCase):
"""
question = self.post_question(user = self.u1)
answer = self.post_answer(user = self.u2, question = question)
- answer.score = min_score
+ answer.score = min_score - 1
answer.save()
self.u1.upvote(answer)
- self.assert_have_badge(badge_key)
+ self.assert_have_badge(badge_key, recipient = self.u2)
self.u3.upvote(answer)
- self.assert_have_badge(badge_key, expected_count = 1)
+ self.assert_have_badge(badge_key, recipient = self.u2, expected_count = 1)
#post another question and check that there are no new badges
answer2 = self.post_answer(user = self.u2, question = question)
- answer2.score = min_score
+ answer2.score = min_score - 1
answer2.save()
self.u1.upvote(answer2)
@@ -44,14 +47,60 @@ class BadgeTests(AskbotTestCase):
else:
expected_count = 1
- self.assert_have_badge(badge_key, expected_count = expected_count)
+ self.assert_have_badge(
+ badge_key,
+ recipient = self.u2,
+ expected_count = expected_count
+ )
+
+ def assert_upvoted_question_badge_works(self,
+ badge_key = None,
+ min_score = None,
+ multiple = False
+ ):
+ """test question badge where question author is the recipient
+ where badge award is triggered by upvotes
+ * min_score - minimum # of upvotes required
+ * multiple - multiple award or not
+ * badge_key - key on askbot.models.badges.Badge object
+ """
+ question = self.post_question(user = self.u1)
+ question.score = min_score - 1
+ question.save()
+ self.u2.upvote(question)
+ self.assert_have_badge(badge_key, recipient = self.u1)
+ self.u3.upvote(question)
+ self.assert_have_badge(badge_key, recipient = self.u1, expected_count = 1)
+
+ #post another question and check that there are no new badges
+ question2 = self.post_question(user = self.u1)
+ question2.score = min_score - 1
+ question2.save()
+ self.u2.upvote(question2)
+
+ if multiple == True:
+ expected_count = 2
+ else:
+ expected_count = 1
+
+ self.assert_have_badge(
+ badge_key,
+ recipient = self.u1,
+ expected_count = expected_count
+ )
def test_disciplined_badge(self):
question = self.post_question(user = self.u1)
question.score = settings.DISCIPLINED_BADGE_MIN_UPVOTES
question.save()
self.u1.delete_question(question)
- self.assert_have_badge('disciplined')
+ self.assert_have_badge('disciplined', recipient = self.u1)
+
+ question2 = self.post_question(user = self.u1)
+ question2.score = settings.DISCIPLINED_BADGE_MIN_UPVOTES
+ question2.save()
+ self.u1.delete_question(question2)
+ self.assert_have_badge('disciplined', recipient = self.u1, expected_count = 2)
def test_peer_pressure_badge(self):
question = self.post_question(user = self.u1)
@@ -59,18 +108,102 @@ class BadgeTests(AskbotTestCase):
answer.score = -1*settings.PEER_PRESSURE_BADGE_MIN_DOWNVOTES
answer.save()
self.u1.delete_answer(answer)
- self.assert_have_badge('peer-pressure')
+ self.assert_have_badge('peer-pressure', recipient = self.u1)
def test_teacher_badge(self):
- self.assert_voted_answer_badge_works(
+ self.assert_upvoted_answer_badge_works(
badge_key = 'teacher',
min_score = settings.TEACHER_BADGE_MIN_UPVOTES,
multiple = False
)
def test_nice_answer_badge(self):
- self.assert_voted_answer_badge_works(
+ self.assert_upvoted_answer_badge_works(
badge_key = 'nice-answer',
min_score = settings.NICE_ANSWER_BADGE_MIN_UPVOTES,
multiple = True
)
+
+ def test_nice_question_badge(self):
+ self.assert_upvoted_question_badge_works(
+ badge_key = 'nice-question',
+ min_score = settings.NICE_QUESTION_BADGE_MIN_UPVOTES,
+ multiple = True
+ )
+
+ def test_popular_question_badge(self):
+ question = self.post_question(user = self.u1)
+ min_views = settings.POPULAR_QUESTION_BADGE_MIN_VIEWS
+ question.view_count = min_views - 1
+ question.save()
+
+ #patch not_a_robot_request to return True
+ from askbot.utils import functions
+ functions.not_a_robot_request = lambda v: True
+
+ url = question.get_absolute_url()
+
+ self.client.login(method='force', user_id = self.u2.id)
+ self.client.get(url)
+ self.assert_have_badge('popular-question', recipient = self.u1)
+
+ self.client.login(method='force', user_id = self.u3.id)
+ self.client.get(url)
+ self.assert_have_badge('popular-question', recipient = self.u1, expected_count = 1)
+
+ question2 = self.post_question(user = self.u1)
+ question2.view_count = min_views - 1
+ question2.save()
+ self.client.login(method='force', user_id = self.u2.id)
+ self.client.get(question2.get_absolute_url())
+ self.assert_have_badge('popular-question', recipient = self.u1, expected_count = 2)
+
+ def test_student_badge(self):
+ question = self.post_question(user = self.u1)
+ self.u2.upvote(question)
+ self.assert_have_badge('student', recipient = self.u1)
+ self.u3.upvote(question)
+ self.assert_have_badge('student', recipient = self.u1, expected_count = 1)
+
+ question2 = self.post_question(user = self.u1)
+ self.u2.upvote(question)
+ self.assert_have_badge('student', recipient = self.u1, expected_count = 1)
+
+ def test_supporter_badge(self):
+ question = self.post_question(user = self.u1)
+ self.u2.upvote(question)
+ self.assert_have_badge('supporter', recipient = self.u2)
+
+ answer = self.post_answer(user = self.u1, question = question)
+ self.u3.upvote(answer)
+ self.assert_have_badge('supporter', recipient = self.u3)
+ self.u2.upvote(answer)
+ self.assert_have_badge('supporter', recipient = self.u2, expected_count = 1)
+
+ def test_critic_badge(self):
+ question = self.post_question(user = self.u1)
+ self.u2.downvote(question)
+ self.assert_have_badge('critic', recipient = self.u2)
+
+ answer = self.post_answer(user = self.u1, question = question)
+ self.u3.downvote(answer)
+ self.assert_have_badge('critic', recipient = self.u3)
+ self.u2.downvote(answer)
+ self.assert_have_badge('critic', recipient = self.u2, expected_count = 1)
+
+ def test_self_learner_badge(self):
+ question = self.post_question(user = self.u1)
+ answer = self.post_answer(user = self.u1, question = question)
+ min_votes = settings.SELF_LEARNER_BADGE_MIN_UPVOTES
+ answer.score = min_votes - 1
+ answer.save()
+ self.u2.upvote(answer)
+ self.assert_have_badge('self-learner', recipient = self.u1)
+
+ #copy-paste of the first question, except expect second badge
+ question = self.post_question(user = self.u1)
+ answer = self.post_answer(user = self.u1, question = question)
+ answer.score = min_votes - 1
+ answer.save()
+ self.u2.upvote(answer)
+ self.assert_have_badge('self-learner', recipient = self.u1, expected_count = 2)
diff --git a/askbot/views/readers.py b/askbot/views/readers.py
index 92228912..9301ba60 100644
--- a/askbot/views/readers.py
+++ b/askbot/views/readers.py
@@ -32,6 +32,7 @@ from askbot.utils.html import sanitize_html
from askbot.utils.diff import textDiff as htmldiff
from askbot.forms import AdvancedSearchForm, AnswerForm
from askbot import models
+from askbot.models.badges import award_badges_signal
from askbot import const
from askbot import auth
from askbot.utils import markup
@@ -481,7 +482,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
if 'question_view_times' not in request.session:
request.session['question_view_times'] = {}
- last_seen = request.session['question_view_times'].get(question.id,None)
+ last_seen = request.session['question_view_times'].get(question.id, None)
updated_when, updated_who = question.get_last_update_info()
if updated_who != request.user:
@@ -493,6 +494,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
request.session['question_view_times'][question.id] = \
datetime.datetime.now()
+
if update_view_count:
question.view_count += 1
question.save()
@@ -502,6 +504,14 @@ def question(request, id):#refactor - long subroutine. display question body, an
#get response notifications
request.user.visit_question(question)
+ #3) send award badges signal for any badges
+ #that are awarded for question views
+ award_badges_signal.send(None,
+ event = 'view_question',
+ actor = request.user,
+ context_object = question,
+ )
+
paginator_data = {
'is_paginated' : (objects_list.count > ANSWERS_PAGE_SIZE),
'pages': objects_list.num_pages,