summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2013-07-21 00:09:14 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2013-07-21 00:09:14 -0400
commit7ab4d396ec035d5325fc6bfd3bc9edb896ab9908 (patch)
tree4743ad021ccca2a5e5fce0d981d269f786d93e8e
parent8bc6e3521e6469b174c35827ad241d5b191de3cd (diff)
downloadaskbot-7ab4d396ec035d5325fc6bfd3bc9edb896ab9908.tar.gz
askbot-7ab4d396ec035d5325fc6bfd3bc9edb896ab9908.tar.bz2
askbot-7ab4d396ec035d5325fc6bfd3bc9edb896ab9908.zip
made api more uniform
-rw-r--r--askbot/doc/source/api.rst31
-rw-r--r--askbot/models/__init__.py2
-rw-r--r--askbot/tests/__init__.py1
-rw-r--r--askbot/tests/api_v1_tests.py52
-rw-r--r--askbot/tests/page_load_tests.py45
-rw-r--r--askbot/urls.py10
-rw-r--r--askbot/views/__init__.py2
-rw-r--r--askbot/views/api.py202
-rw-r--r--askbot/views/api_v1.py199
9 files changed, 284 insertions, 260 deletions
diff --git a/askbot/doc/source/api.rst b/askbot/doc/source/api.rst
index c3a79a4d..dee2a99b 100644
--- a/askbot/doc/source/api.rst
+++ b/askbot/doc/source/api.rst
@@ -5,9 +5,28 @@ Askbot API
Askbot has API to access forum data in read-only mode.
Current version of API is 1.
-All urls start with `/api/v1` and the following endpoints are available::
-*`/api/v1/info/forum/`
-*`/api/v1/info/users/`
-*`/api/v1/info/users/<user_id>/`
-*`/api/v1/info/questions/`
-*`/api/v1/info/questions/<question_id>/`
+All data is returned in json format.
+
+All urls start with `/api/v1/` and the following endpoints are available:
+
+`/api/v1/info/`
+---------------
+
+Returns basic parameters of the site.
+
+`/api/v1/users/`
+----------------
+
+Returns, count, number of pages and basic data for each user.
+Optional parameters: page (<int>), sort (reputation|oldest|recent|username)
+
+`/api/v1/users/<user_id>/`
+--------------------------
+
+Returns basic information about a given user.
+
+`/api/v1/questions/`
+--------------------
+
+*`/api/v1/questions/`
+*`/api/v1/questions/<question_id>/`
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index 644af722..195aa1ec 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -277,7 +277,7 @@ def user_get_default_avatar_url(self, size):
"""
return skin_utils.get_media_url(askbot_settings.DEFAULT_AVATAR_URL)
-def user_get_avatar_url(self, size):
+def user_get_avatar_url(self, size=48):
"""returns avatar url - by default - gravatar,
but if application django-avatar is installed
it will use avatar provided through that app
diff --git a/askbot/tests/__init__.py b/askbot/tests/__init__.py
index 7e7e3c48..87784266 100644
--- a/askbot/tests/__init__.py
+++ b/askbot/tests/__init__.py
@@ -23,3 +23,4 @@ from askbot.tests.user_model_tests import UserModelTests
from askbot.tests.user_views_tests import *
from askbot.tests.utils_tests import *
from askbot.tests.view_context_tests import *
+from askbot.tests.api_v1_tests import *
diff --git a/askbot/tests/api_v1_tests.py b/askbot/tests/api_v1_tests.py
new file mode 100644
index 00000000..1b1417f4
--- /dev/null
+++ b/askbot/tests/api_v1_tests.py
@@ -0,0 +1,52 @@
+from askbot.tests.utils import AskbotTestCase
+from django.core.urlresolvers import reverse
+from django.utils import simplejson
+
+class ApiV1Tests(AskbotTestCase):
+ def test_api_v1_user(self):
+ user = self.create_user('apiuser')
+ response = self.client.get(reverse('api_v1_user', args=(user.id,)))
+ response_data = simplejson.loads(response.content)
+ expected_keys = set(['id', 'name', 'reputation', 'questions', 'comments',
+ 'avatar', 'joined_at', 'last_seen_at', 'answers'])
+ self.assertEqual(expected_keys, set(response_data.keys()))
+
+ def test_api_v1_info(self):
+ response = self.client.get(reverse('api_v1_info'))
+ response_data = simplejson.loads(response.content)
+ expected_keys = set(['answers', 'comments', 'users', 'groups', 'questions'])
+ self.assertEqual(expected_keys, set(response_data.keys()))
+
+ def test_api_v1_users(self):
+ self.create_user('somebody')
+ response = self.client.get(reverse('api_v1_users'))
+ response_data = simplejson.loads(response.content)
+ expected_keys = set(['pages', 'count', 'users'])
+ self.assertEqual(expected_keys, set(response_data.keys()))
+
+ expected_keys = set(['id', 'avatar', 'name',
+ 'joined_at', 'last_seen_at', 'reputation'])
+ self.assertEqual(expected_keys, set(response_data['users'][0].keys()))
+
+ def test_api_v1_questions(self):
+ user = self.create_user('user')
+ self.post_question(user=user)
+ response = self.client.get(reverse('api_v1_questions'))
+ response_data = simplejson.loads(response.content)
+ expected_keys = set(['count', 'pages', 'questions'])
+ self.assertEqual(expected_keys, set(response_data.keys()))
+
+ expected_keys = set([
+ 'id', 'view_count', 'title', 'answer_count',
+ 'last_activity_by', 'last_activity_at', 'author',
+ 'url', 'tags', 'added_at', 'score'
+ ])
+ self.assertEqual(expected_keys, set(response_data['questions'][0].keys()))
+
+ author_info = response_data['questions'][0]['author']
+ self.assertEqual(set(author_info.keys()), set(['id', 'name']))
+ self.assertEqual(set(author_info.values()), set([user.id, user.username]))
+
+ last_act_info = response_data['questions'][0]['last_activity_by']
+ self.assertEqual(set(last_act_info.keys()), set(['id', 'name']))
+ self.assertEqual(set(last_act_info.values()), set([user.id, user.username]))
diff --git a/askbot/tests/page_load_tests.py b/askbot/tests/page_load_tests.py
index 86588441..be54a1dc 100644
--- a/askbot/tests/page_load_tests.py
+++ b/askbot/tests/page_load_tests.py
@@ -189,51 +189,6 @@ class PageLoadTestCase(AskbotTestCase):
response_data = simplejson.loads(response.content)
self.assertEqual(len(response_data), 1)
- def test_api_user_info(self):
- user = self.create_user('apiuser')
- response = self.client.get(reverse('api_user_info', args=(user.id,)))
- response_data = simplejson.loads(response.content)
- expected_keys = ('username', 'reputation', 'questions', 'answers')
- for key in expected_keys:
- self.assertTrue(key in response_data.keys())
-
- def test_api_forum_info(self):
- response = self.client.get(reverse('api_forum_info'))
- response_data = simplejson.loads(response.content)
- expected_keys = ('answers', 'comments', 'users', 'questions')
- for key in expected_keys:
- self.assertTrue(key in response_data.keys())
-
- def test_api_users_info(self):
- response = self.client.get(reverse('api_users_info'))
- response_data = simplejson.loads(response.content)
- expected_keys = ('total_pages', 'count', 'user_list')
- for key in expected_keys:
- self.assertTrue(key in response_data.keys())
-
- expected_keys = ('id', 'username', 'date_joined', 'reputation')
- for key in expected_keys:
- self.assertTrue(key in response_data['user_list'][0].keys())
-
- def test_api_latest_questions(self):
- response = self.client.get(reverse('api_latest_questions'))
- response_data = simplejson.loads(response.content)
- expected_keys = ('query_data', 'question_count', 'query_string',
- 'page_size', 'non_existing_tags', 'question_list')
-
- for key in expected_keys:
- self.assertTrue(key in response_data.keys())
-
- expected_keys = ('tags', 'sort_order', 'ask_query_string')
- for key in expected_keys:
- self.assertTrue(key in response_data['query_data'].keys())
-
- expected_keys = ('id', 'view_count', 'tagnames',
- 'title', 'answer_count',
- 'last_activity_by__username')
- for key in expected_keys:
- self.assertTrue(key in response_data['question_list'][0].keys())
-
def test_ask_page_disallowed_anonymous(self):
self.proto_test_ask_page(False, 302)
diff --git a/askbot/urls.py b/askbot/urls.py
index 07b701e0..8759754c 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -573,11 +573,11 @@ urlpatterns = patterns('',
service_url('^messages/', include('group_messaging.urls')),
service_url('^settings/', include('livesettings.urls')),
- service_url('^api/v1/info/forum/$', views.api.api_forum_info, name='api_forum_info'),
- service_url('^api/v1/info/users/$', views.api.api_users_info, name='api_users_info'),
- service_url('^api/v1/info/users/(?P<user_id>\d+)/$', views.api.api_user_info, name='api_user_info'),
- service_url('^api/v1/info/questions/$', views.api.api_latest_questions, name='api_latest_questions'),
- service_url('^api/v1/info/questions/(?P<thread_id>\d+)/$', views.api.api_question_info, name='api_question_info'),
+ service_url('^api/v1/info/$', views.api_v1.info, name='api_v1_info'),
+ service_url('^api/v1/users/$', views.api_v1.users, name='api_v1_users'),
+ service_url('^api/v1/users/(?P<user_id>\d+)/$', views.api_v1.user, name='api_v1_user'),
+ service_url('^api/v1/questions/$', views.api_v1.questions, name='api_v1_questions'),
+ service_url('^api/v1/questions/(?P<question_id>\d+)/$', views.api_v1.question, name='api_v1_question'),
)
if 'askbot.deps.django_authopenid' in settings.INSTALLED_APPS:
diff --git a/askbot/views/__init__.py b/askbot/views/__init__.py
index c55ede4b..481ca8ab 100644
--- a/askbot/views/__init__.py
+++ b/askbot/views/__init__.py
@@ -8,7 +8,7 @@ from askbot.views import users
from askbot.views import meta
from askbot.views import sharing
from askbot.views import widgets
-from askbot.views import api
+from askbot.views import api_v1
from django.conf import settings
if 'avatar' in settings.INSTALLED_APPS:
from askbot.views import avatar_views
diff --git a/askbot/views/api.py b/askbot/views/api.py
deleted file mode 100644
index 601727d2..00000000
--- a/askbot/views/api.py
+++ /dev/null
@@ -1,202 +0,0 @@
-from django.core.paginator import Paginator, EmptyPage, InvalidPage
-from django.shortcuts import get_object_or_404
-from django.http import HttpResponse, Http404
-from django.contrib.auth.models import User
-from django.utils import simplejson
-from django.db.models import Q
-from django.core.urlresolvers import reverse
-
-from askbot import models
-from askbot.conf import settings as askbot_settings
-from askbot.search.state_manager import SearchState
-
-def api_forum_info(request):
- '''
- Returns general data about the forum
- '''
- data = {}
- data['answers'] = models.Post.objects.get_answers().count()
- data['questions'] = models.Post.objects.get_questions().count()
- data['comments'] = models.Post.objects.get_comments().count()
- data['users'] = User.objects.filter(is_active=True).count()
-
- if askbot_settings.GROUPS_ENABLED:
- data['groups'] = models.Group.objects.exclude_personal().count()
- else:
- data['groups'] = 0
-
- json_string = simplejson.dumps(data)
- return HttpResponse(json_string, mimetype='application/json')
-
-def api_user_info(request, user_id):
- '''
- Returns data about one user
- '''
- user = get_object_or_404(User, pk=user_id)
-
- data = {}
- data['username'] = user.username
- data['reputation'] = user.reputation
- data['questions'] = models.Post.objects.get_questions(user).count()
- data['answers'] = models.Post.objects.get_answers(user).count()
- #epoch time
- data['date_joined'] = user.date_joined.strftime('%s')
- data['last_seen'] = user.last_seen.strftime('%s')
-
- json_string = simplejson.dumps(data)
- return HttpResponse(json_string, mimetype='application/json')
-
-
-def api_users_info(request):
- '''
- Returns data of the most active or latest users.
- '''
- allowed_order_by = ('recent', 'oldest', 'reputation', 'username')
- order_by = request.GET.get('order_by', 'reputation')
-
- try:
- page = int(request.GET.get("page", '1'))
- except ValueError:
- page = 1
-
- if order_by not in allowed_order_by:
- raise Http404
- else:
- if order_by == 'reputation':
- users = models.User.objects.exclude(status = 'b').order_by('-reputation')
- elif order_by == 'oldest':
- users = models.User.objects.exclude(status = 'b').order_by('date_joined')
- elif order_by == 'recent':
- users = models.User.objects.exclude(status = 'b').order_by('-date_joined')
- elif order_by == 'username':
- users = models.User.objects.exclude(status = 'b').order_by('username')
- else:
- raise Exception("Order by method not allowed")
-
-
- paginator = Paginator(users, 10)
-
- try:
- user_objects = paginator.page(page)
- except (EmptyPage, InvalidPage):
- user_objects = paginator.page(paginator.num_pages)
-
- user_list = []
- #serializing to json
- for user in user_objects:
- user_dict = {
- 'id': user.id,
- 'username': user.username,
- 'date_joined': user.date_joined.strftime('%s'),
- 'reputation': user.reputation
- }
- user_list.append(dict.copy(user_dict))
-
- response_dict = {
- 'total_pages': paginator.num_pages,
- 'count': paginator.count,
- 'user_list': user_list
- }
- json_string = simplejson.dumps(response_dict)
-
- return HttpResponse(json_string, mimetype='application/json')
-
-
-def api_question_info(request, question_id):
- '''
- Gets a single question
- '''
- post = get_object_or_404(
- models.Post, id=question_id,
- post_type='question', deleted=False
- )
- question_url = '%s%s' % (askbot_settings.APP_URL, post.get_absolute_url())
-
- response_dict = {
- 'title': post.thread.title,
- 'text': post.text,
- 'username': post.author.username,
- 'user_id': post.author.id,
- 'url': question_url,
- }
-
- json_string = simplejson.dumps(response_dict)
-
- return HttpResponse(json_string, mimetype='application/json')
-
-
-def api_latest_questions(request):
- """
- List of Questions, Tagged questions, and Unanswered questions.
- matching search query or user selection
- """
- try:
- author_id = int(request.GET.get("author"))
- except (TypeError, ValueError):
- author_id = None
-
- try:
- page = int(request.GET.get("page"))
- except (TypeError, ValueError):
- page = None
-
- search_state = SearchState(
- scope = request.GET.get('scope', 'all'),
- sort = request.GET.get('sort', 'activity-desc'),
- query = request.GET.get("query", None),
- tags = request.GET.get("tags", None),
- author = author_id,
- page = page,
- user_logged_in=request.user.is_authenticated(),
- )
-
- page_size = int(askbot_settings.DEFAULT_QUESTIONS_PAGE_SIZE)
-
- qs, meta_data = models.Thread.objects.run_advanced_search(
- request_user=request.user, search_state=search_state
- )
- if meta_data['non_existing_tags']:
- search_state = search_state.remove_tags(meta_data['non_existing_tags'])
-
- #exludes the question from groups
- global_group = models.Group.objects.get_global_group()
- qs = qs.exclude(~Q(groups__id=global_group.id))
-
- paginator = Paginator(qs, page_size)
- if paginator.num_pages < search_state.page:
- search_state.page = 1
- page = paginator.page(search_state.page)
-
- question_list = list(page.object_list.values('title', 'view_count',
- 'tagnames',
- 'id',
- 'last_activity_by__username',
- 'answer_count'))
-
- #adds urls
- for i, question in enumerate(question_list):
- question['url'] = '%s%s' % (askbot_settings.APP_URL,
- reverse('question',
- kwargs={'id': question['id']})
- )
- question_list[i] = question
-
- page.object_list = list(page.object_list) # evaluate the queryset
-
- models.Thread.objects.precache_view_data_hack(threads=page.object_list)
-
- ajax_data = {
- 'query_data': {
- 'tags': search_state.tags,
- 'sort_order': search_state.sort,
- 'ask_query_string': search_state.ask_query_string(),
- },
- 'question_count': paginator.count,
- 'query_string': request.META.get('QUERY_STRING', ''),
- 'page_size' : page_size,
- 'non_existing_tags': meta_data['non_existing_tags'],
- 'question_list': question_list
- }
-
- return HttpResponse(simplejson.dumps(ajax_data),
- mimetype = 'application/json')
diff --git a/askbot/views/api_v1.py b/askbot/views/api_v1.py
new file mode 100644
index 00000000..1141bd30
--- /dev/null
+++ b/askbot/views/api_v1.py
@@ -0,0 +1,199 @@
+from django.core.paginator import Paginator, EmptyPage, InvalidPage
+from django.shortcuts import get_object_or_404
+from django.http import HttpResponse, Http404
+from django.contrib.auth.models import User
+from django.utils import simplejson
+from django.db.models import Q
+from django.core.urlresolvers import reverse
+from askbot import models
+from askbot.conf import settings as askbot_settings
+from askbot.search.state_manager import SearchState
+from askbot.utils.html import site_url
+
+def get_user_data(user):
+ """get common data about the user"""
+ avatar_url = user.get_avatar_url()
+ if not ('gravatar.com' in avatar_url):
+ avatar_url = site_url(avatar_url)
+
+ return {
+ 'id': user.id,
+ 'avatar': avatar_url,
+ 'name': user.username,
+ 'joined_at': user.date_joined.strftime('%s'),
+ 'last_seen_at': user.last_seen.strftime('%s'),
+ 'reputation': user.reputation,
+ }
+
+def get_question_data(thread):
+ """returns data dictionary for a given thread"""
+ datum = {
+ 'added_at': thread.added_at.strftime('%s'),
+ 'id': thread._question_post().id,
+ 'answer_count': thread.answer_count,
+ 'view_count': thread.view_count,
+ 'score': thread.score,
+ 'last_activity_at': thread.last_activity_at.strftime('%s'),
+ 'title': thread.title,
+ 'tags': thread.tagnames.strip().split(),
+ 'url': site_url(thread.get_absolute_url()),
+ }
+ datum['author'] = {
+ 'id': thread._question_post().author.id,
+ 'name': thread._question_post().author.username
+ }
+ datum['last_activity_by'] = {
+ 'id': thread.last_activity_by.id,
+ 'name': thread.last_activity_by.username
+ }
+ return datum
+
+def info(request):
+ '''
+ Returns general data about the forum
+ '''
+ data = {}
+ data['answers'] = models.Post.objects.get_answers().count()
+ data['questions'] = models.Post.objects.get_questions().count()
+ data['comments'] = models.Post.objects.get_comments().count()
+ data['users'] = User.objects.filter(is_active=True).count()
+
+ if askbot_settings.GROUPS_ENABLED:
+ data['groups'] = models.Group.objects.exclude_personal().count()
+ else:
+ data['groups'] = 0
+
+ json_string = simplejson.dumps(data)
+ return HttpResponse(json_string, mimetype='application/json')
+
+def user(request, user_id):
+ '''
+ Returns data about one user
+ '''
+ user = get_object_or_404(User, pk=user_id)
+ data = get_user_data(user)
+ data['questions'] = models.Post.objects.get_questions(user).count()
+ data['answers'] = models.Post.objects.get_answers(user).count()
+ data['comments'] = models.Post.objects.filter(post_type='comment').count()
+ json_string = simplejson.dumps(data)
+ return HttpResponse(json_string, mimetype='application/json')
+
+
+def users(request):
+ '''
+ Returns data of the most active or latest users.
+ '''
+ allowed_order_by = ('recent', 'oldest', 'reputation', 'username')
+ order_by = request.GET.get('sort', 'reputation')
+
+ try:
+ page = int(request.GET.get("page", '1'))
+ except ValueError:
+ page = 1
+
+ if order_by not in allowed_order_by:
+ raise Http404
+ else:
+ if order_by == 'reputation':
+ users = models.User.objects.exclude(status='b').order_by('-reputation')
+ elif order_by == 'oldest':
+ users = models.User.objects.exclude(status='b').order_by('date_joined')
+ elif order_by == 'recent':
+ users = models.User.objects.exclude(status='b').order_by('-date_joined')
+ elif order_by == 'username':
+ users = models.User.objects.exclude(status='b').order_by('username')
+ else:
+ raise Exception("Order by method not allowed")
+
+
+ paginator = Paginator(users, 10)
+
+ try:
+ user_objects = paginator.page(page)
+ except (EmptyPage, InvalidPage):
+ user_objects = paginator.page(paginator.num_pages)
+
+ user_list = []
+ #serializing to json
+ for user in user_objects:
+ user_dict = get_user_data(user)
+ user_list.append(dict.copy(user_dict))
+
+ response_dict = {
+ 'pages': paginator.num_pages,
+ 'count': paginator.count,
+ 'users': user_list
+ }
+ json_string = simplejson.dumps(response_dict)
+
+ return HttpResponse(json_string, mimetype='application/json')
+
+
+def question(request, question_id):
+ '''
+ Gets a single question
+ '''
+ #we retrieve question by post id, b/c that's what is in the url,
+ #not thread id (currently)
+ post = get_object_or_404(
+ models.Post, id=question_id,
+ post_type='question', deleted=False
+ )
+ datum = get_question_data(post.thread)
+ json_string = simplejson.dumps(datum)
+ return HttpResponse(json_string, mimetype='application/json')
+
+
+def questions(request):
+ """
+ List of Questions, Tagged questions, and Unanswered questions.
+ matching search query or user selection
+ """
+ try:
+ author_id = int(request.GET.get("author"))
+ except (ValueError, TypeError):
+ author_id = None
+
+ try:
+ page = int(request.GET.get("page"))
+ except (ValueError, TypeError):
+ page = None
+
+ search_state = SearchState(
+ scope=request.GET.get('scope', 'all'),
+ sort=request.GET.get('sort', 'activity-desc'),
+ query=request.GET.get('query', None),
+ tags=request.GET.get('tags', None),
+ author=author_id,
+ page=page,
+ user_logged_in=request.user.is_authenticated(),
+ )
+
+ qs, meta_data = models.Thread.objects.run_advanced_search(
+ request_user=request.user, search_state=search_state
+ )
+ if meta_data['non_existing_tags']:
+ search_state = search_state.remove_tags(meta_data['non_existing_tags'])
+
+ #exludes the question from groups
+ #global_group = models.Group.objects.get_global_group()
+ #qs = qs.exclude(~Q(groups__id=global_group.id))
+
+ page_size = askbot_settings.DEFAULT_QUESTIONS_PAGE_SIZE
+ paginator = Paginator(qs, page_size)
+ if paginator.num_pages < search_state.page:
+ search_state.page = 1
+ page = paginator.page(search_state.page)
+
+ question_list = list()
+ for thread in page.object_list:
+ datum = get_question_data(thread)
+ question_list.append(datum)
+
+ ajax_data = {
+ 'count': paginator.count,
+ 'pages' : paginator.num_pages,
+ 'questions': question_list
+ }
+ response_data = simplejson.dumps(ajax_data)
+ return HttpResponse(response_data, mimetype='application/json')