diff --git a/django/website/rest_api/tests/term_itemcount_tests.py b/django/website/rest_api/tests/term_itemcount_tests.py index 5904f1920b5f4c510297eab4ae943949f0629faa..80071485f7063ce0f062265964fa3a9e27c46f63 100644 --- a/django/website/rest_api/tests/term_itemcount_tests.py +++ b/django/website/rest_api/tests/term_itemcount_tests.py @@ -1,7 +1,11 @@ from __future__ import unicode_literals, absolute_import + +from datetime import timedelta + import pytest from django.core.urlresolvers import reverse +from django.utils import timezone from rest_framework.test import APIRequestFactory from rest_framework import status @@ -15,16 +19,16 @@ from .taxonomy_and_term_create_tests import ( ) -def get_term_itemcount(taxonomy_slug): - response = get_term_itemcount_response(taxonomy_slug) +def get_term_itemcount(taxonomy_slug, get_params=None): + response = get_term_itemcount_response(taxonomy_slug, get_params) assert status.is_success(response.status_code), response.data return response -def get_term_itemcount_response(taxonomy_slug): +def get_term_itemcount_response(taxonomy_slug, get_params=None): url = reverse('taxonomy-itemcount', kwargs={'slug': taxonomy_slug}) - request = APIRequestFactory().get(url) + request = APIRequestFactory().get(url, data=get_params) view = TaxonomyViewSet.as_view(actions={'get': 'itemcount'}) return view(request, slug=taxonomy_slug) @@ -121,6 +125,49 @@ def test_term_itemcount_contains_taxonomy_term_long_name( assert origins['long_name'] == long_name +@pytest.mark.django_db +def test_items_in_date_range_returned(questions_category_slug): + now = timezone.now().replace( + microsecond=0 # MySQL discards microseconds + ) + + one_day_ago = now - timedelta(days=1) + one_week_ago = now - timedelta(weeks=1) + eight_days_ago = now - timedelta(days=8) + + item_too_recent = create_item( + body="Where did ebola came from?", + timestamp=now + ).data + item_in_range_1 = create_item( + body="What was the caused of ebola outbreak in liberia?", + timestamp=one_day_ago + ).data + item_in_range_2 = create_item( + body="Is Ebola a man made sickness", + timestamp=one_week_ago + ).data + item_too_old = create_item( + body="What brought about ebola in liberia", + timestamp=eight_days_ago + ).data + + origins = add_term(taxonomy=questions_category_slug, name="Test Origins").data + + categorize_item(item_in_range_1, origins) + categorize_item(item_in_range_2, origins) + categorize_item(item_too_old, origins) + categorize_item(item_too_recent, origins) + + get_params = { + 'start_time': one_week_ago, + 'end_time': one_day_ago} + + [term] = get_term_itemcount(questions_category_slug, get_params).data + + assert term['count'] == 2 + + @pytest.mark.django_db def test_error_for_non_existent_taxonomy(): response = get_term_itemcount_response('a-taxonomy-that-does-not-exist') diff --git a/django/website/rest_api/views.py b/django/website/rest_api/views.py index 5893fbccbee318296b6682dcaf67f833a6eac00b..1b4e59d49a52ae8098495358ac141536e9343773 100644 --- a/django/website/rest_api/views.py +++ b/django/website/rest_api/views.py @@ -70,13 +70,24 @@ class TaxonomyViewSet(viewsets.ModelViewSet): data = {'detail': message} return Response(data, status=status.HTTP_400_BAD_REQUEST) - terms = Term.objects.filter(taxonomy=taxonomy).annotate( - count=Count('items') - ) + terms = self._get_terms(request, taxonomy) + data = TermItemCountSerializer(terms, many=True).data return Response(data, status=status.HTTP_200_OK) + def _get_terms(self, request, taxonomy): + start_time = request.query_params.get('start_time', None) + end_time = request.query_params.get('end_time', None) + + filters = {'taxonomy': taxonomy} + + if start_time is not None and end_time is not None: + filters['items__timestamp__range'] = [start_time, end_time] + + return Term.objects.filter(**filters).annotate( + count=Count('items')) + class TermViewSet(viewsets.ModelViewSet): serializer_class = TermSerializer diff --git a/readme.markdown b/readme.markdown index f9569a143c4897db050afa850428b7b715610492..f9df16c54ac2918dd32b978aeb8a7422eec99805 100644 --- a/readme.markdown +++ b/readme.markdown @@ -12,12 +12,12 @@ Base URL '/items/' #### Create Items -'/items/' POST +'/items/' POST - { - "body": "blah", - "timestamp": "..." - } + { + "body": "blah", + "timestamp": "..." + } - create an item. Should return the object, including its unique ID and the system allocated creation time. @@ -26,7 +26,7 @@ Base URL '/items/' #### Create item with tags and categories { - "body": "blah", + "body": "blah", "timestamp": "...", "metadata": [ { "slug": "<taxonomy-slug>", // slug OR: name @@ -54,7 +54,7 @@ should be an exception. - returns a list of Items - [ { "id": nn, + [ { "id": nn, "body": "...", "created": ... "timestamp": ... @@ -65,9 +65,9 @@ should be an exception. - When we do categories it should return those the same way as post above, e.g.: - - [ { - "id": nn, + + [ { + "id": nn, "body": "...", "created": ... "timestamp": ... @@ -94,17 +94,17 @@ the expanded `Item` JSON somehow too. [ { "name": "Ebola Question Type", "slug": "ebola-question-type", - "long_name": "Question Type", + "long_name": "Question Type", "cardinality": "optional", "vocabulary": "closed", "terms": [ - {"name": "Is Ebola Real", + {"name": "Is Ebola Real", "long name": ... }, ... ] }, - { "name": "Reliability", + { "name": "Reliability", ... }, ... @@ -126,11 +126,11 @@ the unique field is derived from the given one? { "name": "Ebola Question Type", "slug": "ebola-question-type", // do we supply this or is it calculated? - "long_name": "Question Type", + "long_name": "Question Type", "cardinality": "optional", "vocabulary": "closed", "terms": [ - {"name": "Is Ebola Real", + {"name": "Is Ebola Real", "long name": ... }, ... @@ -146,27 +146,27 @@ be returned by the call. { "name": "Ebola Question Type", "slug": "ebola-question-type", // calculated from name - "long_name": "Question Type", + "long_name": "Question Type", "cardinality": "optional", "vocabulary": "closed", "terms": [ - {"name": "Is Ebola Real", + {"name": "Is Ebola Real", "long name": ... }, ... ] }, -e.g. `/taxonomies/ebola-questions/` +e.g. `/taxonomies/ebola-questions/` - Taxonomy details URL should use the taxonomy's sluggified name -### Update Taxonomy Details +### Update Taxonomy Details `/taxonomies/ebola-questions/` POST - { - "long_name": "Question Type", + { + "long_name": "Question Type", "cardinality": "optional", "vocabulary": "closed", }, @@ -181,7 +181,7 @@ e.g. `/taxonomies/ebola-questions/` ### Add a term to a taxonomy -We could do +We could do `/taxonomies/ebola-questions/terms/` POST { 'name': 'vaccine' } But for the moment we;re doing @@ -189,6 +189,28 @@ But for the moment we;re doing `/terms/` POST { 'name': 'vaccine', 'taxonomy': 'ebola-questions' } +### Get count of items per term for a taxonomy + +`/taxonomies/<taxonomy-slug>/itemcount?start_time=<start-time>&end_time=<end_time> + +Returns a list of terms: + [ { "name": "Vaccine", + "long_name": "Vaccine Trial", + "count": 2 + }, + { + "name": "Measures", + "long_name": "What measures could end Ebola?", + "count": 1 + }, + { + "name": "Symptoms", + "long_name": "Symptoms/Medical", + "count": 0 + }, + ... + ] + ### List all Terms (all taxonomies) '/terms/' GET @@ -255,7 +277,7 @@ OR '/items/2314/taxonomies/terms/<term-slug>/' DELETE -- Where term slug is the unique slug for the term. +- Where term slug is the unique slug for the term. e.g. '/items/2314/taxonomies/terms/ebola-questions-