summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-11-04 14:51:47 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-11-04 14:51:47 -0400
commit01ff8ebe9a55e019e2b8f02bc2336a7c3d3c8d60 (patch)
tree34c32657c32e92032de4e78c41bdad8f622761eb
parent2ed72c554cbada913b9c344d2224eb0d33502f5f (diff)
downloadaskbot-01ff8ebe9a55e019e2b8f02bc2336a7c3d3c8d60.tar.gz
askbot-01ff8ebe9a55e019e2b8f02bc2336a7c3d3c8d60.tar.bz2
askbot-01ff8ebe9a55e019e2b8f02bc2336a7c3d3c8d60.zip
changed Activity model to prepare for some feature changes, bug fix in post flagging
-rw-r--r--askbot/auth.py11
-rw-r--r--askbot/migrations/0023_auto__add_activityauditstatus.py319
-rw-r--r--askbot/migrations/0024_add_recipients_m2m_to_activity_and_denorm_question_on_activity.py375
-rw-r--r--askbot/models/__init__.py91
-rw-r--r--askbot/models/answer.py12
-rw-r--r--askbot/models/meta.py2
-rw-r--r--askbot/models/question.py10
-rw-r--r--askbot/models/user.py43
-rw-r--r--askbot/tests/on_screen_notification_tests.py336
-rw-r--r--askbot/views/users.py4
10 files changed, 900 insertions, 303 deletions
diff --git a/askbot/auth.py b/askbot/auth.py
index e0f314c1..4a1602e5 100644
--- a/askbot/auth.py
+++ b/askbot/auth.py
@@ -69,6 +69,12 @@ def onFlaggedItem(item, post, user, timestamp=None):
)
reputation.save()
+ signals.flag_offensive.send(
+ sender=post.__class__,
+ instance=post,
+ mark_by=user
+ )
+
#todo: These should be updated to work on same revisions.
if post.offensive_flag_count == askbot_settings.MIN_FLAGS_TO_HIDE_POST:
post.author.reputation = \
@@ -114,11 +120,6 @@ def onFlaggedItem(item, post, user, timestamp=None):
#post.deleted_at = timestamp
#post.deleted_by = Admin
post.save()
- signals.flag_offensive.send(
- sender=post.__class__,
- instance=post,
- mark_by=user
- )
@transaction.commit_on_success
def onAnswerAccept(answer, user, timestamp=None):
diff --git a/askbot/migrations/0023_auto__add_activityauditstatus.py b/askbot/migrations/0023_auto__add_activityauditstatus.py
new file mode 100644
index 00000000..74f849c8
--- /dev/null
+++ b/askbot/migrations/0023_auto__add_activityauditstatus.py
@@ -0,0 +1,319 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'ActivityAuditStatus'
+ db.create_table('askbot_activityauditstatus', (
+ ('status', self.gf('django.db.models.fields.SmallIntegerField')(default=0)),
+ ('activity', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Activity'])),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+ ))
+ db.send_create_signal('askbot', ['ActivityAuditStatus'])
+ #denorm field to make User.visit_question call quicker
+ db.add_column(u'activity', 'question', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['askbot.Question'], null=True), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'ActivityAuditStatus'
+ db.delete_column(u'activity', 'question')
+ db.delete_table('askbot_activityauditstatus')
+
+
+ models = {
+ 'askbot.activity': {
+ 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"},
+ 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']", 'null':'True'}),
+ 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.activityauditstatus': {
+ 'Meta': {'unique_together': "(('user', 'activity'),)", 'object_name': 'ActivityAuditStatus'},
+ 'activity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Activity']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.anonymousanswer': {
+ 'Meta': {'object_name': 'AnonymousAnswer'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_answers'", 'to': "orm['askbot.Question']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'})
+ },
+ 'askbot.anonymousquestion': {
+ 'Meta': {'object_name': 'AnonymousQuestion'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'})
+ },
+ 'askbot.answer': {
+ 'Meta': {'object_name': 'Answer', 'db_table': "u'answer'"},
+ 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['askbot.Question']"}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.answerrevision': {
+ 'Meta': {'object_name': 'AnswerRevision', 'db_table': "u'answer_revision'"},
+ 'answer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Answer']"}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answerrevisions'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'revised_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.Badge']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badge': {
+ 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "'Award'", 'to': "orm['auth.User']"}),
+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}),
+ 'type': ('django.db.models.fields.SmallIntegerField', [], {})
+ },
+ 'askbot.comment': {
+ 'Meta': {'object_name': 'Comment', 'db_table': "u'comment'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'html': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '2048'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.flaggeditem': {
+ 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'FlaggedItem', 'db_table': "u'flagged_item'"},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flaggeditems'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.question': {
+ 'Meta': {'object_name': 'Question', 'db_table': "u'question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'answer_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questions'", 'to': "orm['auth.User']"}),
+ 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'closed_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'symmetrical': 'False', 'through': "'FavoriteQuestion'", 'to': "orm['auth.User']"}),
+ 'favourite_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'followed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followed_questions'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'last_active_in_questions'", 'to': "orm['auth.User']"}),
+ 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'questions'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.questionrevision': {
+ 'Meta': {'object_name': 'QuestionRevision', 'db_table': "u'question_revision'"},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questionrevisions'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Question']"}),
+ 'revised_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Question']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ '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'}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}),
+ 'vote': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'tag_filter_setting': ('django.db.models.fields.CharField', [], {'default': "'ignored'", 'max_length': '16'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/migrations/0024_add_recipients_m2m_to_activity_and_denorm_question_on_activity.py b/askbot/migrations/0024_add_recipients_m2m_to_activity_and_denorm_question_on_activity.py
new file mode 100644
index 00000000..42dfcb52
--- /dev/null
+++ b/askbot/migrations/0024_add_recipients_m2m_to_activity_and_denorm_question_on_activity.py
@@ -0,0 +1,375 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+from askbot import const
+
+#some of activities are not related to question, so they are not processed here
+APPROPRIATE_ACTIVITIES = (
+ const.TYPE_ACTIVITY_ASK_QUESTION,
+ const.TYPE_ACTIVITY_UPDATE_QUESTION,
+ const.TYPE_ACTIVITY_DELETE_QUESTION,
+ const.TYPE_ACTIVITY_UPDATE_TAGS,
+ const.TYPE_ACTIVITY_ANSWER,
+ const.TYPE_ACTIVITY_UPDATE_ANSWER,
+ const.TYPE_ACTIVITY_DELETE_ANSWER,
+ const.TYPE_ACTIVITY_MARK_ANSWER,
+ const.TYPE_ACTIVITY_COMMENT_QUESTION,
+ const.TYPE_ACTIVITY_COMMENT_ANSWER,
+ const.TYPE_ACTIVITY_MARK_OFFENSIVE,
+ const.TYPE_ACTIVITY_MENTION,
+ const.TYPE_ACTIVITY_FAVORITE,
+)
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ self._orm_for_askbot = orm
+ #1) fill in audit status values
+ activities = orm.Activity.objects.exclude(receiving_users=None)
+
+ have_problems = False
+ errors = set()
+ bad_ids = list()
+ for act in activities:
+ users = act.receiving_users.all()
+ for user in users:
+ orm.ActivityAuditStatus(user = user, activity = act).save()
+
+ #2) save question value into the activity
+ for act in orm.Activity.objects.all():
+ if act.activity_type in APPROPRIATE_ACTIVITIES:
+ try:
+ act.question = self.get_question_from_generic_relation(act)
+ act.save()
+ except Exception, e:
+ have_problems = True
+ errors.add(unicode(e))
+ bad_ids.append(str(act.id))
+
+ if have_problems:
+ print 'Migration is now complete, but there were some errors:'
+ print '\n'.join(errors)
+ print 'problematic activity objects are: ' + ','.join(bad_ids)
+
+ def get_orm(self):
+ return self._orm_for_askbot
+
+ def get_question_from_generic_relation(self, parent):
+ orm = self.get_orm()
+ model = parent.content_type.model
+ id = parent.object_id
+ #print 'model is ' + model
+ if model == 'question':
+ return orm.Question.objects.get(id=id)
+ elif model == 'answer':
+ return orm.Answer.objects.get(id=id).question
+ elif model == 'favoritequestion':
+ try:
+ return orm.FavoriteQuestion.objects.get(id=id).question
+ except orm.FavoriteQuestion.DoesNotExist:
+ #ignore this issue for now
+ return None
+ elif model == 'comment':
+ comment = orm.Comment.objects.get(id=id)
+ return self.get_question_from_generic_relation(comment)
+ else:
+ print 'dropped migration of activity in %s' % model
+ return None
+
+ def backwards(self, orm):
+ orm.ActivityAuditStatus.objects.all().delete()
+
+
+ models = {
+ 'askbot.activity': {
+ 'Meta': {'object_name': 'Activity', 'db_table': "u'activity'"},
+ 'active_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'activity_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_auditted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']", 'null':'True'}),
+ 'receiving_users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'received_activity'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'incoming_activity'", 'symmetrical': 'False', 'through': "'ActivityAuditStatus'", 'to': "orm['auth.User']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.activityauditstatus': {
+ 'Meta': {'unique_together': "(('user', 'activity'),)", 'object_name': 'ActivityAuditStatus'},
+ 'activity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Activity']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.anonymousanswer': {
+ 'Meta': {'object_name': 'AnonymousAnswer'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'anonymous_answers'", 'to': "orm['askbot.Question']"}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'})
+ },
+ 'askbot.anonymousquestion': {
+ 'Meta': {'object_name': 'AnonymousQuestion'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ip_addr': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}),
+ 'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'})
+ },
+ 'askbot.answer': {
+ 'Meta': {'object_name': 'Answer', 'db_table': "u'answer'"},
+ 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_answers'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answers'", 'to': "orm['askbot.Question']"}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.answerrevision': {
+ 'Meta': {'object_name': 'AnswerRevision', 'db_table': "u'answer_revision'"},
+ 'answer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Answer']"}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'answerrevisions'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'revised_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {})
+ },
+ 'askbot.award': {
+ 'Meta': {'object_name': 'Award', 'db_table': "u'award'"},
+ 'awarded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'badge': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_badge'", 'to': "orm['askbot.Badge']"}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'notified': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badge': {
+ 'Meta': {'unique_together': "(('name', 'type'),)", 'object_name': 'Badge', 'db_table': "u'badge'"},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "'Award'", 'to': "orm['auth.User']"}),
+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'multiple': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'db_index': 'True', 'max_length': '50', 'blank': 'True'}),
+ 'type': ('django.db.models.fields.SmallIntegerField', [], {})
+ },
+ 'askbot.comment': {
+ 'Meta': {'object_name': 'Comment', 'db_table': "u'comment'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'html': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '2048'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'object_name': 'EmailFeedSetting'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'feed_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'frequency': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '8'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reported_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'subscriber': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notification_subscriptions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.favoritequestion': {
+ 'Meta': {'object_name': 'FavoriteQuestion', 'db_table': "u'favorite_question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.flaggeditem': {
+ 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'FlaggedItem', 'db_table': "u'flagged_item'"},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'flagged_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'flaggeditems'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.markedtag': {
+ 'Meta': {'object_name': 'MarkedTag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'reason': ('django.db.models.fields.CharField', [], {'max_length': '16'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_selections'", 'to': "orm['askbot.Tag']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tag_selections'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.question': {
+ 'Meta': {'object_name': 'Question', 'db_table': "u'question'"},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'answer_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questions'", 'to': "orm['auth.User']"}),
+ 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'closed_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'deleted_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'deleted_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'favorite_questions'", 'symmetrical': 'False', 'through': "'FavoriteQuestion'", 'to': "orm['auth.User']"}),
+ 'favourite_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'followed_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followed_questions'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_activity_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_activity_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'last_active_in_questions'", 'to': "orm['auth.User']"}),
+ 'last_edited_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'last_edited_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_edited_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_questions'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'score': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '180'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'questions'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'vote_down_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'vote_up_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'wiki': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.questionrevision': {
+ 'Meta': {'object_name': 'QuestionRevision', 'db_table': "u'question_revision'"},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'questionrevisions'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['askbot.Question']"}),
+ 'revised_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'revision': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'summary': ('django.db.models.fields.CharField', [], {'max_length': '300', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'})
+ },
+ 'askbot.questionview': {
+ 'Meta': {'object_name': 'QuestionView'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'viewed'", 'to': "orm['askbot.Question']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.repute': {
+ 'Meta': {'object_name': 'Repute', 'db_table': "u'repute'"},
+ 'comment': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'negative': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'positive': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Question']", 'null': 'True', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'reputation_type': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'reputed_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'askbot.tag': {
+ 'Meta': {'object_name': 'Tag', 'db_table': "u'tag'"},
+ 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_tags'", 'to': "orm['auth.User']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'deleted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ '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'}),
+ 'used_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('content_type', 'object_id', 'user'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['auth.User']"}),
+ 'vote': ('django.db.models.fields.SmallIntegerField', [], {}),
+ 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'hide_ignored_questions': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'tag_filter_setting': ('django.db.models.fields.CharField', [], {'default': "'ignored'", 'max_length': '16'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ }
+ }
+
+ complete_apps = ['askbot']
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index ccac6b2d..4bb1293b 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -24,7 +24,7 @@ from askbot.models.question import FavoriteQuestion
from askbot.models.answer import Answer, AnonymousAnswer, AnswerRevision
from askbot.models.tag import Tag, MarkedTag
from askbot.models.meta import Vote, Comment, FlaggedItem
-from askbot.models.user import Activity, EmailFeedSetting
+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
@@ -980,8 +980,9 @@ def user_visit_question(self, question = None, timestamp = None):
ACTIVITY_TYPES = const.RESPONSE_ACTIVITY_TYPES_FOR_DISPLAY
ACTIVITY_TYPES += (const.TYPE_ACTIVITY_MENTION,)
response_activities = Activity.objects.filter(
- receiving_users = self,
+ recipients = self,
activity_type__in = ACTIVITY_TYPES,
+ question = question
)
try:
question_view = QuestionView.objects.get(
@@ -1000,17 +1001,17 @@ def user_visit_question(self, question = None, timestamp = None):
#as per the query in the beginning of this if branch)
#that refer to the children of the currently
#viewed question and clear them for the current user
- for activity in response_activities:
- post = activity.content_object
- if hasattr(post, 'get_origin_post'):
- if question == post.get_origin_post():
- activity.receiving_users.remove(self)
- self.decrement_response_count()
- else:
- logging.critical(
- 'activity content object has no get_origin_post method'
- )
- self.save()
+ need_to_save_user = False
+
+ audit_records = ActivityAuditStatus.objects.filter(
+ activity__in = response_activities,
+ user = self
+ )
+ if len(audit_records) > 0:
+ self.decrement_response_count(len(audit_records))
+ self.save()
+ #todo: set status to seen and call update
+ audit_records.delete()
def user_is_username_taken(cls,username):
try:
@@ -1363,14 +1364,16 @@ def user_increment_response_count(user):
"""
user.response_count += 1
-def user_decrement_response_count(user):
+def user_decrement_response_count(user, amount=1):
"""decrement response count for the user
by one, log critical error if count would go below zero
but limit decrementation at zero exactly
"""
- if user.response_count > 0:
- user.response_count -= 1
+ assert(amount > 0)
+ if user.response_count >= amount:
+ user.response_count -= amount
else:
+ user.response_count = 0
logging.critical(
'response count wanted to go below zero'
)
@@ -1541,7 +1544,7 @@ def format_instant_notification_email(
def send_instant_notifications_about_activity_in_post(
update_activity = None,
post = None,
- receiving_users = None,
+ recipients = None,
):
"""
function called when posts are updated
@@ -1549,7 +1552,7 @@ def send_instant_notifications_about_activity_in_post(
database hits
"""
- if receiving_users is None:
+ if recipients is None:
return
acceptable_types = const.RESPONSE_ACTIVITY_TYPES_FOR_INSTANT_NOTIFICATIONS
@@ -1564,7 +1567,7 @@ def send_instant_notifications_about_activity_in_post(
update_type = update_type_map[update_activity.activity_type]
origin_post = post.get_origin_post()
- for user in receiving_users:
+ for user in recipients:
subject_line, body_text = format_instant_notification_email(
to_user = user,
@@ -1614,7 +1617,8 @@ def record_post_update_activity(
user = updated_by,
active_at = timestamp,
content_object = post,
- activity_type = activity_type
+ activity_type = activity_type,
+ question = post.get_origin_post()
)
update_activity.save()
@@ -1622,23 +1626,23 @@ def record_post_update_activity(
#for example for question - all Q&A contributors
#are included, for comments only authors of comments and parent
#post are included
- receiving_users = post.get_response_receivers(
+ recipients = post.get_response_receivers(
exclude_list = [updated_by, ]
)
- update_activity.receiving_users.add(*receiving_users)
+ update_activity.add_recipients(recipients)
- assert(updated_by not in receiving_users)
+ assert(updated_by not in recipients)
- for user in set(receiving_users) | set(newly_mentioned_users):
+ for user in set(recipients) | set(newly_mentioned_users):
user.increment_response_count()
user.save()
- #todo: weird thing is that only comments need the receiving_users
+ #todo: weird thing is that only comments need the recipients
#todo: debug these calls and then uncomment in the repo
#argument to this call
notification_subscribers = post.get_instant_notification_subscribers(
- potential_subscribers = receiving_users,
+ potential_subscribers = recipients,
mentioned_users = newly_mentioned_users,
exclude_list = [updated_by, ]
)
@@ -1646,7 +1650,7 @@ def record_post_update_activity(
send_instant_notifications_about_activity_in_post(
update_activity = update_activity,
post = post,
- receiving_users = notification_subscribers,
+ recipients = notification_subscribers,
)
@@ -1665,7 +1669,7 @@ def record_award_event(instance, created, **kwargs):
activity_type=const.TYPE_ACTIVITY_PRIZE
)
activity.save()
- activity.receiving_users.add(instance.user)
+ activity.add_recipients([instance.user])
instance.badge.awarded_count += 1
instance.badge.save()
@@ -1704,13 +1708,14 @@ def record_answer_accepted(instance, created, **kwargs):
user=instance.question.author,
active_at=datetime.datetime.now(),
content_object=instance,
- activity_type=const.TYPE_ACTIVITY_MARK_ANSWER
+ activity_type=const.TYPE_ACTIVITY_MARK_ANSWER,
+ question=instance.question
)
activity.save()
- receiving_users = instance.get_author_list(
+ recipients = instance.get_author_list(
exclude_list = [instance.question.author]
)
- activity.receiving_users.add(*receiving_users)
+ activity.add_recipients(recipients)
def update_last_seen(instance, created, **kwargs):
@@ -1776,7 +1781,8 @@ def record_delete_question(instance, delete_by, **kwargs):
user=delete_by,
active_at=datetime.datetime.now(),
content_object=instance,
- activity_type=activity_type
+ activity_type=activity_type,
+ question = instance.get_origin_post()
)
#no need to set receiving user here
activity.save()
@@ -1786,13 +1792,14 @@ def record_flag_offensive(instance, mark_by, **kwargs):
user=mark_by,
active_at=datetime.datetime.now(),
content_object=instance,
- activity_type=const.TYPE_ACTIVITY_MARK_OFFENSIVE
+ activity_type=const.TYPE_ACTIVITY_MARK_OFFENSIVE,
+ question=instance.get_origin_post()
)
activity.save()
- receiving_users = instance.get_author_list(
+ recipients = instance.get_author_list(
exclude_list = [mark_by]
)
- activity.receiving_users.add(*receiving_users)
+ activity.add_recipients(recipients)
def record_update_tags(question, **kwargs):
"""
@@ -1802,7 +1809,8 @@ def record_update_tags(question, **kwargs):
user=question.author,
active_at=datetime.datetime.now(),
content_object=question,
- activity_type=const.TYPE_ACTIVITY_UPDATE_TAGS
+ activity_type=const.TYPE_ACTIVITY_UPDATE_TAGS,
+ question = question
)
activity.save()
@@ -1815,13 +1823,14 @@ def record_favorite_question(instance, created, **kwargs):
user=instance.user,
active_at=datetime.datetime.now(),
content_object=instance,
- activity_type=const.TYPE_ACTIVITY_FAVORITE
+ activity_type=const.TYPE_ACTIVITY_FAVORITE,
+ question=instance.question
)
activity.save()
- receiving_users = instance.question.get_author_list(
+ recipients = instance.question.get_author_list(
exclude_list = [instance.user]
)
- activity.receiving_users.add(*receiving_users)
+ activity.add_recipients(recipients)
def record_user_full_updated(instance, **kwargs):
activity = Activity(
@@ -1872,7 +1881,7 @@ django_signals.post_save.connect(record_answer_accepted, sender=Answer)
django_signals.post_save.connect(update_last_seen, sender=Activity)
django_signals.post_save.connect(record_vote, sender=Vote)
django_signals.post_save.connect(
- record_favorite_question,
+ record_favorite_question,
sender=FavoriteQuestion
)
django_signals.post_delete.connect(record_cancel_vote, sender=Vote)
@@ -1923,6 +1932,7 @@ Award = Award
Repute = Repute
Activity = Activity
+ActivityAuditStatus = ActivityAuditStatus
EmailFeedSetting = EmailFeedSetting
#AuthKeyUserAssociation = AuthKeyUserAssociation
@@ -1950,6 +1960,7 @@ __all__ = [
'Repute',
'Activity',
+ 'ActivityAuditStatus',
'EmailFeedSetting',
#'AuthKeyUserAssociation',
diff --git a/askbot/models/answer.py b/askbot/models/answer.py
index 634481d9..0cf5ec5c 100644
--- a/askbot/models/answer.py
+++ b/askbot/models/answer.py
@@ -169,23 +169,23 @@ class Answer(content.Content, DeletableContent):
the response
"""
assert(exclude_list is not None)
- receiving_users = set()
- receiving_users.update(
+ recipients = set()
+ recipients.update(
self.get_author_list(
include_comments = True
)
)
- receiving_users.update(
+ recipients.update(
self.question.get_author_list(
include_comments = True
)
)
for answer in self.question.answers.all():
- receiving_users.update(answer.get_author_list())
+ recipients.update(answer.get_author_list())
- receiving_users -= set(exclude_list)
+ recipients -= set(exclude_list)
- return list(receiving_users)
+ return list(recipients)
def get_user_vote(self, user):
if user.is_anonymous():
diff --git a/askbot/models/meta.py b/askbot/models/meta.py
index 8fbb651e..248979ae 100644
--- a/askbot/models/meta.py
+++ b/askbot/models/meta.py
@@ -248,7 +248,7 @@ class Comment(base.MetaContent, base.UserContent):
activity_type__in = response_activity_types
)
for activity in activities:
- for user in activity.receiving_users.all():
+ for user in activity.recipients.all():
user.decrement_response_count()
user.save()
activities.delete()
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 81116e0d..ba4237c9 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -452,18 +452,18 @@ class Question(content.Content, DeletableContent):
author of the update so the he/she is not notified about the update
"""
assert(exclude_list != None)
- receiving_users = set()
- receiving_users.update(
+ recipients = set()
+ recipients.update(
self.get_author_list(
include_comments = True
)
)
#do not include answer commenters here
for a in self.answers.all():
- receiving_users.update(a.get_author_list())
+ recipients.update(a.get_author_list())
- receiving_users -= set(exclude_list)
- return receiving_users
+ recipients -= set(exclude_list)
+ return recipients
def retag(self, retagged_by=None, retagged_at=None, tagnames=None):
if None in (retagged_by, retagged_at, tagnames):
diff --git a/askbot/models/user.py b/askbot/models/user.py
index f0a34551..ad302705 100644
--- a/askbot/models/user.py
+++ b/askbot/models/user.py
@@ -73,13 +73,12 @@ class ActivityManager(models.Manager):
kwargs['is_auditted'] = False
mention_activity = Activity(**kwargs)
+ mention_activity.question = mentioned_in.get_origin_post()
mention_activity.save()
if mentioned_whom:
- if functions.is_iterable(mentioned_whom):
- raise NotImplementedError('cannot yet mention multiple people at once')
- else:
- mention_activity.receiving_users.add(mentioned_whom)
+ assert(isinstance(mentioned_whom, User))
+ mention_activity.add_recipients([mentioned_whom])
return mention_activity
@@ -111,9 +110,9 @@ class ActivityManager(models.Manager):
if mentioned_whom:
if functions.is_iterable(mentioned_whom):
- kwargs['receiving_users__in'] = mentioned_whom
+ kwargs['recipients__in'] = mentioned_whom
else:
- kwargs['receiving_users__in'] = (mentioned_whom,)
+ kwargs['recipients__in'] = (mentioned_whom,)
if mentioned_in:
if functions.is_iterable(mentioned_in):
@@ -132,17 +131,38 @@ class ActivityManager(models.Manager):
return self.filter(**kwargs)
+class ActivityAuditStatus(models.Model):
+ """bridge "through" relation between activity and users"""
+ STATUS_NEW = 0
+ STATUS_SEEN = 1
+ STATUS_CHOICES = (
+ (STATUS_NEW, 'new'),
+ (STATUS_SEEN, 'seen')
+ )
+ user = models.ForeignKey(User)
+ activity = models.ForeignKey('Activity')
+ status = models.SmallIntegerField(choices=STATUS_CHOICES, default=STATUS_NEW)
+
+ class Meta:
+ unique_together = ('user', 'activity')
+ app_label = 'askbot'
+ db_table = 'askbot_activityauditstatus'
+
+
class Activity(models.Model):
"""
We keep some history data for user activities
"""
user = models.ForeignKey(User)
receiving_users = models.ManyToManyField(User, related_name='received_activity')
+ recipients = models.ManyToManyField(User, through=ActivityAuditStatus, related_name='incoming_activity')
activity_type = models.SmallIntegerField(choices = const.TYPE_ACTIVITY)
active_at = models.DateTimeField(default=datetime.datetime.now)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
+ #todo: remove this denorm question field when Post model is set up
+ question = models.ForeignKey('Question', null=True)
is_auditted = models.BooleanField(default=False)
objects = ActivityManager()
@@ -155,9 +175,18 @@ class Activity(models.Model):
app_label = 'askbot'
db_table = u'activity'
+ def add_recipients(self, recipients):
+ """have to use a special method, because django does not allow
+ auto-adding to M2M with "through" model
+ """
+ for recipient in recipients:
+ #todo: may optimize for bulk addition
+ aas = ActivityAuditStatus(user = recipient, activity = self)
+ aas.save()
+
def get_mentioned_user(self):
assert(self.activity_type == const.TYPE_ACTIVITY_MENTION)
- user_qs = self.receiving_users.all()
+ user_qs = self.recipients.all()
user_count = len(user_qs)
if user_count == 0:
return None
diff --git a/askbot/tests/on_screen_notification_tests.py b/askbot/tests/on_screen_notification_tests.py
index ea607810..68e51d33 100644
--- a/askbot/tests/on_screen_notification_tests.py
+++ b/askbot/tests/on_screen_notification_tests.py
@@ -89,6 +89,13 @@ class OnScreenUpdateNotificationTests(TestCase):
self.reload_users()
#pre-populate askbot with some content
+ #create a question and two answers, each post gets two comments
+ #users have two digit codes. What users do in the setup code
+ #is explained below (x is a variable that takes integer values of [1-3])
+ #user x1 makes a post, users x2 and x3 add comments to that post
+ #users 1x work on question, 2x and 3x on the answers
+ #users x4 do not do anyting in the setup code
+
self.question = models.Question.objects.create_new(
title = 'test question',
author = self.u11,
@@ -134,6 +141,27 @@ class OnScreenUpdateNotificationTests(TestCase):
comment = 'comment33'
)
+ def assertResponseCountsEqual(self, counts_vector):
+ self.reload_users()
+ self.assertEquals(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ counts_vector
+ )
+
+
def post_then_delete_answer_comment(self):
pass
@@ -167,22 +195,7 @@ class OnScreenUpdateNotificationTests(TestCase):
comment.delete()
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 0)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
0, 0, 0, 0,
0, 0, 0, 0,
@@ -200,22 +213,7 @@ class OnScreenUpdateNotificationTests(TestCase):
comment.delete()
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 0)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
0, 0, 0, 0,
0, 0, 0, 0,
@@ -235,25 +233,10 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set([self.u12, self.u13]),
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
0, 1, 1, 0,
0, 0, 0, 0,
@@ -271,25 +254,10 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set([self.u22, self.u23]),
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
0, 0, 0, 0,
0, 1, 1, 0,
@@ -309,25 +277,10 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set([self.u12, self.u13]),
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
0, 1, 1, 0,
0, 0, 0, 0,
@@ -347,25 +300,10 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set([self.u12, self.u13]),
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
0, 1, 1, 0,
0, 0, 0, 0,
@@ -385,25 +323,10 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set([self.u22, self.u23]),
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
0, 0, 0, 0,
0, 1, 1, 0,
@@ -411,6 +334,52 @@ class OnScreenUpdateNotificationTests(TestCase):
]
)
+ def test_responses_clear_after_visit(self):
+ """user 14 posts comment under question
+ user 11, 12, 21, and 22 visit the question
+ user 13 does not
+ the expected outcome is that 11 and 12 have
+ 0 responses and 13 still has one
+ remaining users still have notifications
+ """
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.question.add_comment(
+ user = self.u14,
+ comment = 'dudududududu',
+ added_at = timestamp
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].recipients.all()),
+ set([self.u11, self.u12, self.u13])#all users are notified
+ )
+ self.assertResponseCountsEqual(
+ [
+ 1, 1, 1, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+ self.u11.visit_question(self.question)
+ self.u12.visit_question(self.question)
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(#visitors are not notified
+ set(notifications[0].recipients.all()),
+ set([self.u13])
+ )
+ self.assertResponseCountsEqual(
+ [
+ 0, 0, 1, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+
+
def test_comments_to_post_authors(self):
self.question.apply_edit(
edited_by = self.u14,
@@ -428,25 +397,10 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set([self.u11, self.u13, self.u14]),
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
1, 0, 1, 1,
0, 0, 0, 0,
@@ -469,25 +423,10 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set([self.u21, self.u23, self.u24]),
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
0, 0, 0, 0,
1, 0, 1, 1,
@@ -511,25 +450,10 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set([self.u11, self.u12, self.u13, self.u21, self.u31])
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
1, 1, 1, 0,
1, 0, 0, 0,
@@ -547,25 +471,10 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set([self.u11, self.u12, self.u13, self.u14, self.u21])
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
1, 1, 1, 1,
1, 0, 0, 0,
@@ -587,7 +496,7 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set(
[
self.u11, self.u12, self.u13,
@@ -596,22 +505,7 @@ class OnScreenUpdateNotificationTests(TestCase):
]
)
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
1, 1, 1, 0,
1, 1, 1, 0,
@@ -633,7 +527,7 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set(
[
self.u12, self.u13,
@@ -641,22 +535,7 @@ class OnScreenUpdateNotificationTests(TestCase):
]
)
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
0, 1, 1, 0,
1, 0, 0, 0,
@@ -675,7 +554,7 @@ class OnScreenUpdateNotificationTests(TestCase):
notifications = get_re_notif_after(timestamp)
self.assertEqual(len(notifications), 1)
self.assertEqual(
- set(notifications[0].receiving_users.all()),
+ set(notifications[0].recipients.all()),
set(
[
self.u11, self.u12, self.u13,
@@ -683,22 +562,7 @@ class OnScreenUpdateNotificationTests(TestCase):
]
)
)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
+ self.assertResponseCountsEqual(
[
1, 1, 1, 0,
1, 0, 0, 0,
diff --git a/askbot/views/users.py b/askbot/views/users.py
index 32a2bdcb..0db51705 100644
--- a/askbot/views/users.py
+++ b/askbot/views/users.py
@@ -704,9 +704,7 @@ def user_responses(request, user):
response_list = []
activities = list()
- activities = models.Activity.responses_and_mentions.filter(
- receiving_users = user
- )
+ activities = models.Activity.responses_and_mentions.filter(recipients=user)
for act in activities:
origin_post = act.content_object.get_origin_post()