summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomasz Zielinski <tomasz.zielinski@pyconsultant.eu>2011-12-12 13:17:31 +0100
committerTomasz Zielinski <tomasz.zielinski@pyconsultant.eu>2011-12-12 13:17:31 +0100
commit4cd8037ca1add201f9f0a11654588094e39b06c8 (patch)
tree4c2784c9fb3432d8e0c821b16777b124585fda98
parent5fc93a3786b89d366d2dfd19f4e665720aa2ba5e (diff)
downloadaskbot-4cd8037ca1add201f9f0a11654588094e39b06c8.tar.gz
askbot-4cd8037ca1add201f9f0a11654588094e39b06c8.tar.bz2
askbot-4cd8037ca1add201f9f0a11654588094e39b06c8.zip
Misc tweaks
-rw-r--r--askbot/migrations/0088_install__post_view__for__development.py3
-rw-r--r--askbot/models/content.py4
-rw-r--r--askbot/models/post.py3
-rw-r--r--askbot/models/post_view.sql208
-rw-r--r--askbot/models/question.py75
-rw-r--r--askbot/skins/default/templates/user_profile/user_stats.html4
-rw-r--r--askbot/tests/permission_assertion_tests.py1
-rw-r--r--askbot/views/users.py6
8 files changed, 246 insertions, 58 deletions
diff --git a/askbot/migrations/0088_install__post_view__for__development.py b/askbot/migrations/0088_install__post_view__for__development.py
index 7d7d1ba9..1066dc5d 100644
--- a/askbot/migrations/0088_install__post_view__for__development.py
+++ b/askbot/migrations/0088_install__post_view__for__development.py
@@ -12,6 +12,9 @@ class Migration(SchemaMigration):
create_post_view_sql = open(
askbot.get_path_to('models/post_view.sql')
).read()
+ import warnings
+ warnings.filterwarnings("ignore", "Unknown table 'askbot.askbot_post'") # DROP VIEW might raise a warning so let's filter that out
+ db.execute('DROP VIEW IF EXISTS askbot_post')
db.execute(create_post_view_sql)
def backwards(self, orm):
diff --git a/askbot/models/content.py b/askbot/models/content.py
index 8424c8f6..d877de7e 100644
--- a/askbot/models/content.py
+++ b/askbot/models/content.py
@@ -860,9 +860,7 @@ class Content(models.Model):
raise NotImplementedError
def get_question_title(self):
- if self.is_answer():
- return self.question.thread.title
- elif self.is_question():
+ if self.is_question():
if self.thread.closed:
attr = const.POST_STATUS['closed']
elif self.deleted:
diff --git a/askbot/models/post.py b/askbot/models/post.py
index 11a3f774..62389e7a 100644
--- a/askbot/models/post.py
+++ b/askbot/models/post.py
@@ -23,8 +23,9 @@ class Post(content.Content):
self_question = models.ForeignKey('Question', blank=True, null=True)
question = property(fget=lambda self: self.self_answer.question) # to simulate Answer model
+ question_id = property(fget=lambda self: self.self_answer.question_id) # to simulate Answer model
- thread = models.ForeignKey('Thread')
+ thread = models.ForeignKey('Thread', related_name='posts')
objects = PostManager()
diff --git a/askbot/models/post_view.sql b/askbot/models/post_view.sql
index a2218d7d..2abac4a9 100644
--- a/askbot/models/post_view.sql
+++ b/askbot/models/post_view.sql
@@ -4,21 +4,32 @@ SQL for creating a database VIEW for the unmanaged `Post` model
Tested with: SQLite3
+Important: String literals should be wrapped in single quotes (http://www.sqlite.org/lang_keywords.html)
+
*/
-CREATE VIEW IF NOT EXISTS askbot_post AS
+-- DROP VIEW IF EXISTS askbot_post;
+
+CREATE VIEW askbot_post AS
+
+/*
+
+Answers
+
+*/
SELECT
- answer.id + 1000000 AS id, -- fake unique ID
+ answer.id + 1000000 AS id, -- fake unique ID - has to stay consistent with Post.parent_id for answer comments (defined below) !
/* Some required pseudo-fields */
- "answer" AS post_type,
+ 'answer' AS post_type,
- joined_question.id AS parent_id,
+ NULL AS parent_id,
joined_question.thread_id AS thread_id,
answer.id AS self_answer_id,
NULL AS self_question_id,
+ NULL AS self_comment_id,
/* Shared fields from content.Content */
answer.author_id,
@@ -59,49 +70,186 @@ ON joined_question.id=answer.question_id
UNION
+/*
+
+Questions
+
+*/
+
SELECT
- question.id AS id, -- fake unique ID
+ question.id AS id, -- fake unique ID - has to stay consistent with Post.parent_id for question comments (defined below) !
/* Some required pseudo-fields */
- "question" AS post_type,
+ 'question' AS post_type,
NULL AS parent_id,
- thread_id,
+ question.thread_id,
+
+ NULL AS self_answer_id,
+ question.id AS self_question_id,
+ NULL AS self_comment_id,
+
+ /* Shared fields from content.Content */
+ question.author_id,
+ question.added_at,
+
+ question.deleted,
+ question.deleted_at,
+ question.deleted_by_id,
+
+ question.wiki,
+ question.wikified_at,
+
+ question.locked,
+ question.locked_by_id,
+ question.locked_at,
+
+ question.score,
+ question.vote_up_count,
+ question.vote_down_count,
+
+ question.comment_count,
+ question.offensive_flag_count,
+
+ question.last_edited_at,
+ question.last_edited_by_id,
+
+ question.html,
+ question.text,
+
+ question.summary,
+
+ question.is_anonymous
+
+FROM question
+
+
+UNION
+
+/*
+
+Comments to Questions
+
+*/
+
+
+SELECT
+ comment.id + 2000000 AS id, -- fake unique ID
+
+ /* Some required pseudo-fields */
+ 'comment' AS post_type,
+
+ joined_question.id AS parent_id, -- has to stay consistent with Post.is for joined_questions !!
+ joined_question.thread_id AS thread_id,
NULL AS self_answer_id,
- id AS self_question_id,
+ NULL AS self_question_id,
+ comment.id AS self_comment_id,
/* Shared fields from content.Content */
- author_id,
- added_at,
+ comment.user_id AS author_id,
+ comment.added_at,
+
+ 0 AS deleted,
+ NULL AS deleted_at,
+ NULL AS deleted_by_id,
+
+ 0 AS wiki,
+ NULL AS wikified_at,
+
+ 0 AS locked,
+ NULL AS locked_by_id,
+ NULL AS locked_at,
+
+ comment.score,
+ comment.score AS vote_up_count,
+ 0 AS vote_down_count,
+
+ 0 AS comment_count,
+ comment.offensive_flag_count,
+
+ NULL AS last_edited_at,
+ NULL AS last_edited_by_id,
+
+ comment.html,
+ NULL AS text,
+
+ '' AS summary,
+
+ 0 AS is_anonymous
+
+FROM comment
+
+INNER JOIN django_content_type AS ct
+ON ct.id=comment.content_type_id AND ct.app_label='askbot' AND ct.model='question'
+
+INNER JOIN question AS joined_question
+ON joined_question.id=comment.object_id
+
+
+UNION
+
+/*
+
+Comments to Answers
+
+*/
+
+
+SELECT
+ comment.id + 2000000 AS id, -- fake unique ID
+
+ /* Some required pseudo-fields */
+ 'comment' AS post_type,
+
+ joined_answer.id + 1000000 AS parent_id, -- has to stay consistent with Post.is for joined_questions !!
+ joined_question.thread_id AS thread_id,
+
+ NULL AS self_answer_id,
+ NULL AS self_question_id,
+ comment.id AS self_comment_id,
+
+ /* Shared fields from content.Content */
+ comment.user_id AS author_id,
+ comment.added_at,
+
+ 0 AS deleted,
+ NULL AS deleted_at,
+ NULL AS deleted_by_id,
+
+ 0 AS wiki,
+ NULL AS wikified_at,
+
+ 0 AS locked,
+ NULL AS locked_by_id,
+ NULL AS locked_at,
- deleted,
- deleted_at,
- deleted_by_id,
+ comment.score,
+ comment.score AS vote_up_count,
+ 0 AS vote_down_count,
- wiki,
- wikified_at,
+ 0 AS comment_count,
+ comment.offensive_flag_count,
- locked,
- locked_by_id,
- locked_at,
+ NULL AS last_edited_at,
+ NULL AS last_edited_by_id,
- score,
- vote_up_count,
- vote_down_count,
+ comment.html,
+ NULL AS text,
- comment_count,
- offensive_flag_count,
+ '' AS summary,
- last_edited_at,
- last_edited_by_id,
+ 0 AS is_anonymous
- html,
- text,
+FROM comment
- summary,
+INNER JOIN django_content_type AS ct
+ON ct.id=comment.content_type_id AND ct.app_label='askbot' AND ct.model='answer'
- is_anonymous
+INNER JOIN answer AS joined_answer
+ON joined_answer.id=comment.object_id
+INNER JOIN question AS joined_question
+ON joined_question.id=joined_answer.question_id
-FROM question; \ No newline at end of file
+; -- End of SQL statement
diff --git a/askbot/models/question.py b/askbot/models/question.py
index 018671bc..e4516a91 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -12,7 +12,7 @@ import askbot
import askbot.conf
from askbot.models.tag import Tag
from askbot.models.base import AnonymousContent
-from askbot.models.post import PostRevision
+from askbot.models.post import Post, PostRevision
from askbot.models.base import BaseQuerySetManager
from askbot.models import content
from askbot.models import signals
@@ -147,6 +147,9 @@ class Thread(models.Model):
def _question(self):
return Question.objects.get(thread=self)
+ def get_absolute_url(self):
+ return self._question().get_absolute_url()
+
def update_favorite_count(self):
self.favourite_count = FavoriteQuestion.objects.filter(thread=self).count()
self.save()
@@ -183,19 +186,32 @@ class Thread(models.Model):
"Creates a list of Tag names from the ``tagnames`` attribute."
return self.tagnames.split(u' ')
+ def get_title(self):
+ if self.closed:
+ attr = const.POST_STATUS['closed']
+ elif self._question().deleted:
+ attr = const.POST_STATUS['deleted']
+ else:
+ attr = None
+ if attr is not None:
+ return u'%s %s' % (self.title, attr)
+ else:
+ return self.title
+
+ def tagname_meta_generator(self):
+ return u','.join([unicode(tag) for tag in self.get_tag_names()])
+
def get_answers(self, user=None):
"""returns query set for answers to this question
that may be shown to the given user
"""
- thread_question = self._question()
-
if user is None or user.is_anonymous():
- return thread_question.answers.filter(deleted=False)
+ return self.posts.get_answers().filter(deleted=False)
else:
if user.is_administrator() or user.is_moderator():
- return thread_question.answers.all()
+ return self.posts.get_answers()
else:
- return thread_question.answers.filter(
+ return self.posts.get_answers().filter(
models.Q(deleted = False) | models.Q(author = user) \
| models.Q(deleted_by = user)
)
@@ -373,18 +389,41 @@ class Thread(models.Model):
return FavoriteQuestion.objects.filter(thread=self, user=user).exists()
def get_last_update_info(self):
- thread_question = self._question()
-
- when, who = thread_question.post_get_last_update_info()
-
- answers = thread_question.answers.all()
- for a in answers:
- a_when, a_who = a.post_get_last_update_info()
- if a_when > when:
- when = a_when
- who = a_who
-
- return when, who
+# thread_question = self._question()
+#
+# when, who = thread_question.post_get_last_update_info()
+#
+# answers = thread_question.answers.all()
+# for a in answers:
+# a_when, a_who = a.post_get_last_update_info()
+# if a_when > when:
+# when = a_when
+# who = a_who
+#
+# return when, who
+
+ # INFO: "CASE" is supported by all databases:
+ # - http://dev.mysql.com/doc/refman/5.0/en/control-flow-functions.html#operator_case
+ # - http://www.sqlite.org/lang_expr.html ("The CASE expression")
+ # - http://www.postgresql.org/docs/8.2/static/functions-conditional.html
+ # But the problem is that `extra_last_updated_at` is returned as a string which can differ depedning on the backend,
+ # version etc. so for now let's do it manually
+# posts = self.posts.extra(select={
+# 'extra_last_updated_at': 'CASE WHEN added_at > last_edited_at THEN added_at ELSE last_edited_at END',
+# 'extra_last_updated_by_id': 'CASE WHEN added_at > last_edited_at THEN author_id ELSE last_edited_by_id END',
+# }).order_by('-extra_last_updated_at')
+
+ posts = self.posts.all()
+
+ last_updated_at = posts[0].added_at
+ last_updated_by = posts[0].author
+
+ for post in posts:
+ last_updated_at, last_updated_by = max((last_updated_at, last_updated_by), (post.added_at, post.author))
+ if post.last_edited_at:
+ last_updated_at, last_updated_by = max((last_updated_at, last_updated_by), (post.last_edited_at, post.last_edited_by))
+
+ return last_updated_at, last_updated_by
class QuestionQuerySet(models.query.QuerySet):
diff --git a/askbot/skins/default/templates/user_profile/user_stats.html b/askbot/skins/default/templates/user_profile/user_stats.html
index 6e6d2008..0691dbad 100644
--- a/askbot/skins/default/templates/user_profile/user_stats.html
+++ b/askbot/skins/default/templates/user_profile/user_stats.html
@@ -19,7 +19,7 @@
{% for top_answer in top_answers %}
<div class="answer-summary">
<a title="{{ top_answer.summary|collapse }}"
- href="{% url question top_answer.parent_id %}{{ top_answer.thread.title|slugify }}#{{ top_answer.self_answer_id }}">
+ href="{% url question top_answer.question_id %}{{ top_answer.thread.title|slugify }}#{{ top_answer.self_answer_id }}">
<span class="answer-votes {% if top_answer.is_answer_accepted() %}answered-accepted{% endif %}"
title="{% trans answer_score=top_answer.score %}the answer has been voted for {{ answer_score }} times{% endtrans %} {% if top_answer.is_answer_accepted() %}{% trans %}this answer has been selected as correct{% endtrans %}{%endif%}">
{{ top_answer.score }}
@@ -27,7 +27,7 @@
</a>
<div class="answer-link">
{% spaceless %}
- <a href="{% url question top_answer.parent_id %}{{ top_answer.thread.title|slugify }}#{{top_answer.self_answer_id}}">{{ top_answer.thread.title }}</a>
+ <a href="{% url question top_answer.question_id %}{{ top_answer.thread.title|slugify }}#{{top_answer.self_answer_id}}">{{ top_answer.thread.title }}</a>
{% endspaceless %}
{% if top_answer.comment_count > 0 %}
<span>
diff --git a/askbot/tests/permission_assertion_tests.py b/askbot/tests/permission_assertion_tests.py
index 8d98cbdd..59609379 100644
--- a/askbot/tests/permission_assertion_tests.py
+++ b/askbot/tests/permission_assertion_tests.py
@@ -484,7 +484,6 @@ class ReopenQuestionPermissionAssertionTests(utils.AskbotTestCase):
class EditQuestionPermissionAssertionTests(utils.AskbotTestCase):
def setUp(self):
- super(EditQuestionPermissionAssertionTests, self).setUp()
self.create_user()
self.create_user(username = 'other_user')
self.post = self.post_question()
diff --git a/askbot/views/users.py b/askbot/views/users.py
index 93ddce92..a0a16028 100644
--- a/askbot/views/users.py
+++ b/askbot/views/users.py
@@ -299,8 +299,8 @@ def user_stats(request, user, context):
#
top_answers = user.posts.get_answers().filter(
deleted=False,
- parent__deleted=False,
- ).select_related('thread').order_by('-score', '-id')[:100]
+ self_answer__question__deleted=False,
+ ).select_related('thread', 'self_answer').order_by('-score', '-id')[:100]
top_answer_count = len(top_answers)
@@ -318,7 +318,7 @@ def user_stats(request, user, context):
# INFO: There's bug in Django that makes the following query kind of broken (GROUP BY clause is problematic):
# http://stackoverflow.com/questions/7973461/django-aggregation-does-excessive-group-by-clauses
# Fortunately it looks to return correct results for the test data
- user_tags = models.Tag.objects.filter(threads__post__author=user).\
+ user_tags = models.Tag.objects.filter(threads__posts__author=user).\
annotate(user_tag_usage_count=Count('threads')).\
order_by('-user_tag_usage_count')[:const.USER_VIEW_DATA_SIZE]
user_tags = list(user_tags) # evaluate