Skip to content

Commit

Permalink
Merge pull request #1423 Monthly Submissions Endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
ukanga authored May 30, 2018
2 parents 9c05c11 + bd1b5c5 commit 2fe415f
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 10 deletions.
44 changes: 44 additions & 0 deletions docs/profiles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,47 @@ Response
::

HTTP 200 OK

Get the total number of monthly submissions
-------------------------------------------

This gets the total number of submissions made in a month to a specific user's forms.
The result is a count of the submissions to both private and public forms.

If there are no private forms then only the number of submissions to the public forms is returned, and vice versa.
If there are no submissions, then an empty dictionary is returned.

Use the month and year as query parameters to get the total number of submissions for a specific month.
If no query parameters are used, the result is the number of submissions of the current month.
If only month is used, then the year is assumed to be the current year. And if only year is passed, then the month is
assumed to be the current year.


Example
^^^^^^^

::
curl -X GET https://api.ona.io/api/v1/profiles/demouser/monthly_submissions

Response
^^^^^^^^

::
{
"public": 41,
"private": 185
}

Example
^^^^^^^

::
curl -X GET https://api.ona.io/api/v1/profiles/demouser/monthly_submissions?month=5&year=2018

Response
^^^^^^^^

::
{
"public": 240
}
130 changes: 120 additions & 10 deletions onadata/apps/api/tests/viewsets/test_user_profile_viewset.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
import datetime
import json
import requests
import os
from builtins import str

from django_digest.test import DigestAuth
from django.contrib.auth.models import User
from django.db.models import signals
from django.test.utils import override_settings
from httmock import all_requests, HTTMock
from django.utils.dateparse import parse_datetime

import requests
from django_digest.test import DigestAuth
from httmock import HTTMock, all_requests
from mock import patch

from onadata.apps.api.tests.viewsets.test_abstract_viewset import\
from onadata.apps.api.tests.viewsets.test_abstract_viewset import \
TestAbstractViewSet
from onadata.apps.api.viewsets.connect_viewset import ConnectViewSet
from onadata.apps.api.viewsets.user_profile_viewset import UserProfileViewSet
from onadata.apps.logger.models.instance import Instance
from onadata.apps.main.models import UserProfile
from django.contrib.auth.models import User
from onadata.libs.serializers.user_profile_serializer import (
_get_first_last_names)
from onadata.apps.api.viewsets.connect_viewset import ConnectViewSet
from onadata.libs.authentication import DigestAuthentication
from onadata.apps.main.models.user_profile import\
from onadata.apps.main.models.user_profile import \
set_kpi_formbuilder_permissions
from onadata.libs.authentication import DigestAuthentication
from onadata.libs.serializers.user_profile_serializer import \
_get_first_last_names


def _profile_data():
Expand Down Expand Up @@ -832,3 +837,108 @@ def test_create_user_with_given_name(self):
with self.settings(KPI_FORMBUILDER_URL='http://test_formbuilder$'):
extra_data = {"username": "rust"}
self._login_user_and_profile(extra_post_data=extra_data)

def test_get_monthly_submissions(self):
"""
Test getting monthly submissions for a user
"""
view = UserProfileViewSet.as_view({'get': 'monthly_submissions'})
# publish form and make submissions
self._publish_xls_form_to_project()
self._make_submissions()
count1 = Instance.objects.filter(xform=self.xform).count()

request = self.factory.get('/', **self.extra)
response = view(request, user=self.user.username)
self.assertEquals(response.status_code, 200)
self.assertFalse(self.xform.shared)
self.assertEquals(response.data, {'private': count1})

# publish another form, make submission and make it public
self._publish_form_with_hxl_support()
self.assertEquals(self.xform.id_string, 'hxl_example')
count2 = Instance.objects.filter(xform=self.xform).filter(
date_created__year=datetime.datetime.now().year).filter(
date_created__month=datetime.datetime.now().month).filter(
date_created__day=datetime.datetime.now().day).count()

self.xform.shared = True
self.xform.save()
request = self.factory.get('/', **self.extra)
response = view(request, user=self.user.username)
self.assertEquals(response.status_code, 200)
self.assertEquals(response.data, {'private': count1, 'public': count2})

def test_get_monthly_submissions_with_year_and_month_params(self):
"""
Test passing both month and year params
"""
view = UserProfileViewSet.as_view({'get': 'monthly_submissions'})
# publish form and make a submission dated 2013-02-18
self._publish_xls_form_to_project()
survey = self.surveys[0]
submission_time = parse_datetime('2013-02-18 15:54:01Z')
self._make_submission(
os.path.join(self.main_directory, 'fixtures', 'transportation',
'instances', survey, survey + '.xml'),
forced_submission_time=submission_time)
count = Instance.objects.filter(xform=self.xform).filter(
date_created__month=2).filter(date_created__year=2013).count()

# get submission count and assert the response is correct
data = {'month': 2, 'year': 2013}
request = self.factory.get('/', data=data, **self.extra)
response = view(request, user=self.user.username)
self.assertEquals(response.status_code, 200)
self.assertFalse(self.xform.shared)
self.assertEquals(response.data, {'private': count})

def test_monthly_submissions_with_month_param(self):
"""
Test that by passing only the value for month,
the year is assumed to be the current year
"""
view = UserProfileViewSet.as_view({'get': 'monthly_submissions'})
month = datetime.datetime.now().month
year = datetime.datetime.now().year

# publish form and make submissions
self._publish_xls_form_to_project()
self._make_submissions()
count = Instance.objects.filter(xform=self.xform).filter(
date_created__year=year).filter(date_created__month=month).count()

data = {'month': month}
request = self.factory.get('/', data=data, **self.extra)
response = view(request, user=self.user.username)
self.assertEquals(response.status_code, 200)
self.assertFalse(self.xform.shared)
self.assertEquals(response.data, {'private': count})

def test_monthly_submissions_with_year_param(self):
"""
Test that by passing only the value for year
the month is assumed to be the current month
"""
view = UserProfileViewSet.as_view({'get': 'monthly_submissions'})
month = datetime.datetime.now().month

# publish form and make submissions dated the year 2013
# and the current month
self._publish_xls_form_to_project()
survey = self.surveys[0]
_time = parse_datetime('2013-' + str(month) + '-18 15:54:01Z')
self._make_submission(
os.path.join(self.main_directory, 'fixtures', 'transportation',
'instances', survey, survey + '.xml'),
forced_submission_time=_time)
count = Instance.objects.filter(xform=self.xform).filter(
date_created__year=2013).filter(
date_created__month=month).count()

data = {'year': 2013}
request = self.factory.get('/', data=data, **self.extra)
response = view(request, user=self.user.username)
self.assertEquals(response.status_code, 200)
self.assertFalse(self.xform.shared)
self.assertEquals(response.data, {'private': count})
36 changes: 36 additions & 0 deletions onadata/apps/api/viewsets/user_profile_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
UserProfileViewSet module.
"""

import datetime
import json

from past.builtins import basestring # pylint: disable=redefined-builtin

from django.conf import settings
from django.core.validators import ValidationError
from django.db.models import Count

from rest_framework import serializers, status
from rest_framework.decorators import detail_route
Expand All @@ -19,13 +22,16 @@

from onadata.apps.api.permissions import UserProfilePermissions
from onadata.apps.api.tools import get_baseviewset_class, load_class
from onadata.apps.logger.models.instance import Instance
from onadata.apps.main.models import UserProfile
from onadata.libs import filters
from onadata.libs.mixins.authenticate_header_mixin import \
AuthenticateHeaderMixin
from onadata.libs.mixins.cache_control_mixin import CacheControlMixin
from onadata.libs.mixins.etags_mixin import ETagsMixin
from onadata.libs.mixins.object_lookup_mixin import ObjectLookupMixin
from onadata.libs.serializers.monthly_submissions_serializer import \
MonthlySubmissionsSerializer
from onadata.libs.serializers.user_profile_serializer import \
UserProfileSerializer

Expand Down Expand Up @@ -164,3 +170,33 @@ def partial_update(self, request, *args, **kwargs):

return super(UserProfileViewSet, self).partial_update(request, *args,
**kwargs)

@detail_route(methods=['GET'])
def monthly_submissions(self, request, *args, **kwargs):
""" Get the total number of submissions for a user """
profile = self.get_object()
month_param = self.request.query_params.get('month', None)
year_param = self.request.query_params.get('year', None)

# check if parameters are valid
if month_param:
if not month_param.isdigit() or \
int(month_param) not in range(1, 13):
raise ValidationError(u'Invalid month provided as parameter')
if year_param:
if not year_param.isdigit() or len(year_param) != 4:
raise ValidationError(u'Invalid year provided as parameter')

# Use query parameter values for month and year
# if none, use the current month and year
now = datetime.datetime.now()
month = month_param if month_param else now.month
year = year_param if year_param else now.year

instance_count = Instance.objects.filter(
xform__user=profile.user, xform__deleted_at__isnull=True,
date_created__year=year, date_created__month=month).values(
'xform__shared').annotate(num_instances=Count('id'))

serializer = MonthlySubmissionsSerializer(instance_count, many=True)
return Response(serializer.data[0])
28 changes: 28 additions & 0 deletions onadata/libs/serializers/monthly_submissions_serializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
Monthly submissions serializer
"""
from rest_framework import serializers


class MonthlySubmissionsListSerializer(serializers.ListSerializer):

def to_representation(self, data):
result = super(MonthlySubmissionsListSerializer,
self).to_representation(data)
result_dictionary = {}
for i in result:
label = 'public' if i['xform__shared'] else 'private'
result_dictionary[label] = i['num_instances']
return [result_dictionary]


class MonthlySubmissionsSerializer(serializers.Serializer):

class Meta:
list_serializer_class = MonthlySubmissionsListSerializer

def to_representation(self, instance):
"""
Returns the total number of private/public submissions for a user
"""
return instance

0 comments on commit 2fe415f

Please sign in to comment.