summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-07-08 04:30:58 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-07-08 04:30:58 -0400
commitae662a630a91363f54173a9713de0db900831795 (patch)
treef486387c41debffddc1fd5c84af2e2fcdc27f25f
parent6561300712c2697fb5c560518cf158c850712585 (diff)
downloadaskbot-ae662a630a91363f54173a9713de0db900831795.tar.gz
askbot-ae662a630a91363f54173a9713de0db900831795.tar.bz2
askbot-ae662a630a91363f54173a9713de0db900831795.zip
changed to using just Tag for suggested and approved tags
-rw-r--r--askbot/migrations/0130_auto__add_field_tag_status.py (renamed from askbot/migrations/0130_auto__add_suggestedtag.py)52
-rw-r--r--askbot/models/__init__.py57
-rw-r--r--askbot/models/question.py30
-rw-r--r--askbot/models/tag.py189
-rw-r--r--askbot/skins/default/templates/moderate_tags.html2
-rw-r--r--askbot/views/commands.py3
-rw-r--r--askbot/views/meta.py7
7 files changed, 171 insertions, 169 deletions
diff --git a/askbot/migrations/0130_auto__add_suggestedtag.py b/askbot/migrations/0130_auto__add_field_tag_status.py
index d769c76d..f5557546 100644
--- a/askbot/migrations/0130_auto__add_suggestedtag.py
+++ b/askbot/migrations/0130_auto__add_field_tag_status.py
@@ -8,40 +8,25 @@ from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
- # Adding model 'SuggestedTag'
- db.create_table('askbot_suggestedtag', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
- ('used_count', self.gf('django.db.models.fields.PositiveIntegerField')(default=1)),
- ('thread_ids', self.gf('django.db.models.fields.TextField')()),
- ))
- db.send_create_signal('askbot', ['SuggestedTag'])
-
- # Adding M2M table for field threads on 'SuggestedTag'
- db.create_table('askbot_suggestedtag_threads', (
- ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('suggestedtag', models.ForeignKey(orm['askbot.suggestedtag'], null=False)),
- ('thread', models.ForeignKey(orm['askbot.thread'], null=False))
- ))
- db.create_unique('askbot_suggestedtag_threads', ['suggestedtag_id', 'thread_id'])
+ # Adding field 'Tag.status'
+ db.add_column(u'tag', 'status',
+ self.gf('django.db.models.fields.SmallIntegerField')(default=1),
+ keep_default=False)
- # Adding M2M table for field users on 'SuggestedTag'
- db.create_table('askbot_suggestedtag_users', (
+ # Adding M2M table for field suggested_by on 'Tag'
+ db.create_table(u'tag_suggested_by', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
- ('suggestedtag', models.ForeignKey(orm['askbot.suggestedtag'], null=False)),
+ ('tag', models.ForeignKey(orm['askbot.tag'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
- db.create_unique('askbot_suggestedtag_users', ['suggestedtag_id', 'user_id'])
+ db.create_unique(u'tag_suggested_by', ['tag_id', 'user_id'])
def backwards(self, orm):
- # Deleting model 'SuggestedTag'
- db.delete_table('askbot_suggestedtag')
+ # Deleting field 'Tag.status'
+ db.delete_column(u'tag', 'status')
- # Removing M2M table for field threads on 'SuggestedTag'
- db.delete_table('askbot_suggestedtag_threads')
-
- # Removing M2M table for field users on 'SuggestedTag'
- db.delete_table('askbot_suggestedtag_users')
+ # Removing M2M table for field suggested_by on 'Tag'
+ db.delete_table('tag_suggested_by')
models = {
'askbot.activity': {
@@ -236,15 +221,6 @@ class Migration(SchemaMigration):
'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
- 'askbot.suggestedtag': {
- 'Meta': {'ordering': "['-used_count', 'name']", 'object_name': 'SuggestedTag'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
- 'thread_ids': ('django.db.models.fields.TextField', [], {}),
- 'threads': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['askbot.Thread']", 'symmetrical': 'False'}),
- 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
- 'users': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
- },
'askbot.tag': {
'Meta': {'ordering': "('-used_count', 'name')", 'object_name': 'Tag', 'db_table': "u'tag'"},
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
@@ -253,6 +229,8 @@ class Migration(SchemaMigration):
'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_tags'", 'null': 'True', 'to': "orm['auth.User']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'suggested_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'suggested_tags'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'tag_wiki': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'described_tag'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
},
@@ -354,4 +332,4 @@ class Migration(SchemaMigration):
}
}
- complete_apps = ['askbot'] \ No newline at end of file
+ complete_apps = ['askbot']
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index 046f19a1..166bf0c8 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -29,7 +29,7 @@ from askbot.skins import utils as skin_utils
from askbot.mail import messages
from askbot.models.question import QuestionView, AnonymousQuestion
from askbot.models.question import FavoriteQuestion
-from askbot.models.tag import Tag, MarkedTag, SuggestedTag
+from askbot.models.tag import Tag, MarkedTag
from askbot.models.tag import get_group_names, get_groups
from askbot.models.user import EmailFeedSetting, ActivityAuditStatus, Activity
from askbot.models.user import GroupMembership, GroupProfile
@@ -39,7 +39,6 @@ from askbot.models import signals
from askbot.models.badges import award_badges_signal, get_badge, BadgeData
from askbot.models.repute import Award, Repute, Vote
from askbot import auth
-from askbot.utils import category_tree
from askbot.utils.decorators import auto_now_timestamp
from askbot.utils.slug import slugify
from askbot.utils.html import sanitize_html
@@ -344,58 +343,6 @@ def user_can_create_tags(self):
else:
return True
-def user_try_creating_tags(self, tag_names = None, thread = None):
- created_tags = list()
- if self.can_create_tags():
- suggested_tags = SuggestedTag.objects.filter(name__in = tag_names)
- suggested_tags_dict = dict([(tag.name, tag) for tag in suggested_tags])
- for name in tag_names:
- #todo: keep better track of who creates the tag
- suggested_tag = suggested_tags_dict.get(name, None)
- if suggested_tag:
- creator = suggested_tag.users.all()[0]
- else:
- creator = self
-
- new_tag = Tag.objects.create(
- name = name,
- created_by = creator,
- used_count = 1#wrong, but we are recounting downstream
- )
- created_tags.append(new_tag)
- #remove added tags from the suggested tag list
- suggested_tags.delete()
- elif tag_names:#notify admins by email about new tags
- #maybe remove tags to report based on categories
- #todo: maybe move this to tags_updated signal handler
- if askbot_settings.TAG_SOURCE == 'category-tree':
- category_names = category_tree.get_leaf_names()
- #remove category tree tags from creation list
- tag_names = tag_names - category_names
-
- #here we put tags on the moderation queue
- if len(tag_names) > 0:
- suggested_tags = SuggestedTag.objects.filter(name__in = tag_names)
-
- previously_suggested_tag_names = set()
- for tag in suggested_tags:
- tag.used_count += 1
- tag.threads.add(thread)
- tag.users.add(self)
- tag.save()
- previously_suggested_tag_names.add(tag.name)
-
- tag_names = set(tag_names) - previously_suggested_tag_names
-
- SuggestedTag.objects.create(
- tag_names = tag_names,
- user = self,
- thread = thread
- )
-
- return created_tags
-
-
def user_can_have_strong_url(self):
"""True if user's homepage url can be
followed by the search engine crawlers"""
@@ -2664,7 +2611,6 @@ User.add_to_class('set_admin_status', user_set_admin_status)
User.add_to_class('edit_group_membership', user_edit_group_membership)
User.add_to_class('is_group_member', user_is_group_member)
User.add_to_class('remove_admin_status', user_remove_admin_status)
-User.add_to_class('try_creating_tags', user_try_creating_tags)
User.add_to_class('is_moderator', user_is_moderator)
User.add_to_class('is_approved', user_is_approved)
User.add_to_class('is_watched', user_is_watched)
@@ -3424,7 +3370,6 @@ __all__ = [
'Vote',
'PostFlagReason',
'MarkedTag',
- 'SuggestedTag',
'BadgeData',
'Award',
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 2a5c557d..e4fb7e56 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -16,6 +16,7 @@ from askbot.conf import settings as askbot_settings
from askbot import mail
from askbot.mail import messages
from askbot.models.tag import Tag, get_groups, get_tags_by_names
+from askbot.models.tag import filter_accepted_tags, filter_suggested_tags
from askbot.models.tag import delete_tags, separate_unused_tags
from askbot.models.base import AnonymousContent, BaseQuerySetManager
from askbot.models.tag import Tag, get_groups
@@ -811,7 +812,7 @@ class Thread(models.Model):
*IMPORTANT*: self._question_post() has to
exist when update_tags() is called!
"""
- previous_tags = list(self.tags.all())
+ previous_tags = list(self.tags.filter(status = Tag.STATUS_ACCEPTED))
ordered_updated_tagnames = [t for t in tagnames.strip().split(' ')]
@@ -822,7 +823,9 @@ class Thread(models.Model):
#remove tags from the question's tags many2many relation
#used_count values are decremented on all tags
removed_tags = self.remove_tags_by_names(removed_tagnames)
+
#modified tags go on to recounting their use
+ #todo - this can actually be done asynchronously - not so important
modified_tags, unused_tags = separate_unused_tags(removed_tags)
delete_tags(unused_tags)#tags with used_count == 0 are deleted
@@ -831,7 +834,6 @@ class Thread(models.Model):
#add new tags to the relation
added_tagnames = updated_tagnames - previous_tagnames
- #todo: the part below is too long and needs to be refactored
if added_tagnames:
#find reused tags
reused_tags, new_tagnames = get_tags_by_names(added_tagnames)
@@ -839,17 +841,20 @@ class Thread(models.Model):
added_tags = list(reused_tags)
#tag moderation is in the call below
- created_tags = user.try_creating_tags(new_tagnames, thread = self)
+ created_tags = Tag.objects.create_in_bulk(
+ tag_names = new_tagnames, user = user
+ )
+
added_tags.extend(created_tags)
#todo: not nice that assignment of added_tags is way above
self.tags.add(*added_tags)
modified_tags.extend(added_tags)
-
else:
added_tags = Tag.objects.none()
#Save denormalized tag names on thread. Preserve order from user input.
- added_tagnames = set([tag.name for tag in added_tags])
+ accepted_added_tags = filter_accepted_tags(added_tags)
+ added_tagnames = set([tag.name for tag in accepted_added_tags])
final_tagnames = (previous_tagnames - removed_tagnames) | added_tagnames
ordered_final_tagnames = list()
for tagname in ordered_updated_tagnames:
@@ -859,6 +864,21 @@ class Thread(models.Model):
self.tagnames = ' '.join(ordered_final_tagnames)
self.save()#need to save here?
+ #todo: factor out - tell author about suggested tags
+ suggested_tags = filter_suggested_tags(added_tags)
+ if len(suggested_tags) > 0:
+ if len(suggested_tags) == 1:
+ msg = _(
+ 'Tag %s is new and will be submitted for the '
+ 'moderators approval'
+ ) % suggested_tags[0].name
+ else:
+ msg = _(
+ 'Tags %s are new and will be submitted for the '
+ 'moderators approval'
+ ) % ', '.join([tag.name for tag in suggested_tags])
+ user.message_set.create(message = msg)
+
####################################################################
self.update_summary_html() # regenerate question/thread summary html
####################################################################
diff --git a/askbot/models/tag.py b/askbot/models/tag.py
index 54c632e7..50edb801 100644
--- a/askbot/models/tag.py
+++ b/askbot/models/tag.py
@@ -5,6 +5,7 @@ from django.utils.translation import ugettext as _
from askbot.models.base import BaseQuerySetManager
from askbot import const
from askbot.conf import settings as askbot_settings
+from askbot.utils import category_tree
def delete_tags(tags):
"""deletes tags in the list"""
@@ -26,6 +27,26 @@ def get_tags_by_names(tag_names):
return tags, new_tag_names
+def filter_tags_by_status(tags, status = None):
+ """returns a list or a query set of tags which are accepted"""
+ if isinstance(tags, models.query.QuerySet):
+ return tags.filter(status = status)
+ else:
+ return [tag for tag in tags if tag.status == status]
+
+def filter_accepted_tags(tags):
+ return filter_tags_by_status(tags, status = Tag.STATUS_ACCEPTED)
+
+def filter_suggested_tags(tags):
+ return filter_tags_by_status(tags, status = Tag.STATUS_SUGGESTED)
+
+def is_preapproved_tag_name(tag_name):
+ """true if tag name is in the category tree
+ or any other container of preapproved tags"""
+ #get list of preapproved tags, to make exceptions for
+ if askbot_settings.TAG_SOURCE == 'category-tree':
+ return tag_name in category_tree.get_leaf_names()
+ return False
def separate_unused_tags(tags):
"""returns two lists::
@@ -144,6 +165,94 @@ class TagManager(BaseQuerySetManager):
def get_query_set(self):
return TagQuerySet(self.model)
+ def create(self, name = None, created_by = None, **kwargs):
+ """Creates a new tag"""
+ if created_by.can_create_tags() or is_preapproved_tag_name(name):
+ status = Tag.STATUS_ACCEPTED
+ else:
+ status = Tag.STATUS_SUGGESTED
+
+ kwargs['created_by'] = created_by
+ kwargs['name'] = name
+ kwargs['status'] = status
+
+ return super(TagManager, self).create(**kwargs)
+
+ def create_suggested_tag(self, tag_names = None, user = None):
+ """This function is not used, and will probably need
+ to be retired. In the previous version we were sending
+ email to admins when the new tags were created,
+ now we have a separate page where new tags are listed.
+ """
+ #todo: stuff below will probably go after
+ #tag moderation actions are implemented
+ from askbot import mail
+ from askbot.mail import messages
+ body_text = messages.notify_admins_about_new_tags(
+ tags = tag_names,
+ user = user,
+ thread = self
+ )
+ site_name = askbot_settings.APP_SHORT_NAME
+ subject_line = _('New tags added to %s') % site_name
+ mail.mail_moderators(
+ subject_line,
+ body_text,
+ headers = {'Reply-To': user.email}
+ )
+
+ msg = _(
+ 'Tags %s are new and will be submitted for the '
+ 'moderators approval'
+ ) % ', '.join(tag_names)
+ user.message_set.create(message = msg)
+
+ def create_in_bulk(self, tag_names = None, user = None):
+ """creates tags by names. If user can create tags,
+ then they are set status ``STATUS_ACCEPTED``,
+ otherwise the status will be set to ``STATUS_SUGGESTED``.
+
+ One exception: if suggested tag is in the category tree
+ and source of tags is category tree - then status of newly
+ created tag is ``STATUS_ACCEPTED``
+ """
+
+ #load suggested tags
+ pre_suggested_tags = self.filter(
+ name__in = tag_names, status = Tag.STATUS_SUGGESTED
+ )
+
+ #deal with suggested tags
+ if user.can_create_tags():
+ #turn previously suggested tags into accepted
+ pre_suggested_tags.update(status = Tag.STATUS_ACCEPTED)
+ else:
+ #increment use count and add user to "suggested_by"
+ for tag in pre_suggested_tags:
+ tag.times_used += 1
+ tag.suggested_by.add(user)
+ tag.save()
+
+ created_tags = list()
+ pre_suggested_tag_names = list()
+ for tag in pre_suggested_tags:
+ pre_suggested_tag_names.append(tag.name)
+ created_tags.append(tag)
+
+ for tag_name in set(tag_names) - set(pre_suggested_tag_names):
+
+ #status for the new tags is automatically set within the create()
+ new_tag = Tag.objects.create(name = tag_name, created_by = user)
+ created_tags.append(new_tag)
+
+ if new_tag.status == Tag.STATUS_SUGGESTED:
+ new_tag.suggested_by.add(user)
+
+ #todo: here we have a chance to send a signal and notify
+ #whoever wants about the new tag creation
+
+ return created_tags
+
class GroupTagQuerySet(TagQuerySet):
"""Custom query set for the group"""
@@ -192,8 +301,20 @@ class GroupTagManager(BaseQuerySetManager):
return tag
class Tag(models.Model):
- name = models.CharField(max_length=255, unique=True)
- created_by = models.ForeignKey(User, related_name='created_tags')
+ #a couple of status constants
+ STATUS_SUGGESTED = 0
+ STATUS_ACCEPTED = 1
+
+ name = models.CharField(max_length=255, unique=True)
+ created_by = models.ForeignKey(User, related_name='created_tags')
+
+ suggested_by = models.ManyToManyField(
+ User, related_name='suggested_tags',
+ help_text = 'Works only for suggested tags for tag moderation'
+ )
+
+ status = models.SmallIntegerField(default = STATUS_ACCEPTED)
+
# Denormalised data
used_count = models.PositiveIntegerField(default=0)
@@ -237,67 +358,3 @@ def get_groups():
def get_group_names():
#todo: cache me
return get_groups().values_list('name', flat = True)
-
-class SuggestedTagManager(models.Manager):
- def create(self, tag_names = None, user = None, thread = None):
- """creates ``SuggestedTag`` records and saves them
- in the database"""
- suggested_tags = list()
- for tag_name in tag_names:
- #create new record
- suggested_tag = SuggestedTag(name = tag_name)
- suggested_tag.save()
- #add user and thread
- suggested_tag.users.add(user)
- suggested_tag.threads.add(thread)
- #add to the list that is to be returned
- suggested_tags.append(suggested_tag)
-
- #todo: stuff below will probably go after
- #tag moderation actions are implemented
- from askbot import mail
- from askbot.mail import messages
- body_text = messages.notify_admins_about_new_tags(
- tags = tag_names,
- user = user,
- thread = thread
- )
- site_name = askbot_settings.APP_SHORT_NAME
- subject_line = _('New tags added to %s') % site_name
- mail.mail_moderators(
- subject_line,
- body_text,
- headers = {'Reply-To': user.email}
- )
-
- msg = _(
- 'Tags %s are new and will be submitted for the '
- 'moderators approval'
- ) % ', '.join(tag_names)
- user.message_set.create(message = msg)
-
- return suggested_tags
-
-class SuggestedTag(models.Model):
- """Suggested tag knows about who suggested it
- and in what thread"""
- name = models.CharField(
- max_length=255, unique=True, help_text = 'Name for the proposed tag'
- )
- used_count = models.PositiveIntegerField(default = 1)
- #todo: instead these can be associated with revisions
- #but the problem is that there would be too many joins
- #to pull out threads
- #if we used revisions, then we would not need to have
- #a separate "user" field
- threads = models.ManyToManyField('Thread')
- users = models.ManyToManyField(User)
- thread_ids = models.TextField(
- help_text = 'comma-separated list of thread ids'
- )
-
- objects = SuggestedTagManager()
-
- class Meta:
- app_label = 'askbot'
- ordering = ['-used_count', 'name']
diff --git a/askbot/skins/default/templates/moderate_tags.html b/askbot/skins/default/templates/moderate_tags.html
index fbf43d5e..5cd95a87 100644
--- a/askbot/skins/default/templates/moderate_tags.html
+++ b/askbot/skins/default/templates/moderate_tags.html
@@ -22,7 +22,7 @@
<span class="tag-number">&#215;{{ tag.used_count }}</span>
</td>
<td class="users-col">
- {% for user in tag.users.all() %}
+ {% for user in tag.suggested_by.all() %}
<p>{{ user.get_profile_link() }}</p>
{% endfor %}
</td>
diff --git a/askbot/views/commands.py b/askbot/views/commands.py
index 0f59c122..19684179 100644
--- a/askbot/views/commands.py
+++ b/askbot/views/commands.py
@@ -493,7 +493,8 @@ def get_tag_list(request):
function
"""
tag_names = models.Tag.objects.filter(
- deleted = False
+ deleted = False,
+ status = models.Tag.STATUS_ACCEPTED
).values_list(
'name', flat = True
)
diff --git a/askbot/views/meta.py b/askbot/views/meta.py
index fd7cb3e2..418c9cd2 100644
--- a/askbot/views/meta.py
+++ b/askbot/views/meta.py
@@ -17,7 +17,7 @@ from askbot import skins
from askbot.conf import settings as askbot_settings
from askbot.forms import FeedbackForm
from askbot.mail import mail_moderators
-from askbot.models import BadgeData, Award, User, SuggestedTag
+from askbot.models import BadgeData, Award, User, Tag
from askbot.models import badges as badge_data
from askbot.skins.loaders import get_template, render_into_skin, render_text_into_skin
from askbot.utils.decorators import admins_only
@@ -164,9 +164,10 @@ def moderate_tags(request):
or cancel the moderation reuest."""
if askbot_settings.ENABLE_TAG_MODERATION == False:
raise Http404
- tags = SuggestedTag.objects.all()
+ tags = Tag.objects.filter(status = Tag.STATUS_SUGGESTED)
+ tags = tags.order_by('-used_count', 'name')
#paginate moderated tags
- paginator = Paginator(SuggestedTag.objects.all(), 20)
+ paginator = Paginator(tags, 20)
page_no = request.GET.get('page', '1')