Permalink
Browse files

[bug 956836] Replace tastypie with django-rest-framework.

KPI app was using tastypie. Now it uses DRF.
  • Loading branch information...
1 parent 35511d5 commit 747f8feb490967d755161cd315d46f9e038fa898 @rlr rlr committed Jan 8, 2014
View
2 kitsune/dashboards/templates/dashboards/locale_metrics.html
@@ -35,7 +35,7 @@
</div>
</section>
- <section id="kpi-vote" data-url="{{ url('api_dispatch_list', resource_name='kpi_kb_vote', api_name='v1')|urlparams(locale=current_locale) }}">
+ <section id="kpi-vote" data-url="{{ url('api.kpi.kb-votes')|urlparams(locale=current_locale) }}">
<h2>{{ _('Helpful Votes') }}</h2>
<div class="rickshaw">
<div class="inline-controls"></div>
View
255 kitsune/kpi/api.py
@@ -1,14 +1,12 @@
from operator import itemgetter
from datetime import date, timedelta
+from django.core.cache import cache
from django.db import connections, router
from django.db.models import Count, F
-from tastypie import fields
-from tastypie.authentication import BasicAuthentication
-from tastypie.authorization import Authorization
-from tastypie.cache import SimpleCache
-from tastypie.resources import Resource
+from rest_framework.views import APIView
+from rest_framework.response import Response
from kitsune.kpi.models import (
Metric, MetricKind, AOA_CONTRIBUTORS_METRIC_CODE,
@@ -20,79 +18,41 @@
from kitsune.wiki.models import HelpfulVote
-class CachedResource(Resource):
- def obj_get_list(self, request=None, **kwargs):
- """Override ``obj_get_list`` to use the cache."""
- if request:
- # Add request query params to cache key.
- kwargs.update(**request.GET.dict())
- cache_key = self.generate_cache_key('list', **kwargs)
- obj_list = self._meta.cache.get(cache_key)
-
- if obj_list is None:
- obj_list = self.get_object_list(request)
- self._meta.cache.set(cache_key, obj_list, timeout=60 * 60 * 3)
-
- return obj_list
-
-
-class WriteOnlyBasicAuthentication(BasicAuthentication):
- """Authenticator that prompts for credentials only for write requests."""
- def is_authenticated(self, request, **kwargs):
- if request.method == 'GET':
- return True
- return (super(WriteOnlyBasicAuthentication, self)
- .is_authenticated(request, **kwargs))
-
-
-class PermissionAuthorization(Authorization):
- """Authorization which allows all users to make read-only requests and
- users with a certain permission to write."""
-
- def __init__(self, write=None):
- self.write_perm = write
-
- def is_authorized(self, request, object=None):
- # Supports just GET and POST so far
- if request.method == 'GET':
- return True
- elif request.method == 'POST':
- return request.user.has_perm(self.write_perm)
- return False
+class CachedAPIView(APIView):
+ """An APIView that caches the objects to be returned.
+ Subclasses must implement the get_objects() method.
+ """
-class Struct(object):
- """Convert a dict to an object"""
- def __init__(self, **entries):
- self.__dict__.update(entries)
+ def _cache_key(self, request):
+ params = []
+ for key, value in request.GET.items():
+ params.append("%s=%s" % (key, value))
+ return u'{viewname}:{params}'.format(
+ viewname=self.__class__.__name__,
+ params=u':'.join(sorted(params)))
- def __unicode__(self):
- return unicode(self.__dict__)
+ def get(self, request):
+ cache_key = self._cache_key(request)
+ objs = cache.get(cache_key)
+ if objs is None:
+ objs = self.get_objects(request)
+ cache.add(cache_key, objs, 60 * 60 * 3)
-class SearchClickthroughMeta(object):
- """Abstract Meta inner class for search clickthrough resources"""
- # The Resource metaclass isn't smart enough to merge in attributes from
- # Meta classes defined in subclasses of an abstract resource, like Django's
- # ORM metaclass is. There's might be a supported way, but I'm really sick
- # of reading tastypie docs (and source).
- object_class = Struct
- authentication = WriteOnlyBasicAuthentication()
- authorization = PermissionAuthorization(write='kpi.add_metric')
+ return Response({
+ 'objects': objs
+ })
+ def get_objects(self, request):
+ """Returns a list of dicts the API view will return."""
+ raise NotImplementedError('Must be overriden in subclass')
-class SearchClickthroughResource(CachedResource):
- """Clickthrough ratio for searches over one period
- Represents a ratio of {clicks of results}/{total searches} for one engine.
+class SearchClickthroughMetricList(CachedAPIView):
+ """The API list view for search click-through rate metrics."""
- """
- #: Date of period start. Assumes 1-day periods.
- start = fields.DateField('start')
- #: How many searches had (at least?) 1 result clicked
- clicks = fields.IntegerField('clicks', default=0)
- #: How many searches were performed with this engine
- searches = fields.IntegerField('searches', default=0)
+ engine = 'elastic'
@property
def searches_kind(self):
@@ -102,11 +62,7 @@ def searches_kind(self):
def clicks_kind(self):
return 'search clickthroughs:%s:clicks' % self.engine
- def obj_get(self, request=None, **kwargs):
- """Fetch a particular ratio by start date."""
- raise NotImplementedError
-
- def get_object_list(self, request=None, **kwargs):
+ def get_objects(self, request):
"""Return all the ratios.
Or, if a ``min_start`` query param is present, return the (potentially
@@ -138,52 +94,21 @@ def get_object_list(self, request=None, **kwargs):
if min_start:
args.append(min_start)
cursor.execute(query, args)
- return [Struct(start=s, clicks=n, searches=d) for
+ return [dict(start=s, clicks=n, searches=d) for
s, n, d in reversed(cursor.fetchall())]
- def obj_create(self, bundle, request=None, **kwargs):
- def create_metric(kind, value_field, data):
- """Given POSTed data, create a Metric.
-
- Assume day-long buckets for the moment.
- """
- start = date(*_parse_date(data['start']))
- Metric.objects.create(kind=MetricKind.objects.get(code=kind),
- start=start,
- end=start + timedelta(days=1),
- value=data[value_field])
-
- create_metric(self.searches_kind, 'searches', bundle.data)
- create_metric(self.clicks_kind, 'clicks', bundle.data)
-
- def get_resource_uri(self, bundle_or_obj):
- """Return a fake answer; we don't care, for now."""
- return ''
-
-
-class ElasticClickthroughResource(SearchClickthroughResource):
- engine = 'elastic'
-
- class Meta(SearchClickthroughMeta):
- resource_name = 'elastic-clickthrough-rate'
-
-
-class QuestionsResource(CachedResource):
- """Returns metrics related to questions.
+class QuestionsMetricList(CachedAPIView):
+ """The API list view for support forum metrics.
* Number of questions asked
+ * Number of questions responded to within 24 hours
* Number of questions responded to within 72 hours
* Number of questions solved
"""
- date = fields.DateField('date')
- questions = fields.IntegerField('questions', default=0)
- responded_72 = fields.IntegerField('responded_72', default=0)
- responded_24 = fields.IntegerField('responded_24', default=0)
- solved = fields.IntegerField('solved', default=0)
-
- def get_object_list(self, request):
+ def get_objects(self, request):
+ # Set up the queries for the data we need
locale = request.GET.get('locale')
# Set up the query for the data we need.
@@ -216,21 +141,11 @@ def get_object_list(self, request):
responded_72=rs_72,
responded_24=rs_24)
- class Meta(object):
- cache = SimpleCache()
- resource_name = 'kpi_questions'
- allowed_methods = ['get']
+class VoteMetricList(CachedAPIView):
+ """The API list view for vote metrics."""
-class VoteResource(CachedResource):
- """Returns the number helpful votes for Articles and Answers."""
- date = fields.DateField('date')
- kb_helpful = fields.IntegerField('kb_helpful', default=0)
- kb_votes = fields.IntegerField('kb_votes', default=0)
- ans_helpful = fields.IntegerField('ans_helpful', default=0)
- ans_votes = fields.IntegerField('ans_votes', default=0)
-
- def get_object_list(self, request):
+ def get_objects(self, request):
# Set up the queries for the data we need
qs_kb_votes = _qs_for(HelpfulVote)
qs_ans_votes = _qs_for(AnswerVote)
@@ -245,21 +160,12 @@ def get_object_list(self, request):
ans_votes=qs_ans_votes,
ans_helpful=qs_ans_helpful_votes)
- class Meta(object):
- cache = SimpleCache()
- resource_name = 'kpi_vote'
- allowed_methods = ['get']
-
-class KBVoteResource(CachedResource):
- """Returns the number helpful votes for KB Articles."""
- date = fields.DateField('date')
- kb_helpful = fields.IntegerField('kb_helpful', default=0)
- kb_votes = fields.IntegerField('kb_votes', default=0)
+class KBVoteMetricList(CachedAPIView):
+ """The API list view for KB vote metrics."""
- def get_object_list(self, request):
+ def get_objects(self, request):
# Set up the queries for the data we need
-
locale = request.GET.get('locale')
qs_kb_votes = HelpfulVote.objects.filter(
@@ -285,27 +191,17 @@ def get_object_list(self, request):
return merge_results(
kb_votes=qs_kb_votes, kb_helpful=qs_kb_helpful_votes)
- class Meta(object):
- cache = SimpleCache()
- resource_name = 'kpi_kb_vote'
- allowed_methods = ['get']
-
-class ActiveContributorsResource(CachedResource):
- """Returns the number of active contributors.
+class ContributorsMetricList(CachedAPIView):
+ """The API list view for active contributor metrics.
* en-US KB contributors
* non-en-US contributors
* Support Forum contributors
* Army of Awesome contributors
"""
- date = fields.DateField('date')
- en_us = fields.IntegerField('en_us', default=0)
- non_en_us = fields.IntegerField('non_en_us', default=0)
- support_forum = fields.IntegerField('support_forum', default=0)
- aoa = fields.IntegerField('aoa', default=0)
- def get_object_list(self, request):
+ def get_objects(self, request):
# Set up the queries for the data we need
kind = MetricKind.objects.get(code=KB_ENUS_CONTRIBUTORS_METRIC_CODE)
en_us = Metric.objects.filter(kind=kind).order_by('-start')
@@ -335,64 +231,36 @@ def merge_results(metrics_qs, label):
# Convert that to a list of dicts.
results_list = [dict(date=k, **v) for k, v in results_dict.items()]
- return [Struct(**x) for x in sorted(
+ return [dict(**x) for x in sorted(
results_list, key=itemgetter('date'), reverse=True)]
- class Meta:
- cache = SimpleCache()
- resource_name = 'kpi_active_contributors'
- allowed_methods = ['get']
+class VisitorsMetricList(CachedAPIView):
+ """The API list view for visitor metrics."""
-class VisitorsResource(CachedResource):
- """Returns the number of unique visitors per day."""
- date = fields.DateField('date')
- visitors = fields.IntegerField('visitors', default=0)
-
- def get_object_list(self, request):
+ def get_objects(self, request):
# Set up the query for the data we need
kind = MetricKind.objects.get(code=VISITORS_METRIC_CODE)
qs = Metric.objects.filter(kind=kind).order_by('-start')
- return [Struct(date=m.start, visitors=m.value) for m in qs]
-
- class Meta(object):
- cache = SimpleCache()
- resource_name = 'kpi_visitors'
- allowed_methods = ['get']
+ return [dict(date=m.start, visitors=m.value) for m in qs]
-class L10nCoverageResource(CachedResource):
- """Returns the L10n coverage per day."""
- date = fields.DateField('date')
- coverage = fields.IntegerField('coverage', default=0)
+class L10nCoverageMetricList(CachedAPIView):
+ """The API list view for L10n coverage metrics."""
- def get_object_list(self, request):
+ def get_objects(self, request):
# Set up the query for the data we need
kind = MetricKind.objects.get(code=L10N_METRIC_CODE)
qs = Metric.objects.filter(kind=kind).order_by('-start')
- return [Struct(date=m.start, coverage=m.value) for m in qs]
+ return [dict(date=m.start, coverage=m.value) for m in qs]
- class Meta(object):
- cache = SimpleCache()
- resource_name = 'kpi_l10n_coverage'
- allowed_methods = ['get']
+class ExitSurveyMetricList(CachedAPIView):
+ """The API list view for exit survey metrics."""
-class ExitSurveyResultsResource(CachedResource):
- """Returns the results of the exit survey
-
- * Number of "Yes" answers
- * Number of "No" answers
- * Number of "I don't know" answers
- """
- date = fields.DateField('date')
- yes = fields.IntegerField('yes', default=0)
- no = fields.IntegerField('no', default=0)
- dont_know = fields.IntegerField('dont_know', default=0)
-
- def get_object_list(self, request):
+ def get_objects(self, request):
# Set up the queries for the data we need
kind = MetricKind.objects.get(code=EXIT_SURVEY_YES_CODE)
yes = Metric.objects.filter(kind=kind).order_by('-start')
@@ -417,14 +285,9 @@ def merge_results(metrics_qs, label):
# Convert that to a list of dicts.
results_list = [dict(date=k, **v) for k, v in results_dict.items()]
- return [Struct(**x) for x in sorted(
+ return [dict(**x) for x in sorted(
results_list, key=itemgetter('date'), reverse=True)]
- class Meta:
- cache = SimpleCache()
- resource_name = 'kpi_exit_survey_results'
- allowed_methods = ['get']
-
def _daily_qs_for(model_cls):
"""Return the daily grouped queryset we need for model_cls."""
@@ -481,7 +344,7 @@ def _remap_date_counts(**kwargs):
def merge_results(**kwargs):
res_dict = reduce(_merge_results, _remap_date_counts(**kwargs))
res_list = [dict(date=k, **v) for k, v in res_dict.items()]
- return [Struct(**x)
+ return [dict(**x)
for x in sorted(res_list, key=itemgetter('date'), reverse=True)]
View
16 kitsune/kpi/templates/kpi/dashboard.html
@@ -10,7 +10,7 @@
<article id="kpi-dash-rickshaw">
<section id="kpi-questions"
- data-url="{{ url('api_dispatch_list', resource_name='kpi_questions', api_name='v1') }}">
+ data-url="{{ url('api.kpi.questions') }}">
<h2>{{ _('Questions') }}</h2>
@@ -43,7 +43,7 @@
</section>
<section id="kpi-vote"
- data-url="{{ url('api_dispatch_list', resource_name='kpi_vote', api_name='v1') }}">
+ data-url="{{ url('api.kpi.votes') }}">
<h2>{{ _('Helpful Votes') }}</h2>
@@ -69,7 +69,7 @@
</section>
<section id="kpi-active-contributors"
- data-url="{{ url('api_dispatch_list', resource_name='kpi_active_contributors', api_name='v1') }}">
+ data-url="{{ url('api.kpi.contributors') }}">
<h2>{{ _('Active Contributors') }}</h2>
@@ -136,7 +136,7 @@
</section>
<section id="kpi-ctr"
- data-url="{{ url('api_dispatch_list', resource_name='elastic-clickthrough-rate', api_name='v1') }}">
+ data-url="{{ url('api.kpi.search-ctr') }}">
<h2>{{ _('Search Clickthrough Rate') }}</h2>
@@ -162,7 +162,7 @@
</section>
<section id="kpi-visitors"
- data-url="{{ url('api_dispatch_list', resource_name='kpi_visitors', api_name='v1') }}">
+ data-url="{{ url('api.kpi.visitors') }}">
<h2>{{ _('Daily Unique Visitors') }}</h2>
@@ -176,7 +176,7 @@
</section>
<section id="kpi-l10n"
- data-url="{{ url('api_dispatch_list', resource_name='kpi_l10n_coverage', api_name='v1') }}">
+ data-url="{{ url('api.kpi.l10n-coverage') }}">
<h2>{{ _('L10n Coverage') }}</h2>
@@ -206,7 +206,7 @@
<li>{% trans %}A level 3 review change drops the localization to 0% of the article{% endtrans %}</li>
<li>{% trans %}More than one level 2 review change drops the localization to 0%{% endtrans %}</li>
</ul>
-
+
<p>
{% trans %}Example:{% endtrans %}
</p>
@@ -222,7 +222,7 @@
</section>
<section id="exit-survey"
- data-url="{{ url('api_dispatch_list', resource_name='kpi_exit_survey_results', api_name='v1') }}">
+ data-url="{{ url('api.kpi.exit-survey') }}">
<h2>{{ _('Exit Survey Results') }}</h2>
View
93 kitsune/kpi/tests/test_api.py
@@ -1,4 +1,3 @@
-from base64 import b64encode
from datetime import date, datetime, timedelta
import json
@@ -15,16 +14,14 @@
EXIT_SURVEY_YES_CODE, EXIT_SURVEY_NO_CODE, EXIT_SURVEY_DONT_KNOW_CODE)
from kitsune.kpi.tests import metric, metric_kind
from kitsune.sumo.helpers import urlparams
-from kitsune.sumo.tests import TestCase, LocalizingClient
+from kitsune.sumo.tests import TestCase
from kitsune.sumo.urlresolvers import reverse
from kitsune.questions.tests import answer, answervote, question
-from kitsune.users.tests import user, add_permission
+from kitsune.users.tests import user
from kitsune.wiki.tests import document, revision, helpful_vote
class KpiApiTests(TestCase):
- client_class = LocalizingClient
-
def _make_elastic_metric_kinds(self):
click_kind = metric_kind(code='search clickthroughs:elastic:clicks',
save=True)
@@ -38,11 +35,9 @@ def _make_contributor_metric_kinds(self):
metric_kind(code=KB_L10N_CONTRIBUTORS_METRIC_CODE, save=True)
metric_kind(code=SUPPORT_FORUM_CONTRIBUTORS_METRIC_CODE, save=True)
- def _get_api_result(self, resource_name, **kwargs):
+ def _get_api_result(self, name, **kwargs):
"""Helper to make API calls, parse the json and return the result."""
- url = reverse('api_dispatch_list',
- kwargs={'resource_name': resource_name,
- 'api_name': 'v1'})
+ url = reverse(name)
url = urlparams(url, format='json', **kwargs)
response = self.client.get(url)
eq_(200, response.status_code)
@@ -61,7 +56,7 @@ def test_questions(self):
# A locked question that shouldn't be counted for anything
question(is_locked=True, save=True)
- r = self._get_api_result('kpi_questions')
+ r = self._get_api_result('api.kpi.questions')
eq_(r['objects'][0]['solved'], 1)
eq_(r['objects'][0]['responded_24'], 2)
eq_(r['objects'][0]['responded_72'], 2)
@@ -84,25 +79,25 @@ def test_questions_by_locale(self):
question(locale='pt-BR', save=True)
# Verify no locale filtering:
- r = self._get_api_result('kpi_questions')
+ r = self._get_api_result('api.kpi.questions')
eq_(r['objects'][0]['solved'], 1)
eq_(r['objects'][0]['responded_24'], 2)
eq_(r['objects'][0]['responded_72'], 2)
eq_(r['objects'][0]['questions'], 4)
# Verify locale=en-US
- r = self._get_api_result('kpi_questions', locale='en-US')
+ r = self._get_api_result('api.kpi.questions', locale='en-US')
eq_(r['objects'][0]['solved'], 1)
eq_(r['objects'][0]['responded_24'], 2)
eq_(r['objects'][0]['responded_72'], 2)
eq_(r['objects'][0]['questions'], 3)
# Verify locale=pt-BR
- r = self._get_api_result('kpi_questions', locale='pt-BR')
- eq_(r['objects'][0]['solved'], 0)
- eq_(r['objects'][0]['responded_24'], 0)
- eq_(r['objects'][0]['responded_72'], 0)
+ r = self._get_api_result('api.kpi.questions', locale='pt-BR')
eq_(r['objects'][0]['questions'], 1)
+ assert 'solved' not in r['objects'][0]
+ assert 'responded_24' not in r['objects'][0]
+ assert 'responded_72' not in r['objects'][0]
def test_questions_inactive_user(self):
"""Verify questions from inactive users aren't counted."""
@@ -112,17 +107,15 @@ def test_questions_inactive_user(self):
question(creator=u, save=True)
question(creator=u, save=True)
- r = self._get_api_result('kpi_questions')
+ r = self._get_api_result('api.kpi.questions')
eq_(len(r['objects']), 0)
# Activate the user, now the questions should count.
u.is_active = True
u.save()
cache.clear() # We need to clear the cache for new results.
- url = reverse('api_dispatch_list',
- kwargs={'resource_name': 'kpi_questions',
- 'api_name': 'v1'})
+ url = reverse('api.kpi.questions')
response = self.client.get(url + '?format=json')
eq_(200, response.status_code)
r = json.loads(response.content)
@@ -140,7 +133,7 @@ def test_vote(self):
answervote(answer=a, helpful=True, save=True)
answervote(answer=a, helpful=True, save=True)
- r = self._get_api_result('kpi_vote')
+ r = self._get_api_result('api.kpi.votes')
eq_(r['objects'][0]['kb_helpful'], 1)
eq_(r['objects'][0]['kb_votes'], 3)
eq_(r['objects'][0]['ans_helpful'], 2)
@@ -157,17 +150,17 @@ def test_kb_vote(self):
helpful_vote(revision=r, helpful=True, save=True)
# All votes should be counted if we don't specify a locale
- r = self._get_api_result('kpi_kb_vote')
+ r = self._get_api_result('api.kpi.kb-votes')
eq_(r['objects'][0]['kb_helpful'], 3)
eq_(r['objects'][0]['kb_votes'], 9)
# Only en-US votes:
- r = self._get_api_result('kpi_kb_vote', locale='en-US')
+ r = self._get_api_result('api.kpi.kb-votes', locale='en-US')
eq_(r['objects'][0]['kb_helpful'], 1)
eq_(r['objects'][0]['kb_votes'], 3)
# Only es votes:
- r = self._get_api_result('kpi_kb_vote', locale='es')
+ r = self._get_api_result('api.kpi.kb-votes', locale='es')
eq_(r['objects'][0]['kb_helpful'], 2)
eq_(r['objects'][0]['kb_votes'], 6)
@@ -201,7 +194,7 @@ def test_active_contributors(self):
self._make_contributor_metric_kinds()
update_contributor_metrics(day=date.today() + timedelta(days=1))
- r = self._get_api_result('kpi_active_contributors')
+ r = self._get_api_result('api.kpi.contributors')
eq_(r['objects'][0]['en_us'], 2)
eq_(r['objects'][0]['non_en_us'], 2)
@@ -225,7 +218,7 @@ def test_asker_replies_arent_a_contribution(self):
self._make_contributor_metric_kinds()
update_contributor_metrics(day=date.today() + timedelta(days=1))
- r = self._get_api_result('kpi_active_contributors')
+ r = self._get_api_result('api.kpi.contributors')
eq_(r['objects'][0]['support_forum'], 0)
# Change the question creator, now we should have 1 contributor.
@@ -236,7 +229,7 @@ def test_asker_replies_arent_a_contribution(self):
Metric.objects.all().delete()
update_contributor_metrics(day=date.today() + timedelta(days=1))
- r = self._get_api_result('kpi_active_contributors')
+ r = self._get_api_result('api.kpi.contributors')
eq_(r['objects'][0]['support_forum'], 1)
def test_elastic_clickthrough_get(self):
@@ -259,52 +252,20 @@ def test_elastic_clickthrough_get(self):
value=20,
save=True)
- url = reverse('api_dispatch_list',
- kwargs={'resource_name': 'elastic-clickthrough-rate',
- 'api_name': 'v1'})
+ url = reverse('api.kpi.search-ctr')
response = self.client.get(url + '?format=json')
data = json.loads(response.content)
eq_(data['objects'], [
- {'clicks': 2, 'resource_uri': u'', 'searches': 20,
+ {'clicks': 2, 'searches': 20,
'start': u'2000-01-09'},
- {'clicks': 1, 'resource_uri': u'', 'searches': 10,
+ {'clicks': 1, 'searches': 10,
'start': u'2000-01-01'}])
# Test filtering by start date:
response = self.client.get(url + '?format=json&min_start=2000-01-09')
data = json.loads(response.content)
eq_(data['objects'], [{u'searches': 20, u'start': u'2000-01-09',
- u'clicks': 2, u'resource_uri': u''}])
-
- def test_elastic_clickthrough_post(self):
- """Test elastic clickthrough write API."""
- u = user(save=True)
- add_permission(u, Metric, 'add_metric')
-
- click_kind, search_kind = self._make_elastic_metric_kinds()
-
- # POST the new object:
- url = reverse('api_dispatch_list',
- kwargs={'resource_name': 'elastic-clickthrough-rate',
- 'api_name': 'v1'})
- auth = 'Basic ' + b64encode('%s:%s' % (u.username, 'testpass'))
- response = self.client.post(url,
- json.dumps({'start': '2000-01-02',
- 'searches': 1e8,
- 'clicks': 5e7}),
- content_type='application/json',
- HTTP_AUTHORIZATION=auth)
- eq_(response.status_code, 201)
-
- # Do a GET, and see if the round trip worked:
- response = self.client.get(url + '?format=json')
- data = json.loads(response.content)
- eq_(data['objects'], [
- {'clicks': 50000000, 'resource_uri': u'', 'searches': 100000000,
- 'start': '2000-01-02'}])
-
- # Correspnding ElasticSearch APIs are likely correct by dint
- # of factoring.
+ u'clicks': 2}])
def test_visitors(self):
"""Test unique visitors API call."""
@@ -314,7 +275,7 @@ def test_visitors(self):
save=True)
# There should be 42 visitors.
- r = self._get_api_result('kpi_visitors')
+ r = self._get_api_result('api.kpi.visitors')
eq_(r['objects'][0]['visitors'], 42)
def test_l10n_coverage(self):
@@ -325,7 +286,7 @@ def test_l10n_coverage(self):
save=True)
# The l10n coverage should be 56%.
- r = self._get_api_result('kpi_l10n_coverage')
+ r = self._get_api_result('api.kpi.l10n-coverage')
eq_(r['objects'][0]['coverage'], 56)
def test_exit_survey_results(self):
@@ -342,7 +303,7 @@ def test_exit_survey_results(self):
save=True)
# Verify the results returned from the API
- r = self._get_api_result('kpi_exit_survey_results')
+ r = self._get_api_result('api.kpi.exit-survey')
eq_(r['objects'][0]['yes'], 1337)
eq_(r['objects'][0]['no'], 42)
eq_(r['objects'][0]['dont_know'], 777)
View
24 kitsune/kpi/urls.py
@@ -1,27 +1,7 @@
-from django.conf.urls import patterns, url, include
-
-from tastypie.api import Api
-
-from kitsune.kpi.api import (
- QuestionsResource, VoteResource, ActiveContributorsResource,
- ElasticClickthroughResource, VisitorsResource, L10nCoverageResource,
- KBVoteResource, ExitSurveyResultsResource)
-
-
-v1_api = Api(api_name='v1')
-v1_api.register(QuestionsResource())
-v1_api.register(VoteResource())
-v1_api.register(KBVoteResource())
-v1_api.register(ActiveContributorsResource())
-v1_api.register(ElasticClickthroughResource())
-v1_api.register(VisitorsResource())
-v1_api.register(L10nCoverageResource())
-v1_api.register(ExitSurveyResultsResource())
+from django.conf.urls import patterns, url
urlpatterns = patterns(
'kitsune.kpi.views',
- url(r'^dashboard$', 'dashboard',
- name='kpi.dashboard'),
- (r'^api/', include(v1_api.urls)),
+ url(r'^dashboard$', 'dashboard', name='kpi.dashboard'),
)
View
24 kitsune/kpi/urls_api.py
@@ -0,0 +1,24 @@
+from django.conf.urls import patterns, url
+
+from kitsune.kpi import api
+
+urlpatterns = patterns(
+ '',
+ url(r'^api/v1/kpi/l10n-coverage/?$', api.L10nCoverageMetricList.as_view(),
+ name='api.kpi.l10n-coverage'),
+ url(r'^api/v1/kpi/exit-survey/?$', api.ExitSurveyMetricList.as_view(),
+ name='api.kpi.exit-survey'),
+ url(r'^api/v1/kpi/visitors/?$', api.VisitorsMetricList.as_view(),
+ name='api.kpi.visitors'),
+ url(r'^api/v1/kpi/contributors/?$', api.ContributorsMetricList.as_view(),
+ name='api.kpi.contributors'),
+ url(r'^api/v1/kpi/kb-votes/?$', api.KBVoteMetricList.as_view(),
+ name='api.kpi.kb-votes'),
+ url(r'^api/v1/kpi/votes/?$', api.VoteMetricList.as_view(),
+ name='api.kpi.votes'),
+ url(r'^api/v1/kpi/questions/?$', api.QuestionsMetricList.as_view(),
+ name='api.kpi.questions'),
+ url(r'^api/v1/kpi/search-ctr/?$',
+ api.SearchClickthroughMetricList.as_view(),
+ name='api.kpi.search-ctr'),
+)
View
2 kitsune/questions/templates/questions/metrics.html
@@ -24,7 +24,7 @@
{% endfor %}
</section>
- {% set api_url = url('api_dispatch_list', resource_name='kpi_questions', api_name='v1') %}
+ {% set api_url = url('api.kpi.questions') %}
{% if current_locale %}
{% set api_url = api_url|urlparams(locale=current_locale) %}
{% endif %}
View
6 kitsune/sumo/static/js/questions.metrics-dashboard.js
@@ -6,13 +6,17 @@ function init() {
}
function makeTopicsGraph() {
- var $topic, datums, seriesSpec, key;
+ var $topics, datums, seriesSpec, key;
$('input[type=date]').datepicker({
dateFormat: 'yy-mm-dd'
});
$topics = $('#topic-stats');
+ if ($topics.length === 0) {
+ return;
+ }
+
datums = $topics.data('graph');
seriesSpec = [];
window.datums = datums;
View
1 kitsune/urls.py
@@ -53,6 +53,7 @@
(r'^', include('kitsune.dashboards.urls')),
(r'^', include('kitsune.landings.urls')),
(r'^', include('tidings.urls')), # Keep short for email wrapping.
+ (r'^', include('kitsune.kpi.urls_api')),
# Users
('', include('kitsune.users.urls')),

0 comments on commit 747f8fe

Please sign in to comment.