summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askbot/const/__init__.py16
-rw-r--r--askbot/importers/stackexchange/management/commands/load_stackexchange.py38
-rw-r--r--askbot/models/__init__.py13
-rw-r--r--askbot/models/badges.py80
-rw-r--r--askbot/models/repute.py34
-rw-r--r--askbot/views/meta.py8
-rw-r--r--askbot/views/readers.py47
-rw-r--r--askbot/views/users.py2
8 files changed, 158 insertions, 80 deletions
diff --git a/askbot/const/__init__.py b/askbot/const/__init__.py
index eb1ba23d..e5214a05 100644
--- a/askbot/const/__init__.py
+++ b/askbot/const/__init__.py
@@ -233,5 +233,21 @@ DEPENDENCY_URLS = {
PASSWORD_MIN_LENGTH = 8
+GOLD_BADGE = 1
+SILVER_BADGE = 2
+BRONZE_BADGE = 3
+BADGE_TYPE_CHOICES = (
+ (GOLD_BADGE, _('gold')),
+ (SILVER_BADGE, _('silver')),
+ (BRONZE_BADGE, _('bronze')),
+)
+BADGE_CSS_CLASSES = {
+ GOLD_BADGE: 'badge1',
+ SILVER_BADGE: 'badge2',
+ BRONZE_BADGE: 'badge3',
+}
+BADGE_DISPLAY_SYMBOL = '●'
+
+
#an exception import * because that file has only strings
from askbot.const.message_keys import *
diff --git a/askbot/importers/stackexchange/management/commands/load_stackexchange.py b/askbot/importers/stackexchange/management/commands/load_stackexchange.py
index fb3b520f..2aef092f 100644
--- a/askbot/importers/stackexchange/management/commands/load_stackexchange.py
+++ b/askbot/importers/stackexchange/management/commands/load_stackexchange.py
@@ -265,7 +265,7 @@ class X(object):#
@classmethod
def get_badge_name(cls, name):
- return cls.badge_exceptions.get(name, name)
+ return slugify(cls.badge_exceptions.get(name, name).lower())
class Command(BaseCommand):
help = 'Loads StackExchange data from unzipped directory of XML files into the ASKBOT database'
@@ -618,23 +618,20 @@ class Command(BaseCommand):
user = USER[se_c.user.id]
)
- def _install_missing_badges(self):
+ def _collect_missing_badges(self):
self._missing_badges = {}
for se_b in se.Badge.objects.all():
name = X.get_badge_name(se_b.name)
try:
- askbot.Badge.objects.get(name=name)
- except:
+ #todo: query badge from askbot.models.badges
+ #using case-insensitive name matching
+ askbot.badges.get_badge(name=name)
+ except KeyError:
+ #todo: if absent - print error message
+ #and drop it
self._missing_badges[name] = 0
if len(se_b.description) > 300:
print 'Warning truncated description for badge %d' % se_b.id
- askbot.Badge.objects.create(
- name = name,
- slug = slugify(name),
- description = se_b.description,
- multiple = (not se_b.single),
- type = se_b.class_type
- )
def _award_badges(self):
#note: SE does not keep information on
@@ -644,7 +641,7 @@ class Command(BaseCommand):
continue #skip community user
u = USER[se_a.user.id]
badge_name = X.get_badge_name(se_a.badge.name)
- b = askbot.Badge.objects.get(name=badge_name)
+ b = askbot.badges.get_badge(name=badge_name)
if b.multiple == False:
if b.award_badge.count() > 0:
continue
@@ -652,29 +649,28 @@ class Command(BaseCommand):
#todo: but askbot requires related content object
askbot.Award.objects.create(
user=u,
- badge=b,
+ badge=b.get_stored_data(),
awarded_at=se_a.date,
content_object=u,
)
if b.name in self._missing_badges:
self._missing_badges[b.name] += 1
- def _cleanup_badges(self):
+ def _report_missing_badges(self):
d = self._missing_badges
unused = [name for name in d.keys() if d[name] == 0]
- askbot.Badge.objects.filter(name__in=unused).delete()
- installed = [name for name in d.keys() if d[name] > 0]
- print 'Warning - following unsupported badges were installed:'
- print ', '.join(installed)
+ dropped = [name for name in d.keys() if d[name] > 0]
+ print 'Warning - following unsupported badges were dropped:'
+ print ', '.join(dropped)
def transfer_badges(self):
#note: badge level is neglected
#1) install missing badges
- self._install_missing_badges()
+ self._collect_missing_badges()
#2) award badges
self._award_badges()
- #3) delete unused newly installed badges
- self._cleanup_badges()
+ #3) report missing badges
+ self._report_missing_badges()
pass
def transfer_question_view_counts(self):
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index d280bba7..426ff582 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -27,7 +27,8 @@ from askbot.models.meta import Vote, Comment
from askbot.models.user import EmailFeedSetting, ActivityAuditStatus, Activity
from askbot.models import signals
#from user import AuthKeyUserAssociation
-from askbot.models.repute import Badge, Award, Repute
+from askbot.models.repute import BadgeData, Award, Repute
+from askbot.models import badges
from askbot import auth
from askbot.utils.decorators import auto_now_timestamp
from askbot.utils.slug import slugify
@@ -1755,11 +1756,11 @@ def record_award_event(instance, created, **kwargs):
instance.badge.awarded_count += 1
instance.badge.save()
- if instance.badge.type == Badge.GOLD:
+ if instance.badge.type == badges.GOLD:
instance.user.gold += 1
- if instance.badge.type == Badge.SILVER:
+ if instance.badge.type == badges.SILVER:
instance.user.silver += 1
- if instance.badge.type == Badge.BRONZE:
+ if instance.badge.type == badges.BRONZE:
instance.user.bronze += 1
instance.user.save()
@@ -2011,7 +2012,7 @@ Comment = Comment
Vote = Vote
MarkedTag = MarkedTag
-Badge = Badge
+BadgeData = BadgeData
Award = Award
Repute = Repute
@@ -2038,7 +2039,7 @@ __all__ = [
'Vote',
'MarkedTag',
- 'Badge',
+ 'BadgeData',
'Award',
'Repute',
diff --git a/askbot/models/badges.py b/askbot/models/badges.py
new file mode 100644
index 00000000..ecf05710
--- /dev/null
+++ b/askbot/models/badges.py
@@ -0,0 +1,80 @@
+"""badges data that is not stored in the database.
+This data is static, so there is no point storing it in the db.
+
+However, the database does have model BadgeData, that contains
+additional mutable data pertaining to the badges - denormalized award counts
+and lists of recipients
+"""
+from django.template.defaultfilters import slugify
+from django.utils.translation import gettext as _
+from askbot.models import BadgeData
+from askbot import const
+
+class Badge(object):
+ """base class for the badges
+ """
+ def __init__(self,
+ name = '',
+ level = None,
+ description = None,
+ multiple = False):
+
+ self._name = name
+ self.level = level
+ self._description = description
+ self.multiple = multiple
+
+ def get_stored_data(self):
+ data, created = BadgeData.objects.get_or_create(name = self.name)
+ return data
+
+ @property
+ def awarded_count(self):
+ return self.get_stored_data().awarded_count
+
+ @property
+ def awarded_to(self):
+ return self.get_stored_data().awarded_to
+
+ def award(self, recipient):
+ """do award, the recipient was proven to deserve"""
+ pass
+
+ def consider_award(self, actor = None, context_object = None):
+ """This method should be implemented in subclasses
+ actor - user who committed some action, context_object -
+ the object related to the award situation, e.g. an
+ answer that is being upvoted
+
+ the method should internally check who might be awarded and
+ whether the situation is appropriate
+ """
+ raise NotImplementedError()
+
+class Disciplined(Badge):
+ def __init__(self):
+ super(Disciplined, self).__init__(
+ key = 'disciplined',
+ name = _('Disciplined'),
+ description = _('Deleted own post with score of 3 or higher'),
+ level = const.BRONZE_BADGE,
+ multiple = True
+ )
+
+ def consider_award(self, actor = None, context_object = None):
+ if context_object.author != actor:
+ return
+ if context_object.score > 2:
+ self.award(actor)
+
+
+BADGES = {
+ 'disciplined': Disciplined
+}
+
+def get_badge(name = None):
+ """Get badge object by name, if none mathes the name
+ raise KeyError
+ """
+ key = slugify(name)
+ return BADGES[key]()
diff --git a/askbot/models/repute.py b/askbot/models/repute.py
index d8712636..a933801a 100644
--- a/askbot/models/repute.py
+++ b/askbot/models/repute.py
@@ -1,37 +1,19 @@
+import datetime
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import ugettext as _
-import datetime
-from askbot import const
from django.core.urlresolvers import reverse
+from askbot import const
+from askbot models import badges
-class Badge(models.Model):
+class BadgeData(models.Model):
"""Awarded for notable actions performed on the site by Users."""
- GOLD = 1
- SILVER = 2
- BRONZE = 3
- TYPE_CHOICES = (
- (GOLD, _('gold')),
- (SILVER, _('silver')),
- (BRONZE, _('bronze')),
- )
- CSS_CLASSES = {
- GOLD: 'badge1',
- SILVER: 'badge2',
- BRONZE: 'badge3',
- }
- DISPLAY_SYMBOL = '●'
-
- name = models.CharField(max_length=50)
- type = models.SmallIntegerField(choices=TYPE_CHOICES)
- slug = models.SlugField(max_length=50, blank=True)
- description = models.CharField(max_length=300)
- multiple = models.BooleanField(default=False)
+
+ name = models.CharField(max_length=50, unique=True)
# Denormalised data
awarded_count = models.PositiveIntegerField(default=0)
-
awarded_to = models.ManyToManyField(User, through='Award', related_name='badges')
class Meta:
@@ -46,7 +28,7 @@ class Badge(models.Model):
def save(self, **kwargs):
if not self.slug:
self.slug = self.name#slugify(self.name)
- super(Badge, self).save(**kwargs)
+ super(BadgeData, self).save(**kwargs)
def get_absolute_url(self):
return '%s%s/' % (reverse('badge', args=[self.id]), self.slug)
@@ -67,7 +49,7 @@ class AwardManager(models.Manager):
class Award(models.Model):
"""The awarding of a Badge to a User."""
user = models.ForeignKey(User, related_name='award_user')
- badge = models.ForeignKey('Badge', related_name='award_badge')
+ badge = models.ForeignKey(BadgeData, related_name='award_badge')
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
diff --git a/askbot/views/meta.py b/askbot/views/meta.py
index 0884d3e8..9da75f91 100644
--- a/askbot/views/meta.py
+++ b/askbot/views/meta.py
@@ -13,7 +13,7 @@ from django.views import static
from askbot.forms import FeedbackForm
from askbot.utils.forms import get_next_url
from askbot.utils.mail import mail_moderators
-from askbot.models import Badge, Award
+from askbot.models import BadgeData, Award
from askbot.skins.loaders import ENV
from askbot import skins
import askbot
@@ -90,7 +90,8 @@ def logout(request):#refactor/change behavior?
return HttpResponse(template.render(context))
def badges(request):#user status/reputation system
- badges = Badge.objects.all().order_by('name')
+ #todo: supplement database data with the stuff from badges.py
+ badges = BadgeData.objects.all().order_by('name')
my_badges = []
if request.user.is_authenticated():
my_badges = Award.objects.filter(user=request.user).values('badge_id')
@@ -108,7 +109,8 @@ def badges(request):#user status/reputation system
return HttpResponse(template.render(context))
def badge(request, id):
- badge = get_object_or_404(Badge, id=id)
+ #todo: supplement database data with the stuff from badges.py
+ badge = get_object_or_404(BadgeData, id=id)
awards = Award.objects.extra(
select={'id': 'auth_user.id',
'name': 'auth_user.username',
diff --git a/askbot/views/readers.py b/askbot/views/readers.py
index 42ddb63a..1f670c2c 100644
--- a/askbot/views/readers.py
+++ b/askbot/views/readers.py
@@ -31,12 +31,13 @@ from askbot.utils.html import sanitize_html
#from lxml.html.diff import htmldiff
from askbot.utils.diff import textDiff as htmldiff
from askbot.forms import AdvancedSearchForm, AnswerForm
-from askbot.models import *
+from askbot import models
+from askbot.models import badges
from askbot import const
from askbot import auth
from askbot.utils import markup
from askbot.utils.forms import get_next_url
-from askbot.utils.functions import not_a_robot_request
+from askbot.utils import functions
from askbot.utils.decorators import anonymous_forbidden, ajax_only, get_only
from askbot.search.state_manager import SearchState
from askbot.templatetags import extra_tags
@@ -61,7 +62,7 @@ def _get_tags_cache_json():#service routine used by views requiring tag list in
"""returns list of all tags in json format
no caching yet, actually
"""
- tags = Tag.objects.filter(deleted=False).all()
+ tags = models.Tag.objects.filter(deleted=False).all()
tags_list = []
for tag in tags:
dic = {'n': tag.name, 'c': tag.used_count}
@@ -124,7 +125,7 @@ def questions(request):
#request.session.modified = True
#todo: have this call implemented for sphinx, mysql and pgsql
- (qs, meta_data, related_tags) = Question.objects.run_advanced_search(
+ (qs, meta_data, related_tags) = models.Question.objects.run_advanced_search(
request_user = request.user,
search_state = search_state,
)
@@ -136,7 +137,7 @@ def questions(request):
page = paginator.page(search_state.page)
- contributors = Question.objects.get_question_and_answer_contributors(page.object_list)
+ contributors = models.Question.objects.get_question_and_answer_contributors(page.object_list)
paginator_context = {
'is_paginated' : (paginator.count > search_state.page_size),
@@ -181,7 +182,7 @@ def questions(request):
'faces': list()
}
- badge_levels = dict(Badge.TYPE_CHOICES)
+ badge_levels = dict(badges.TYPE_CHOICES)
def pluralize_badge_count(count, level):
return ungettext(
'%(badge_count)d %(badge_level)s badge',
@@ -192,9 +193,9 @@ def questions(request):
'badge_level': badge_levels[level]
}
- gold_badge_css_class = Badge.CSS_CLASSES[Badge.GOLD],
- silver_badge_css_class = Badge.CSS_CLASSES[Badge.SILVER],
- bronze_badge_css_class = Badge.CSS_CLASSES[Badge.BRONZE],
+ gold_badge_css_class = badges.CSS_CLASSES[badges.GOLD],
+ silver_badge_css_class = badges.CSS_CLASSES[badges.SILVER],
+ bronze_badge_css_class = badges.CSS_CLASSES[badges.BRONZE],
for tag in related_tags:
tag_data = {
@@ -242,30 +243,30 @@ def questions(request):
'views_class': views_class,
'views_word': ungettext('view', 'views', question.view_count),
'timestamp': unicode(timestamp),
- 'timesince': extra_tags.diff_date(timestamp),
+ 'timesince': functions.diff_date(timestamp),
'u_id': author.id,
'u_name': author.username,
'u_rep': author.reputation,
'u_gold': author.gold,
'u_gold_title': pluralize_badge_count(
author.gold,
- Badge.GOLD
+ badges.GOLD
),
- 'u_gold_badge_symbol': Badge.DISPLAY_SYMBOL,
+ 'u_gold_badge_symbol': badges.DISPLAY_SYMBOL,
'u_gold_css_class': gold_badge_css_class,
'u_silver': author.silver,
'u_silver_title': pluralize_badge_count(
author.silver,
- Badge.SILVER
+ badges.SILVER
),
- 'u_silver_badge_symbol': Badge.DISPLAY_SYMBOL,
+ 'u_silver_badge_symbol': badges.DISPLAY_SYMBOL,
'u_silver_css_class': silver_badge_css_class,
'u_bronze': author.bronze,
'u_bronze_title': pluralize_badge_count(
author.bronze,
- Badge.BRONZE
+ badges.BRONZE
),
- 'u_bronze_badge_symbol': Badge.DISPLAY_SYMBOL,
+ 'u_bronze_badge_symbol': badges.DISPLAY_SYMBOL,
'u_bronze_css_class': bronze_badge_css_class,
}
ajax_data['questions'].append(question_data)
@@ -352,12 +353,12 @@ def tags(request):#view showing a listing of available tags - plain list
if request.method == "GET":
stag = request.GET.get("q", "").strip()
if stag != '':
- objects_list = Paginator(Tag.objects.filter(deleted=False).exclude(used_count=0).extra(where=['name like %s'], params=['%' + stag + '%']), DEFAULT_PAGE_SIZE)
+ objects_list = Paginator(models.Tag.objects.filter(deleted=False).exclude(used_count=0).extra(where=['name like %s'], params=['%' + stag + '%']), DEFAULT_PAGE_SIZE)
else:
if sortby == "name":
- objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("name"), DEFAULT_PAGE_SIZE)
+ objects_list = Paginator(models.Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("name"), DEFAULT_PAGE_SIZE)
else:
- objects_list = Paginator(Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-used_count"), DEFAULT_PAGE_SIZE)
+ objects_list = Paginator(models.Tag.objects.all().filter(deleted=False).exclude(used_count=0).order_by("-used_count"), DEFAULT_PAGE_SIZE)
try:
tags = objects_list.page(page)
@@ -417,7 +418,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
logging.debug('answer_sort_method=' + unicode(answer_sort_method))
- question = get_object_or_404(Question, id=id)
+ question = get_object_or_404(models.Question, id=id)
try:
assert(request.path == question.get_absolute_url())
except AssertionError:
@@ -473,7 +474,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
objects_list = Paginator(filtered_answers, ANSWERS_PAGE_SIZE)
page_objects = objects_list.page(page)
- if not_a_robot_request(request):
+ if functions.not_a_robot_request(request):
#todo: split this out into a subroutine
#todo: merge view counts per user and per session
#1) view count per session
@@ -539,7 +540,7 @@ def question(request, id):#refactor - long subroutine. display question body, an
def revisions(request, id, object_name=None):
assert(object_name in ('Question', 'Answer'))
- post = get_object_or_404(get_model(object_name), id=id)
+ post = get_object_or_404(models.get_model(object_name), id=id)
revisions = list(post.revisions.all())
revisions.reverse()
for i, revision in enumerate(revisions):
@@ -568,6 +569,6 @@ def get_comment(request):
and request must be ajax
"""
id = int(request.GET['id'])
- comment = Comment.objects.get(id = id)
+ comment = models.Comment.objects.get(id = id)
request.user.assert_can_edit_comment(comment)
return {'text': comment.comment}
diff --git a/askbot/views/users.py b/askbot/views/users.py
index 466b2697..51103f91 100644
--- a/askbot/views/users.py
+++ b/askbot/views/users.py
@@ -445,7 +445,7 @@ def user_recent(request, user):
self.time = time
self.type = get_type_name(type)
self.type_id = type
- self.badge = get_object_or_404(models.Badge, id=id)
+ self.badge = get_object_or_404(models.BadgeData, id=id)
activities = []
# ask questions