Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exposes submissions url to enketo #1526

Merged
merged 31 commits into from
Mar 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b196a23
Exposes submissions url to enketo
WinnyTroy Dec 21, 2018
e3cf4e5
Improve code
WinnyTroy Dec 21, 2018
fa3c76a
Code cleanup- Removed pdb from code base
WinnyTroy Jan 4, 2019
8b64023
Included test to check correct url received from function
WinnyTroy Jan 4, 2019
6b15478
Included test to check if error message is being passed appropriately
WinnyTroy Jan 4, 2019
8cdb4b4
Improve code
WinnyTroy Jan 7, 2019
2d8f7bd
Include request_mock package to requirements file
WinnyTroy Jan 7, 2019
2084407
Fix Flake8 errors
WinnyTroy Jan 8, 2019
651e5d1
Fix flake8 errors.
WinnyTroy Jan 8, 2019
bcf0ae3
Fix error handling.
WinnyTroy Jan 8, 2019
bf543d8
Include viewset to expose single submit url.
WinnyTroy Jan 9, 2019
5dcac53
Include required packages in setup file
WinnyTroy Jan 9, 2019
6ab1bec
Specifies exact error being raised.
WinnyTroy Jan 10, 2019
c743fec
Included Test for single submit url
WinnyTroy Jan 10, 2019
7458679
Fix failing tests.
WinnyTroy Jan 10, 2019
176c5d3
Fixes TypeError
WinnyTroy Jan 10, 2019
1c3c0c5
Code refactor
WinnyTroy Jan 16, 2019
57f8768
Fix failing test
WinnyTroy Jan 16, 2019
65cbefc
Fix TypeError caused due to refactoring of the code.
WinnyTroy Jan 17, 2019
871847d
Cleaned up enketo viewset
WinnyTroy Jan 18, 2019
d1628d0
Fix failing tests
WinnyTroy Jan 22, 2019
f8e81be
Include API docs and required packages
WinnyTroy Jan 23, 2019
8bd5ad1
Improve logic
lincmba Jan 23, 2019
54ef91f
Minor Corrections
WinnyTroy Jan 24, 2019
b3a24e2
Include constant in settings file
WinnyTroy Jan 24, 2019
f4b93bc
Use correct mock
lincmba Feb 15, 2019
82ca47a
Improve mock data
lincmba Feb 18, 2019
792da5c
Restore imports
lincmba Feb 19, 2019
ec457c2
code cleanup
lincmba Feb 19, 2019
e97aacc
Change Naming convections for certain variables
WinnyTroy Mar 1, 2019
d607c11
flake8 cleanup
ukanga Mar 11, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions docs/forms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,33 @@ Response
"enketo_preview_url": "https://H6Ic6.enketo.org/webform/preview?server=https://api.ona.io/geoffreymuchai/&id=form_id"
}

Get single submission url
-------------------------
.. raw:: html

<pre class="prettyprint">
<b>GET</b> /api/v1/forms/<code>{pk}</code>/enketo?survey_type=single</pre>

Request
^^^^^^^
::

curl -X GET https://api.ona.io/api/v1/forms/28058/enketo?survey_type=single

Response
^^^^^^^^
::

HTTP 200 OK

Response
^^^^^^^^^
::

{
"single_submit_url": "https://enke.to/single/::abcd"
}


Get form data in xls, csv format.
---------------------------------
Expand Down
14 changes: 13 additions & 1 deletion onadata/apps/api/tests/mocked_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import json

import requests
from httmock import urlmatch
from httmock import urlmatch, all_requests


@urlmatch(netloc=r'(.*\.)?ona\.io$', path=r'^/examples/forms/tutorial/form$')
Expand Down Expand Up @@ -77,6 +77,18 @@ def enketo_mock(url, request): # pylint: disable=unused-argument
return response


@all_requests
def enketo_single_submission_mock(url, request):
"""Return mocked enketo single submission Response object."""
response = requests.Response()
response.status_code = 200
# pylint: disable=protected-access
response._content = \
'{\n "single_url": "https:\\/\\/enketo.ona.io\\/single/::XZqoZ94y",\n'\
' "code": "200"\n}'
return response


@urlmatch(netloc=r'(.*\.)?enketo\.ona\.io$', path=r'^/api_v1/survey/preview$')
def enketo_preview_url_mock(url, request): # pylint: disable=unused-argument
"""
Expand Down
25 changes: 22 additions & 3 deletions onadata/apps/api/tests/viewsets/test_xform_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import json
import os
import re
from builtins import open
from collections import OrderedDict
from datetime import datetime
from datetime import timedelta
Expand All @@ -17,7 +18,6 @@
from xml.dom import minidom

import jwt
from builtins import open
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache
Expand All @@ -39,7 +39,8 @@
enketo_url_mock, external_mock, external_mock_single_instance,
external_mock_single_instance2, xls_url_no_extension_mock,
xls_url_no_extension_mock_content_disposition_attr_jumbled_v1,
xls_url_no_extension_mock_content_disposition_attr_jumbled_v2)
xls_url_no_extension_mock_content_disposition_attr_jumbled_v2,
enketo_single_submission_mock)
from onadata.apps.api.tests.viewsets.test_abstract_viewset import \
TestAbstractViewSet
from onadata.apps.api.viewsets.project_viewset import ProjectViewSet
Expand Down Expand Up @@ -732,8 +733,11 @@ def test_enketo_url_error502(self):
response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.data, data)

@override_settings(TESTING_MODE=False)
def test_enketo_url(self):
with HTTMock(enketo_preview_url_mock, enketo_url_mock):
"""Test functionality to expose enketo urls."""
with HTTMock(enketo_preview_url_mock, enketo_url_mock,
enketo_single_submission_mock):
self._publish_xls_form_to_project()
view = XFormViewSet.as_view({
'get': 'enketo'
Expand All @@ -747,6 +751,21 @@ def test_enketo_url(self):
data = {"enketo_url": url, "enketo_preview_url": preview_url}
self.assertEqual(response.data, data)

def test_get_single_submit_url(self):
with HTTMock(enketo_preview_url_mock, enketo_url_mock,
enketo_single_submission_mock):
self._publish_xls_form_to_project()
view = XFormViewSet.as_view({
'get': 'enketo'
})
formid = self.xform.pk
get_data = {'survey_type': 'single'}
request = self.factory.get('/', data=get_data, **self.extra)
response = view(request, pk=formid)
submit_url = "https://enketo.ona.io/single/::XZqoZ94y"
data = {"single_submit_url": submit_url}
self.assertEqual(response.data, data)

def test_enketo_url_with_default_form_params(self):
with HTTMock(enketo_preview_url_mock, enketo_mock_with_form_defaults):
self._publish_xls_form_to_project()
Expand Down
19 changes: 15 additions & 4 deletions onadata/apps/api/viewsets/xform_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
from onadata.libs.utils.viewer_tools import (enketo_url,
generate_enketo_form_defaults,
get_enketo_preview_url,
get_form_url)
get_form_url,
get_enketo_single_submit_url)
from onadata.libs.exceptions import EnketoError
from onadata.settings.common import XLS_EXTENSIONS, CSV_EXTENSION

Expand Down Expand Up @@ -387,6 +388,9 @@ def login(self, request, **kwargs):

@action(methods=['GET'], detail=True)
def enketo(self, request, **kwargs):
"""Expose enketo urls."""
survey_type = self.kwargs.get('survey_type') or \
request.GET.get('survey_type')
self.object = self.get_object()
form_url = get_form_url(
request, self.object.user.username, settings.ENKETO_PROTOCOL,
Expand All @@ -400,17 +404,24 @@ def enketo(self, request, **kwargs):
request_vars = request.GET
defaults = generate_enketo_form_defaults(
self.object, **request_vars)
url = enketo_url(form_url, self.object.id_string, **defaults)
url = enketo_url(
form_url, self.object.id_string, **defaults)
preview_url = get_enketo_preview_url(request,
self.object.user.username,
self.object.id_string,
xform_pk=self.object.pk)
except EnketoError as e:
data = {'message': _(u"Enketo error: %s" % e)}
else:
if url and preview_url:
if survey_type == 'single':
single_submit_url = get_enketo_single_submit_url(
request, self.object.user.username, self.object.id_string,
xform_pk=self.object.pk)
data = {"single_submit_url": single_submit_url}
elif url and preview_url:
http_status = status.HTTP_200_OK
data = {"enketo_url": url, "enketo_preview_url": preview_url}
data = {"enketo_url": url,
"enketo_preview_url": preview_url}

return Response(data, http_status)

Expand Down
103 changes: 70 additions & 33 deletions onadata/libs/tests/utils/test_viewer_tools.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,39 @@
# -*- coding: utf-8 -*-
"""
Test onadata.libs.utils.viewer_tools
"""
"""Test onadata.libs.utils.viewer_tools."""
import os
from mock import patch

import requests_mock
from django.conf import settings
from django.core.files.base import File
from django.http import Http404
from django.test.client import RequestFactory
from django.test.utils import override_settings
from django.utils import timezone
from mock import patch

from onadata.apps.logger.models import XForm, Instance, Attachment
from onadata.apps.main.tests.test_base import TestBase
from onadata.libs.utils.viewer_tools import (export_def_from_filename,
from onadata.libs.exceptions import EnketoError
from onadata.libs.utils.viewer_tools import (create_attachments_zipfile,
export_def_from_filename,
generate_enketo_form_defaults,
get_client_ip, get_form,
get_form_url,
create_attachments_zipfile)
get_enketo_single_submit_url)


class TestViewerTools(TestBase):
"""
Test viewer_tools functions
"""
"""Test viewer_tools functions."""

def test_export_def_from_filename(self):
"""
Test export_def_from_filename().
"""
"""Test export_def_from_filename()."""
filename = "path/filename.xlsx"
ext, mime_type = export_def_from_filename(filename)
self.assertEqual(ext, 'xlsx')
self.assertEqual(mime_type, 'vnd.openxmlformats')

def test_get_client_ip(self):
"""
Test get_client_ip().
"""
"""Test get_client_ip()."""
request = RequestFactory().get("/")
client_ip = get_client_ip(request)
self.assertIsNotNone(client_ip)
Expand All @@ -45,9 +42,7 @@ def test_get_client_ip(self):

# pylint: disable=C0103
def test_get_enketo_defaults_without_vars(self):
"""
Test generate_enketo_form_defaults() without vars.
"""
"""Test generate_enketo_form_defaults() without vars."""
# create xform
self._publish_transportation_form()
# create map without variables
Expand All @@ -58,9 +53,7 @@ def test_get_enketo_defaults_without_vars(self):

# pylint: disable=C0103
def test_get_enketo_defaults_with_right_xform(self):
"""
Test generate_enketo_form_defaults() with xform vars.
"""
"""Test generate_enketo_form_defaults() with xform vars."""
# create xform
self._publish_transportation_form()
# create kwargs with existing xform variable
Expand All @@ -76,9 +69,7 @@ def test_get_enketo_defaults_with_right_xform(self):

# pylint: disable=C0103
def test_get_enketo_defaults_with_multiple_params(self):
"""
Test generate_enketo_form_defaults() with multiple params
"""
"""Test generate_enketo_form_defaults() with multiple params."""
# create xform
self._publish_transportation_form()
# create kwargs with existing xform variable
Expand Down Expand Up @@ -106,9 +97,7 @@ def test_get_enketo_defaults_with_multiple_params(self):

# pylint: disable=C0103
def test_get_enketo_defaults_with_non_existent_field(self):
"""
Test generate_enketo_form_defaults() with non existent field.
"""
"""Test generate_enketo_form_defaults() with non existent field."""
# create xform
self._publish_transportation_form()
# create kwargs with NON-existing xform variable
Expand All @@ -117,9 +106,7 @@ def test_get_enketo_defaults_with_non_existent_field(self):
self.assertEqual(defaults, {})

def test_get_form(self):
"""
Test get_form().
"""
"""Test get_form()."""
# non existent id_string
with self.assertRaises(Http404):
get_form({'id_string': 'non_existent_form'})
Expand All @@ -144,9 +131,7 @@ def test_get_form(self):

@override_settings(TESTING_MODE=False)
def test_get_form_url(self):
"""
Test get_form_url()
"""
"""Test get_form_url()."""
request = RequestFactory().get('/')

# default https://ona.io
Expand Down Expand Up @@ -193,3 +178,55 @@ def test_create_attachments_zipfile_file_too_big(self, rpt_mock):

self.assertTrue(rpt_mock.called)
rpt_mock.assert_called_with(message[0], message[1])

@override_settings(TESTING_MODE=False)
def test_get_submissions_url(self):
"""Test get_submissions_url()."""
@override_settings(TESTING_MODE=False, ENKETO_URL='https://enketo.ona.io')
@requests_mock.Mocker()
def test_get_enketo_single_submit_url(self, mocked):
"""Test get_single_submit_url.

Ensures single submit url is being received.
"""
request = RequestFactory().get('/')

mocked_response = {
"single_url": "https://enketo.ona.io/single/::XZqoZ94y",
"code": 200
}

enketo_url = settings.ENKETO_URL + "/api/v2/survey/single/once"
username = "bob"
server_url = get_form_url(
request, username, settings.ENKETO_PROTOCOL, True, xform_pk=1)

url = '{}?server_url={}&form_id={}'.format(
enketo_url, server_url, "tag_team")
mocked.get(url, json=mocked_response)
response = get_enketo_single_submit_url(
request, username, id_string="tag_team", xform_pk=1)

self.assertEqual(
lincmba marked this conversation as resolved.
Show resolved Hide resolved
response, 'https://enketo.ona.io/single/::XZqoZ94y')

@override_settings(TESTING_MODE=False, ENKETO_URL='https://enketo.ona.io')
@requests_mock.Mocker()
def test_get_single_submit_url_error_action(self, mocked):
"""Test get_single_submit_url to raises EnketoError."""
request = RequestFactory().get('/')

enketo_url = settings.ENKETO_URL + "/api/v2/survey/single/once"
username = "Milly"
server_url = get_form_url(
request, username, settings.ENKETO_PROTOCOL, True, xform_pk=1)

url = '{}?server_url={}&form_id={}'.format(
enketo_url, server_url, "tag_team")
mocked.get(url, status_code=401)
msg = "There was a problem with your submissionor form."\
" Please contact support."
self.assertRaisesMessage(
EnketoError,
msg, get_enketo_single_submit_url,
request, username, "tag_team", 1)
Loading