summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdolfo Fitoria <adolfo.fitoria@gmail.com>2012-10-17 09:09:09 -0600
committerAdolfo Fitoria <adolfo.fitoria@gmail.com>2012-10-17 09:09:09 -0600
commitdb2b8e4e7dfee48e3641a7b9ad4262c07d9160f7 (patch)
tree250d3c5f03673195c86e1acdccad83187a3ccdf2
parente641ae9c3270fdc86e6b25af2399e4c56f85867e (diff)
parented59d636fa57c8a381a9fb5e19a21c3fde5fce10 (diff)
downloadaskbot-db2b8e4e7dfee48e3641a7b9ad4262c07d9160f7.tar.gz
askbot-db2b8e4e7dfee48e3641a7b9ad4262c07d9160f7.tar.bz2
askbot-db2b8e4e7dfee48e3641a7b9ad4262c07d9160f7.zip
Merge branch 'master' of github.com:ASKBOT/askbot-devel
-rw-r--r--askbot/media/style/style.css46
-rw-r--r--askbot/media/style/style.less20
-rw-r--r--askbot/migrations/0004_install_full_text_indexes_for_mysql.py18
-rw-r--r--askbot/migrations/0154_update_postgres_user_search.py390
-rw-r--r--askbot/models/post.py2
-rw-r--r--askbot/models/question.py27
-rw-r--r--askbot/search/mysql.py29
-rw-r--r--askbot/search/postgresql/user_profile_search_16102012.plsql98
-rw-r--r--askbot/tests/db_api_tests.py4
-rw-r--r--askbot/tests/user_model_tests.py10
-rw-r--r--askbot/utils/mysql.py36
11 files changed, 593 insertions, 87 deletions
diff --git a/askbot/media/style/style.css b/askbot/media/style/style.css
index 3abbd362..79bc422b 100644
--- a/askbot/media/style/style.css
+++ b/askbot/media/style/style.css
@@ -139,6 +139,9 @@ blockquote {
padding: 10px 0px 1px 10px;
background-color: #F5F5F5;
}
+html {
+ overflow-y: scroll;
+}
/* http://pathfindersoftware.com/2007/09/developers-note-2/ */
* html .clearfix,
* html .paginator {
@@ -444,6 +447,7 @@ body.user-messages {
#searchBar .searchInputCancelable {
font-size: 26px;
height: 39px;
+ line-height: 39px;
font-weight: 300;
background: #FFF;
border: 0px;
@@ -484,6 +488,7 @@ body.user-messages {
color: #ce8888;
background: #fff;
height: 42px;
+ line-height: 42px;
border: 0px;
border-left: #deded0 1px solid;
text-align: center;
@@ -661,6 +666,7 @@ body.anon #searchBar .searchInputCancelable {
padding-left: 5px;
border: #c9c9b5 1px solid;
height: 25px;
+ line-height: 25px;
font-size: 14px;
}
.box .inputs #ab-tag-search {
@@ -1434,8 +1440,8 @@ ul#related-tags li {
}
#askFormBar .questionTitleInput {
font-size: 24px;
- line-height: 24px;
height: 36px;
+ line-height: 36px;
margin: 0px;
padding: 0px 0 0 5px;
border: #cce6ec 3px solid;
@@ -1501,6 +1507,7 @@ ul#related-tags li {
.edit-answer-page #id_post_author_email {
border: #cce6ec 3px solid;
height: 25px;
+ line-height: 25px;
padding-left: 5px;
font-size: 14px;
width: 186px;
@@ -1815,6 +1822,24 @@ ul#related-tags li {
.wmd-preview IMG {
max-width: 600px;
}
+.defaultSkin table.mceLayout,
+.defaultSkin table.mceLayout tr.mceFirst td {
+ border: none;
+}
+.defaultSkin table.mceLayout tr.mceLast td {
+ border-bottom: none;
+}
+.mceStatusbar {
+ height: 5px;
+ background: #fff;
+}
+.defaultSkin span.mce_askbot_imageuploader {
+ background-position: -380px 0px;
+}
+.defaultSkin span.mce_askbot_attachment {
+ background-image: url(../images/attachment.png);
+ background-position: 0px 0px;
+}
.user-page .wmd-buttons {
width: 725px;
}
@@ -2053,16 +2078,24 @@ ul#related-tags li {
}
.question-page .post-controls .question-delete,
.question-page .answer-controls .question-delete {
- background: url(../images/delete.png) no-repeat center left;
+ background: url(../images/delete.png) no-repeat left 2px;
padding-left: 11px;
}
.question-page .post-controls .question-flag,
.question-page .answer-controls .question-flag {
background: url(../images/flag.png) no-repeat center left;
}
+.question-page .post-controls .answer-publish,
+.question-page .answer-controls .answer-publish {
+ background: url(../images/publish.png) no-repeat center left;
+}
+.question-page .post-controls .answer-unpublish,
+.question-page .answer-controls .answer-unpublish {
+ background: url(../images/unpublish.png) no-repeat 2px center;
+}
.question-page .post-controls .question-edit,
.question-page .answer-controls .question-edit {
- background: url(../images/edit2.png) no-repeat center left;
+ background: url(../images/edit2.png) no-repeat 2px center;
}
.question-page .post-controls .question-retag,
.question-page .answer-controls .question-retag {
@@ -2607,6 +2640,7 @@ ul#related-tags li {
.user-profile-page select {
border: #cce6ec 3px solid;
height: 25px;
+ line-height: 25px;
padding-left: 5px;
width: 395px;
font-size: 14px;
@@ -2886,11 +2920,10 @@ a:hover.medal {
/* profile page */
.tabBar-profile {
width: 100%;
- margin-bottom: 15px;
+ margin-bottom: 5px;
float: left;
}
.user-profile-page {
- font-size: 13px;
color: #525252;
}
.user-profile-page p {
@@ -3301,7 +3334,7 @@ table.ab-subscr-form {
}
.errors {
line-height: 20px;
- color: red;
+ color: #990000;
}
.error,
.openid-signin p.error {
@@ -4084,6 +4117,7 @@ textarea.tipped-input {
border: none;
font-size: 15px;
font-color: #707070;
+ height: 16px;
line-height: 16px;
margin-top: 9px;
-webkit-box-shadow: none;
diff --git a/askbot/media/style/style.less b/askbot/media/style/style.less
index a7d035ed..9e2e2a25 100644
--- a/askbot/media/style/style.less
+++ b/askbot/media/style/style.less
@@ -469,6 +469,7 @@ body.user-messages {
.searchInput, .searchInputCancelable{
font-size: 26px;
height: 39px;
+ line-height: 39px;
font-weight:300;
background:#FFF;
border:0px;
@@ -515,6 +516,7 @@ body.user-messages {
color: #ce8888;
background:#fff;
height: 42px;
+ line-height: 42px;
border:0px;
border-left:#deded0 1px solid;
text-align: center;
@@ -669,6 +671,7 @@ body.anon {
padding-left:5px;
border:#c9c9b5 1px solid;
height:25px;
+ line-height:25px;
font-size: 14px;
}
#ab-tag-search {
@@ -1433,8 +1436,8 @@ ul#related-tags li {
}
.questionTitleInput {
font-size: 24px;
- line-height: 24px;
height: 36px;
+ line-height: 36px;
margin: 0px;
padding: 0px 0 0 5px;
border:#cce6ec 3px solid;
@@ -1496,11 +1499,12 @@ ul#related-tags li {
.edit-answer-page {
#id_post_author_username,
#id_post_author_email {
- border:#cce6ec 3px solid;
- height:25px;
- padding-left:5px;
- font-size:14px;
- width:186px;
+ border: #cce6ec 3px solid;
+ height: 25px;
+ line-height: 25px;
+ padding-left: 5px;
+ font-size: 14px;
+ width: 186px;
}
#id_post_author_email {
margin-left: 10px;
@@ -2503,6 +2507,7 @@ ul#related-tags li {
input[type="text"],input[type="password"],select{
border:#cce6ec 3px solid;
height:25px;
+ line-height: 25px;
padding-left:5px;
width:395px;
font-size:14px;
@@ -2564,7 +2569,7 @@ ul#related-tags li {
#email-input-fs,#local_login_buttons,#password-fs,#openid-fs{
margin-top:10px;
#id_email,#id_username,#id_password{
- font-size: 12px;
+ font-size: 12px;
line-height: 20px;
height: 20px;
margin: 0px;
@@ -3974,6 +3979,7 @@ textarea.tipped-input {
border: none;
font-size: 15px;
font-color: @info-text;
+ height: 16px;
line-height: 16px;
margin-top: 9px;
-webkit-box-shadow: none;/* undo bootstrap glow */
diff --git a/askbot/migrations/0004_install_full_text_indexes_for_mysql.py b/askbot/migrations/0004_install_full_text_indexes_for_mysql.py
index 7c7c52f0..e484c59b 100644
--- a/askbot/migrations/0004_install_full_text_indexes_for_mysql.py
+++ b/askbot/migrations/0004_install_full_text_indexes_for_mysql.py
@@ -3,11 +3,12 @@ import os
import datetime
from south.db import db
from south.v2 import DataMigration
+from django.db import connection
from django.db import models
-from askbot.utils import mysql
Q_INDEX_NAME = 'askbot_question_full_text_index'
A_INDEX_NAME = 'askbot_answer_full_text_index'
+SUPPORTS_FTS = None
NO_FTS_WARNING = """
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@@ -18,6 +19,17 @@ NO_FTS_WARNING = """
!! !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
"""
+def supports_full_text_search():
+ global SUPPORTS_FTS
+ if SUPPORTS_FTS is None:
+ cursor = connection.cursor()
+ cursor.execute("SHOW CREATE TABLE question") # In migration 0004 model forum.Question used db table `question`
+ data = cursor.fetchone()
+ if 'ENGINE=MyISAM' in data[1]:
+ SUPPORTS_FTS = True
+ else:
+ SUPPORTS_FTS = False
+ return SUPPORTS_FTS
def get_create_full_text_index_sql(index_name, table_name, column_list):
column_sql = '(%s)' % ','.join(column_list)
@@ -35,7 +47,7 @@ class Migration(DataMigration):
and will probably fail otherwise
"""
if db.backend_name == 'mysql':
- if mysql.supports_full_text_search_migr0004():
+ if supports_full_text_search():
#todo: extract column names by introspection
question_index_sql = get_create_full_text_index_sql(
Q_INDEX_NAME,
@@ -55,7 +67,7 @@ class Migration(DataMigration):
def backwards(self, orm):
"code for removal of full text indices in mysql"
- if db.backend_name == 'mysql' and mysql.supports_full_text_search():
+ if db.backend_name == 'mysql' and supports_full_text_search():
db.execute(
get_drop_index_sql(
Q_INDEX_NAME,
diff --git a/askbot/migrations/0154_update_postgres_user_search.py b/askbot/migrations/0154_update_postgres_user_search.py
new file mode 100644
index 00000000..54bb5c50
--- /dev/null
+++ b/askbot/migrations/0154_update_postgres_user_search.py
@@ -0,0 +1,390 @@
+# encoding: utf-8
+import askbot
+import datetime
+import os
+from askbot.search import postgresql
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Write your forwards methods here."
+ db_engine_name = askbot.get_database_engine_name()
+ if 'postgresql_psycopg2' in db_engine_name:
+ script_path = os.path.join(
+ askbot.get_install_directory(),
+ 'search',
+ 'postgresql',
+ 'user_profile_search_16102012.plsql'
+ )
+ postgresql.setup_full_text_search(script_path)
+
+
+ def backwards(self, orm):
+ "Write your backwards methods here."
+
+
+ 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'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'question': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']", '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': "orm['askbot.ActivityAuditStatus']", 'to': "orm['auth.User']"}),
+ 'summary': ('django.db.models.fields.TextField', [], {'default': "''"}),
+ '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.Post']"}),
+ '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'})
+ },
+ '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'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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'})
+ },
+ 'askbot.askwidget': {
+ 'Meta': {'object_name': 'AskWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'include_text_field': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'inner_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'outer_style': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Tag']", 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ '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.BadgeData']"}),
+ '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'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.badgedata': {
+ 'Meta': {'ordering': "('slug',)", 'object_name': 'BadgeData'},
+ 'awarded_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'awarded_to': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'badges'", 'symmetrical': 'False', 'through': "orm['askbot.Award']", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'})
+ },
+ 'askbot.draftanswer': {
+ 'Meta': {'object_name': 'DraftAnswer'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'draft_answers'", 'to': "orm['askbot.Thread']"})
+ },
+ 'askbot.draftquestion': {
+ 'Meta': {'object_name': 'DraftQuestion'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125', 'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'})
+ },
+ 'askbot.emailfeedsetting': {
+ 'Meta': {'unique_together': "(('subscriber', 'feed_type'),)", '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'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_favorite_questions'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.group': {
+ 'Meta': {'object_name': 'Group', '_ormbases': ['auth.Group']},
+ 'description': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'described_group'", 'unique': 'True', 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'group_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.Group']", 'unique': 'True', 'primary_key': 'True'}),
+ 'is_vip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
+ 'moderate_answers_to_enquirers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'moderate_email': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'openness': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'preapproved_email_domains': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
+ 'preapproved_emails': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.groupmembership': {
+ 'Meta': {'object_name': 'GroupMembership', '_ormbases': ['auth.AuthUserGroups']},
+ 'authusergroups_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.AuthUserGroups']", 'unique': 'True', 'primary_key': 'True'}),
+ 'level': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ '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.post': {
+ 'Meta': {'object_name': 'Post'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'posts'", 'to': "orm['auth.User']"}),
+ 'comment_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': '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_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_posts'", 'symmetrical': 'False', 'through': "orm['askbot.PostToGroup']", 'to': "orm['askbot.Group']"}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'locked_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'locked_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locked_posts'", 'null': 'True', 'to': "orm['auth.User']"}),
+ 'offensive_flag_count': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'old_answer_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_comment_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'old_question_id': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'post_type': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'summary': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'posts'", 'null': 'True', 'blank': 'True', 'to': "orm['askbot.Thread']"}),
+ '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'}),
+ 'wikified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'askbot.postflagreason': {
+ 'Meta': {'object_name': 'PostFlagReason'},
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'details': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'post_reject_reasons'", 'to': "orm['askbot.Post']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'askbot.postrevision': {
+ 'Meta': {'ordering': "('-revision',)", 'unique_together': "(('post', 'revision'),)", 'object_name': 'PostRevision'},
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'approved_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'approved_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'postrevisions'", 'to': "orm['auth.User']"}),
+ 'by_email': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'revisions'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ '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', [], {'default': "''", 'max_length': '125', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {}),
+ 'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '300', 'blank': 'True'})
+ },
+ 'askbot.posttogroup': {
+ 'Meta': {'unique_together': "(('post', 'group'),)", 'object_name': 'PostToGroup', 'db_table': "'askbot_post_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Post']"})
+ },
+ '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.Post']"}),
+ 'when': ('django.db.models.fields.DateTimeField', [], {}),
+ 'who': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'question_views'", 'to': "orm['auth.User']"})
+ },
+ 'askbot.questionwidget': {
+ 'Meta': {'object_name': 'QuestionWidget'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'order_by': ('django.db.models.fields.CharField', [], {'default': "'-added_at'", 'max_length': '18'}),
+ 'question_number': ('django.db.models.fields.PositiveIntegerField', [], {'default': '7'}),
+ 'search_query': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'style': ('django.db.models.fields.TextField', [], {'default': '"\\n@import url(\'http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:300,400,700\');\\nbody {\\n overflow: hidden;\\n}\\n\\n#container {\\n width: 200px;\\n height: 350px;\\n}\\nul {\\n list-style: none;\\n padding: 5px;\\n margin: 5px;\\n}\\nli {\\n border-bottom: #CCC 1px solid;\\n padding-bottom: 5px;\\n padding-top: 5px;\\n}\\nli:last-child {\\n border: none;\\n}\\na {\\n text-decoration: none;\\n color: #464646;\\n font-family: \'Yanone Kaffeesatz\', sans-serif;\\n font-size: 15px;\\n}\\n"', 'blank': 'True'}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'askbot.replyaddress': {
+ 'Meta': {'object_name': 'ReplyAddress'},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '25'}),
+ 'allowed_from_email': ('django.db.models.fields.EmailField', [], {'max_length': '150'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reply_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'reply_action': ('django.db.models.fields.CharField', [], {'default': "'auto_answer_or_comment'", 'max_length': '32'}),
+ 'response_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'edit_addresses'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'used_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'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.Post']", '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': {'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']"}),
+ 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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'}),
+ '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'})
+ },
+ 'askbot.thread': {
+ 'Meta': {'object_name': 'Thread'},
+ 'accepted_answer': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['askbot.Post']"}),
+ 'added_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'answer_accepted_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'answer_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'approved': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'close_reason': ('django.db.models.fields.SmallIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'closed_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'closed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+ 'favorited_by': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'unused_favorite_threads'", 'symmetrical': 'False', 'through': "orm['askbot.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_threads'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'group_threads'", 'symmetrical': 'False', 'through': "orm['askbot.ThreadToGroup']", 'to': "orm['askbot.Group']"}),
+ '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': "'unused_last_active_in_threads'", 'to': "orm['auth.User']"}),
+ 'points': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_column': "'score'"}),
+ 'tagnames': ('django.db.models.fields.CharField', [], {'max_length': '125'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'threads'", 'symmetrical': 'False', 'to': "orm['askbot.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '300'}),
+ 'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
+ },
+ 'askbot.threadtogroup': {
+ 'Meta': {'unique_together': "(('thread', 'group'),)", 'object_name': 'ThreadToGroup', 'db_table': "'askbot_thread_groups'"},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'thread': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['askbot.Thread']"}),
+ 'visibility': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'})
+ },
+ 'askbot.vote': {
+ 'Meta': {'unique_together': "(('user', 'voted_post'),)", 'object_name': 'Vote', 'db_table': "u'vote'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ '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'}),
+ 'voted_post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['askbot.Post']"})
+ },
+ 'auth.authusergroups': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'AuthUserGroups', 'db_table': "'auth_user_groups'", 'managed': 'False'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ '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': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", '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'}),
+ 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ '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'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ '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'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ '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'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ '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': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", '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/post.py b/askbot/models/post.py
index 10b3cdc7..0c5e2b00 100644
--- a/askbot/models/post.py
+++ b/askbot/models/post.py
@@ -40,7 +40,7 @@ from askbot.models.base import BaseQuerySetManager, DraftContent
#todo: maybe merge askbot.utils.markup and forum.utils.html
from askbot.utils.diff import textDiff as htmldiff
-from askbot.utils import mysql
+from askbot.search import mysql
class PostToGroup(models.Model):
post = models.ForeignKey('Post')
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 6c45f1eb..44653dd0 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -6,6 +6,7 @@ from django.conf import settings as django_settings
from django.db import models
from django.contrib.auth.models import User
from django.core import cache # import cache, not from cache import cache, to be able to monkey-patch cache.cache in test cases
+from django.core import exceptions as django_exceptions
from django.core.urlresolvers import reverse
from django.utils.hashcompat import md5_constructor
from django.utils.translation import ugettext as _
@@ -176,7 +177,7 @@ class ThreadManager(BaseQuerySetManager):
"""returns a query set of questions,
matching the full text query
"""
- if django_settings.ENABLE_HAYSTACK_SEARCH:
+ if getattr(django_settings, 'ENABLE_HAYSTACK_SEARCH', False):
from askbot.search.haystack import AskbotSearchQuerySet
hs_qs = AskbotSearchQuerySet().filter(content=search_query)
return hs_qs.get_django_queryset()
@@ -612,6 +613,8 @@ class Thread(models.Model):
group_ids = thread_groups.values_list('group_id', flat=True)
+ group_ids = list(group_ids)#force query for MySQL
+
from askbot.models import GroupMembership
user_ids = GroupMembership.objects.filter(
group__id__in=group_ids
@@ -635,7 +638,7 @@ class Thread(models.Model):
thread_groups = thread_groups[:max_count]
group_ids = thread_groups.values_list('group_id', flat=True)
- return Group.objects.filter(id__in=group_ids)
+ return Group.objects.filter(id__in=list(group_ids))#force list 4 mysql
def update_favorite_count(self):
self.favourite_count = FavoriteQuestion.objects.filter(thread=self).count()
@@ -1158,8 +1161,8 @@ class Thread(models.Model):
#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
+ delete_tags(unused_tags)#tags with used_count == 0 are deleted
modified_tags = removed_tags
#add new tags to the relation
@@ -1173,8 +1176,9 @@ class Thread(models.Model):
added_tags = list(reused_tags)
#tag moderation is in the call below
created_tags = Tag.objects.create_in_bulk(
- tag_names = new_tagnames, user = user
- )
+ tag_names=new_tagnames,
+ user=user
+ )
added_tags.extend(created_tags)
#todo: not nice that assignment of added_tags is way above
@@ -1216,15 +1220,15 @@ class Thread(models.Model):
####################################################################
self.update_summary_html() # regenerate question/thread summary html
####################################################################
-
#if there are any modified tags, update their use counts
+ modified_tags = set(modified_tags) - set(unused_tags)
if modified_tags:
Tag.objects.update_use_counts(modified_tags)
signals.tags_updated.send(None,
- thread = self,
- tags = modified_tags,
- user = user,
- timestamp = timestamp
+ thread=self,
+ tags=modified_tags,
+ user=user,
+ timestamp=timestamp
)
return True
@@ -1251,6 +1255,9 @@ class Thread(models.Model):
if None in (retagged_by, retagged_at, tagnames):
raise Exception('arguments retagged_at, retagged_by and tagnames are required')
+ if len(tagnames) > 125:#todo: remove magic number!!!
+ raise django_exceptions.ValidationError('tagnames value too long')
+
thread_question = self._question_post()
self.tagnames = tagnames.strip()
diff --git a/askbot/search/mysql.py b/askbot/search/mysql.py
index df86070d..055b30ca 100644
--- a/askbot/search/mysql.py
+++ b/askbot/search/mysql.py
@@ -1,7 +1,6 @@
+"""Utilities for the MySQL backend"""
from django.db import connection
-SUPPORTS_FTS = None
-HINT_TABLE = None
NO_FTS_WARNING = """
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! !!
@@ -11,22 +10,15 @@ NO_FTS_WARNING = """
!! !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
"""
+SUPPORTS_FTS = None
-def supports_full_text_search(hint_table = None):
- """True if the database engine is MyISAM
- hint_table - is the table that we look into to determine
- whether database supports FTS or not.
- """
+def supports_full_text_search():
+ """True if the database engine is MyISAM"""
+ from askbot.models import Post
global SUPPORTS_FTS
- global HINT_TABLE
if SUPPORTS_FTS is None:
cursor = connection.cursor()
- if hint_table:
- table_name = hint_table
- HINT_TABLE = hint_table
- else:
- from askbot.models import Post
- table_name = Post._meta.db_table
+ table_name = Post._meta.db_table
cursor.execute("SHOW CREATE TABLE %s" % table_name)
data = cursor.fetchone()
if 'ENGINE=MyISAM' in data[1]:
@@ -35,19 +27,12 @@ def supports_full_text_search(hint_table = None):
SUPPORTS_FTS = False
return SUPPORTS_FTS
- question_index_sql = get_create_full_text_index_sql(
- index_name,
- table_namee,
- ('title','text','tagnames',)
- )
def get_create_full_text_index_sql(index_name, table_name, column_list):
cursor = connection.cursor()
column_sql = '(%s)' % ','.join(column_list)
sql = 'CREATE FULLTEXT INDEX %s on %s %s' % (index_name, table_name, column_sql)
- cursor.execute(question_index_sql)
+ cursor.execute(sql)
return sql
- else:
- print NO_FTS_WARNING
def get_drop_index_sql(index_name, table_name):
return 'ALTER TABLE %s DROP INDEX %s' % (table_name, index_name)
diff --git a/askbot/search/postgresql/user_profile_search_16102012.plsql b/askbot/search/postgresql/user_profile_search_16102012.plsql
new file mode 100644
index 00000000..ca00a5d3
--- /dev/null
+++ b/askbot/search/postgresql/user_profile_search_16102012.plsql
@@ -0,0 +1,98 @@
+/*
+Script depends on functions defined for general askbot full text search.
+to_tsvector(), add_tsvector_column()
+
+calculates text search vector for the user profile
+the searched fields are:
+1) user name
+2) user profile
+3) group names - for groups to which user belongs
+*/
+CREATE OR REPLACE FUNCTION get_auth_user_tsv(user_id integer)
+RETURNS tsvector AS
+$$
+DECLARE
+ group_query text;
+ user_query text;
+ onerow record;
+ tsv tsvector;
+BEGIN
+ group_query =
+ 'SELECT user_group.name as group_name ' ||
+ 'FROM auth_group AS user_group ' ||
+ 'INNER JOIN auth_user_groups AS gm ' ||
+ 'ON gm.user_id= ' || user_id || ' AND gm.group_id=user_group.id';
+
+ tsv = to_tsvector('');
+ FOR onerow in EXECUTE group_query LOOP
+ tsv = tsv || to_tsvector(onerow.group_name);
+ END LOOP;
+
+ user_query = 'SELECT username, about FROM auth_user WHERE id=' || user_id;
+ FOR onerow in EXECUTE user_query LOOP
+ tsv = tsv || to_tsvector(onerow.username) || to_tsvector(onerow.about);
+ END LOOP;
+ RETURN tsv;
+END;
+$$ LANGUAGE plpgsql;
+
+/* create tsvector columns in the content tables */
+SELECT add_tsvector_column('text_search_vector', 'auth_user');
+
+/* populate tsvectors with data */
+UPDATE auth_user SET text_search_vector = get_auth_user_tsv(id);
+
+/* one trigger per table for tsv updates */
+
+/* set up auth_user triggers */
+CREATE OR REPLACE FUNCTION auth_user_tsv_update_handler()
+RETURNS trigger AS
+$$
+BEGIN
+ new.text_search_vector = get_auth_user_tsv(new.id);
+ RETURN new;
+END;
+$$ LANGUAGE plpgsql;
+DROP TRIGGER IF EXISTS auth_user_tsv_update_trigger ON auth_user;
+
+CREATE TRIGGER auth_user_tsv_update_trigger
+BEFORE INSERT OR UPDATE ON auth_user
+FOR EACH ROW EXECUTE PROCEDURE auth_user_tsv_update_handler();
+
+/* group membership trigger - reindex users when group membership
+ * changes */
+CREATE OR REPLACE FUNCTION group_membership_tsv_update_handler()
+RETURNS trigger AS
+$$
+DECLARE
+ tsv tsvector;
+ user_query text;
+BEGIN
+ IF (TG_OP = 'INSERT') THEN
+ user_query = 'UPDATE auth_user SET username=username WHERE ' ||
+ 'id=' || new.user_id;
+ ELSE
+ user_query = 'UPDATE auth_user SET username=username WHERE ' ||
+ 'id=' || old.user_id;
+ END IF;
+ /* just trigger the tsv update on user */
+ EXECUTE user_query;
+ RETURN NULL;
+END;
+$$ LANGUAGE plpgsql;
+
+DROP TRIGGER IF EXISTS group_membership_tsv_update_trigger
+ON auth_user_groups;
+
+CREATE TRIGGER group_membership_tsv_update_trigger
+AFTER INSERT OR DELETE
+ON auth_user_groups
+FOR EACH ROW EXECUTE PROCEDURE group_membership_tsv_update_handler();
+
+/* todo: whenever group name changes - also
+ * reindex users belonging to the group */
+
+DROP INDEX IF EXISTS auth_user_search_idx;
+
+CREATE INDEX auth_user_search_idx ON auth_user
+USING gin(text_search_vector);
diff --git a/askbot/tests/db_api_tests.py b/askbot/tests/db_api_tests.py
index 5477990a..8a6ffe0e 100644
--- a/askbot/tests/db_api_tests.py
+++ b/askbot/tests/db_api_tests.py
@@ -186,11 +186,11 @@ class DBApiTests(AskbotTestCase):
self.assertTrue(saved_question.thread.answer_count == 1)
def test_unused_tag_is_auto_deleted(self):
- self.user.retag_question(self.question, tags = 'one-tag')
+ self.user.retag_question(self.question, tags='one-tag')
tag = models.Tag.objects.get(name='one-tag')
self.assertEquals(tag.used_count, 1)
self.assertEquals(tag.deleted, False)
- self.user.retag_question(self.question, tags = 'two-tag')
+ self.user.retag_question(self.question, tags='two-tag')
count = models.Tag.objects.filter(name='one-tag').count()
self.assertEquals(count, 0)
diff --git a/askbot/tests/user_model_tests.py b/askbot/tests/user_model_tests.py
index df4974dd..e46cdb77 100644
--- a/askbot/tests/user_model_tests.py
+++ b/askbot/tests/user_model_tests.py
@@ -15,3 +15,13 @@ class UserModelTests(AskbotTestCase):
group=group, user=user
)
self.assertEqual(memberships.count(), 1)
+
+ def test_delete_user(self):
+ user = self.create_user('user')
+ user.delete()
+ self.assertRaises(User.DoesNotExist, User.objects.get, username='user')
+
+ def test_rename_user(self):
+ user = self.create_user('user')
+ user.username = 'user2'
+ user.save()
diff --git a/askbot/utils/mysql.py b/askbot/utils/mysql.py
deleted file mode 100644
index cf0e71ef..00000000
--- a/askbot/utils/mysql.py
+++ /dev/null
@@ -1,36 +0,0 @@
-"""Utilities for the MySQL backend"""
-from django.db import connection
-
-#in-memory cached variable
-SUPPORTS_FTS = None
-
-def supports_full_text_search():
- """True if the database engine is MyISAM"""
- from askbot.models import Post
- global SUPPORTS_FTS
- if SUPPORTS_FTS is None:
- cursor = connection.cursor()
- table_name = Post._meta.db_table
- cursor.execute("SHOW CREATE TABLE %s" % table_name)
- data = cursor.fetchone()
- if 'ENGINE=MyISAM' in data[1]:
- SUPPORTS_FTS = True
- else:
- SUPPORTS_FTS = False
- return SUPPORTS_FTS
-
-
-# This is needed to maintain compatibility with the old 0004 migration
-# Usually South migrations should be self-contained and shouldn't depend on anything but themselves,
-# but 0004 is an unfortunate exception
-def supports_full_text_search_migr0004():
- global SUPPORTS_FTS
- if SUPPORTS_FTS is None:
- cursor = connection.cursor()
- cursor.execute("SHOW CREATE TABLE question") # In migration 0004 model forum.Question used db table `question`
- data = cursor.fetchone()
- if 'ENGINE=MyISAM' in data[1]:
- SUPPORTS_FTS = True
- else:
- SUPPORTS_FTS = False
- return SUPPORTS_FTS