Skip to content

Commit

Permalink
Refactor flow-results endpoint with pagination support.
Browse files Browse the repository at this point in the history
  • Loading branch information
ukanga committed Feb 1, 2018
1 parent cefca24 commit 4144ab2
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 43 deletions.
2 changes: 1 addition & 1 deletion onadata/apps/api/tests/viewsets/test_floip_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def test_publishing_responses(self):
"flow-results-example-2-api-data.json")
with open(path) as json_file:
descriptor = json.load(json_file)
descriptor['id'] = floip_data['id']
descriptor['data']['id'] = floip_data['id']
request = self.factory.post(
'/',
data=json.dumps(descriptor),
Expand Down
70 changes: 28 additions & 42 deletions onadata/apps/api/viewsets/floip_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,21 @@
"""
FloipViewSet: API endpoint for /api/floip
"""
from cStringIO import StringIO

from rest_framework import mixins, status, viewsets
from rest_framework.decorators import detail_route
from rest_framework.exceptions import ParseError
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework_json_api.pagination import PageNumberPagination
from rest_framework_json_api.parsers import JSONParser
from rest_framework_json_api.renderers import JSONRenderer

from onadata.apps.api.permissions import XFormPermissions
from onadata.apps.logger.models import XForm
from onadata.libs import filters
from onadata.libs.renderers.renderers import floip_list
from onadata.libs.serializers.floip_serializer import (FloipListSerializer,
FloipSerializer)
from onadata.libs.utils.logger_tools import dict2xform, safe_create_instance


def parse_responses(responses):
"""
Returns individual submission for all responses in a flow-results responses
package.
"""
submission = {}
current_key = None
for row in responses:
if current_key is None:
current_key = row[1]
if current_key != row[1]:
yield submission
submission = {}
current_key = row[1]
submission[row[3]] = row[4]

yield submission
from onadata.libs.serializers.floip_serializer import (
FloipListSerializer, FloipSerializer, FlowResultsResponseSerializer)


# pylint: disable=too-many-ancestors
Expand All @@ -54,6 +33,7 @@ class FloipViewSet(mixins.CreateModelMixin, mixins.DestroyModelMixin,
queryset = XForm.objects.filter(deleted_at__isnull=True)
serializer_class = FloipSerializer

pagination_class = PageNumberPagination
parser_classes = (JSONParser, )
renderer_classes = (JSONRenderer, )
resource_name = ['packages', 'responses']
Expand All @@ -80,27 +60,33 @@ def responses(self, request, uuid=None):
"""
status_code = status.HTTP_200_OK
xform = self.get_object()
if request.method == 'POST':
responses = request.data.get('responses', [])
for submission in parse_responses(responses):
xml_string = dict2xform(submission, xform.id_string, 'data')
xml_file = StringIO(xml_string)

error, _instance = safe_create_instance(
request.user.username, xml_file, [], None, request)
if error:
raise ParseError(error)
status_code = status.HTTP_201_CREATED
data = {
"id": uuid or xform.uuid,
"type": "flow-results-data",
}
headers = {
'Content-Type': 'application/vnd.api+json',
'Location': self.request.build_absolute_uri(
reverse('flow-results-responses', kwargs={'uuid': xform.uuid}))
} # yapf: disable
if request.method == 'POST':
serializer = FlowResultsResponseSerializer(
data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
serializer.save()
data['response'] = serializer.data['responses']
status_code = status.HTTP_201_CREATED
else:
queryset = xform.instances.values_list('json', flat=True)
paginate_queryset = self.paginate_queryset(queryset)
if paginate_queryset:
data['responses'] = floip_list(paginate_queryset)
response = self.get_paginated_response(data)
for key, value in headers.items():
response[key] = value

return response

data['responses'] = floip_list(queryset)

return Response(
{
"id": uuid,
"type": "flow-results-data",
"responses":
floip_list(xform.instances.values_list('json', flat=True))
}, headers=headers, status=status_code) # yapf: disable
return Response(data, headers=headers, status=status_code)
62 changes: 62 additions & 0 deletions onadata/libs/serializers/floip_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext_lazy as _
from floip import survey_to_floip_package
from rest_framework.reverse import reverse
from rest_framework_json_api import serializers

from onadata.apps.api.tools import do_publish_xlsform
from onadata.apps.logger.models import XForm
from onadata.libs.utils.logger_tools import dict2xform, safe_create_instance


def _get_user(username):
Expand All @@ -40,6 +42,25 @@ def _get_owner(request):
return owner


def parse_responses(responses):
"""
Returns individual submission for all responses in a flow-results responses
package.
"""
submission = {}
current_key = None
for row in responses:
if current_key is None:
current_key = row[1]
if current_key != row[1]:
yield submission
submission = {}
current_key = row[1]
submission[row[3]] = row[4]

yield submission


# pylint: disable=too-many-ancestors
class FloipListSerializer(serializers.HyperlinkedModelSerializer):
"""
Expand Down Expand Up @@ -141,3 +162,44 @@ def to_representation(self, instance):
data['profile'] = 'flow-results-package'

return data


class FlowResultsResponse(object):
"""
FLowResultsResponse class to hold a list of submission ids.
"""
id = None # pylint: disable=invalid-name
responses = []

def __init__(self, uuid, responses):
self.id = uuid
self.responses = responses


class FlowResultsResponseSerializer(serializers.Serializer):
"""
FlowResultsResponseSerializer for handling publishing of Flow Results
Response package.
"""
id = serializers.CharField() # pylint: disable=invalid-name
responses = serializers.ListField()

def create(self, validated_data):
request = self.context['request']
responses = validated_data['responses']
xform = get_object_or_404(XForm, uuid=validated_data['id'])
processed = []
for submission in parse_responses(responses):
xml_file = StringIO(
dict2xform(submission, xform.id_string, 'data'))

error, instance = safe_create_instance(
request.user.username, xml_file, [], None, request)
processed.append(instance.pk)
if error:
raise serializers.ValidationError(error)

return FlowResultsResponse(xform.uuid, responses)

def update(self, instance, validated_data):
pass

0 comments on commit 4144ab2

Please sign in to comment.