Skip to content

Commit

Permalink
Merge pull request #2451 from onaio/briefcase-fixes
Browse files Browse the repository at this point in the history
Add project and form level odk submission-list and download endpoints
  • Loading branch information
KipSigei authored Sep 4, 2023
2 parents 1d52a9a + 404829b commit 2221bbb
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 3 deletions.
134 changes: 134 additions & 0 deletions onadata/apps/api/tests/viewsets/test_briefcase_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,68 @@ def test_view_submission_list(self):
'{{resumptionCursor}}', '%s' % last_index)
self.assertContains(response, expected_submission_list)

def test_view_submission_list_w_xformid(self):
view = BriefcaseViewset.as_view({'get': 'list'})
self._publish_xml_form()
self._make_submissions()
self._submission_list_url = reverse(
'view-submission-list',
kwargs={'xform_pk': self.xform.pk})
request = self.factory.get(
self._submission_list_url,
data={'formId': self.xform.id_string})
response = view(request, xform_pk=self.xform.pk)
self.assertEqual(response.status_code, 401)
auth = DigestAuth(self.login_username, self.login_password)
request.META.update(auth(request.META, response))
response = view(request, xform_pk=self.xform.pk)
self.assertEqual(response.status_code, 200)
submission_list_path = os.path.join(
self.main_directory, 'fixtures', 'transportation',
'view', 'submissionList.xml')
instances = ordered_instances(self.xform)

self.assertEqual(instances.count(), NUM_INSTANCES)

last_index = instances[instances.count() - 1].pk
with codecs.open(submission_list_path, 'rb', encoding='utf-8') as f:
expected_submission_list = f.read()
expected_submission_list = \
expected_submission_list.replace(
'{{resumptionCursor}}', '%s' % last_index)
self.assertContains(response, expected_submission_list)

def test_view_submission_list_w_projectid(self):
view = BriefcaseViewset.as_view({'get': 'list'})
self._publish_xml_form()
self._make_submissions()
self._submission_list_url = reverse(
'view-submission-list',
kwargs={'project_pk': self.xform.project.pk})
request = self.factory.get(
self._submission_list_url,
data={'formId': self.xform.id_string})
response = view(request, project_pk=self.xform.project.pk)
self.assertEqual(response.status_code, 401)
auth = DigestAuth(self.login_username, self.login_password)
request.META.update(auth(request.META, response))
response = view(request, project_pk=self.xform.project.pk)
self.assertEqual(response.status_code, 200)
submission_list_path = os.path.join(
self.main_directory, 'fixtures', 'transportation',
'view', 'submissionList.xml')
instances = ordered_instances(self.xform)

self.assertEqual(instances.count(), NUM_INSTANCES)

last_index = instances[instances.count() - 1].pk
with codecs.open(submission_list_path, 'rb', encoding='utf-8') as f:
expected_submission_list = f.read()
expected_submission_list = \
expected_submission_list.replace(
'{{resumptionCursor}}', '%s' % last_index)
self.assertContains(response, expected_submission_list)

def test_view_submission_list_w_soft_deleted_submission(self):
view = BriefcaseViewset.as_view({'get': 'list'})
self._publish_xml_form()
Expand Down Expand Up @@ -307,6 +369,78 @@ def test_view_downloadSubmission(self):
self.assertContains(response, instanceId, status_code=200)
self.assertMultiLineEqual(response.content.decode('utf-8'), text)

def test_view_downloadSubmission_w_xformid(self):
view = BriefcaseViewset.as_view({'get': 'retrieve'})
self._publish_xml_form()
self.maxDiff = None
self._submit_transport_instance_w_attachment()
instanceId = u'5b2cc313-fc09-437e-8149-fcd32f695d41'
instance = Instance.objects.get(uuid=instanceId)
formId = u'%(formId)s[@version=null and @uiVersion=null]/' \
u'%(formId)s[@key=uuid:%(instanceId)s]' % {
'formId': self.xform.id_string,
'instanceId': instanceId}
params = {'formId': formId}
auth = DigestAuth(self.login_username, self.login_password)
self._download_submission_url = reverse(
'view-download-submission',
kwargs={'xform_pk': self.xform.pk})
request = self.factory.get(
self._download_submission_url, data=params)
response = view(request, xform_pk=self.xform.pk)
self.assertEqual(response.status_code, 401)
request.META.update(auth(request.META, response))
response = view(request, xform_pk=self.xform.pk)
text = "uuid:%s" % instanceId
download_submission_path = os.path.join(
self.main_directory, 'fixtures', 'transportation',
'view', 'downloadSubmission.xml')
with codecs.open(download_submission_path, encoding='utf-8') as f:
text = f.read()
for var in ((u'{{submissionDate}}',
instance.date_created.isoformat()),
(u'{{form_id}}', str(self.xform.id)),
(u'{{media_id}}', str(self.attachment.id))):
text = text.replace(*var)
self.assertContains(response, instanceId, status_code=200)
self.assertMultiLineEqual(response.content.decode('utf-8'), text)

def test_view_downloadSubmission_w_projectid(self):
view = BriefcaseViewset.as_view({'get': 'retrieve'})
self._publish_xml_form()
self.maxDiff = None
self._submit_transport_instance_w_attachment()
instanceId = u'5b2cc313-fc09-437e-8149-fcd32f695d41'
instance = Instance.objects.get(uuid=instanceId)
formId = u'%(formId)s[@version=null and @uiVersion=null]/' \
u'%(formId)s[@key=uuid:%(instanceId)s]' % {
'formId': self.xform.id_string,
'instanceId': instanceId}
params = {'formId': formId}
auth = DigestAuth(self.login_username, self.login_password)
self._download_submission_url = reverse(
'view-download-submission',
kwargs={'project_pk': self.xform.project.pk})
request = self.factory.get(
self._download_submission_url, data=params)
response = view(request, project_pk=self.xform.project.pk)
self.assertEqual(response.status_code, 401)
request.META.update(auth(request.META, response))
response = view(request, project_pk=self.xform.project.pk)
text = "uuid:%s" % instanceId
download_submission_path = os.path.join(
self.main_directory, 'fixtures', 'transportation',
'view', 'downloadSubmission.xml')
with codecs.open(download_submission_path, encoding='utf-8') as f:
text = f.read()
for var in ((u'{{submissionDate}}',
instance.date_created.isoformat()),
(u'{{form_id}}', str(self.xform.id)),
(u'{{media_id}}', str(self.attachment.id))):
text = text.replace(*var)
self.assertContains(response, instanceId, status_code=200)
self.assertMultiLineEqual(response.content.decode('utf-8'), text)

def test_view_downloadSubmission_OtherUser(self):
view = BriefcaseViewset.as_view({'get': 'retrieve'})
self._publish_xml_form()
Expand Down
39 changes: 36 additions & 3 deletions onadata/apps/api/viewsets/briefcase_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ def get_object(self, queryset=None):
id_string = _extract_id_string(form_id)
uuid = _extract_uuid(form_id)
username = self.kwargs.get("username")
form_pk = self.kwargs.get("xform_pk")
project_pk = self.kwargs.get("project_pk")

if not username:
if form_pk:
queryset = self.queryset.filter(pk=form_pk)
if queryset.first():
username = queryset.first().user.username
elif project_pk:
queryset = self.queryset.filter(project__pk=project_pk)
if queryset.first():
username = queryset.first().user.username

obj = get_object_or_404(
Instance,
Expand All @@ -109,24 +121,45 @@ def get_object(self, queryset=None):

return obj

# pylint: disable=too-many-branches
# pylint: disable=too-many-branches,too-many-statements
def filter_queryset(self, queryset):
"""
Filters an XForm submission instances using ODK Aggregate query parameters.
"""
username = self.kwargs.get("username")
if username is None and self.request.user.is_anonymous:
form_pk = self.kwargs.get("xform_pk")
project_pk = self.kwargs.get("project_pk")

if (
not username or not form_pk or not project_pk
) and self.request.user.is_anonymous:
# raises a permission denied exception, forces authentication
self.permission_denied(self.request)

if username is not None and self.request.user.is_anonymous:
profile = get_object_or_404(UserProfile, user__username__iexact=username)
profile = None
if username:
profile = get_object_or_404(
UserProfile, user__username__iexact=username
)
elif form_pk:
queryset = queryset.filter(pk=form_pk)
if queryset.first():
profile = queryset.first().user.profile
elif project_pk:
queryset = queryset.filter(project__pk=project_pk)
if queryset.first():
profile = queryset.first().user.profile

if profile.require_auth:
# raises a permission denied exception, forces authentication
self.permission_denied(self.request)
else:
queryset = queryset.filter(user=profile.user)
elif form_pk:
queryset = queryset.filter(pk=form_pk)
elif project_pk:
queryset = queryset.filter(project__pk=project_pk)
else:
queryset = super().filter_queryset(queryset)

Expand Down
20 changes: 20 additions & 0 deletions onadata/apps/main/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,31 @@
BriefcaseViewset.as_view({"get": "list", "head": "list"}),
name="view-submission-list",
),
re_path(
r"^forms/(?P<xform_pk>\w+)/view/submissionList$",
BriefcaseViewset.as_view({"get": "list", "head": "list"}),
name="view-submission-list",
),
re_path(
r"^projects/(?P<project_pk>\d+)/view/submissionList$",
BriefcaseViewset.as_view({"get": "list", "head": "list"}),
name="view-submission-list",
),
re_path(
r"^(?P<username>\w+)/view/downloadSubmission$",
BriefcaseViewset.as_view({"get": "retrieve", "head": "retrieve"}),
name="view-download-submission",
),
re_path(
r"^forms/(?P<xform_pk>\w+)/view/downloadSubmission$",
BriefcaseViewset.as_view({"get": "retrieve", "head": "retrieve"}),
name="view-download-submission",
),
re_path(
r"^projects/(?P<project_pk>\d+)/view/downloadSubmission$",
BriefcaseViewset.as_view({"get": "retrieve", "head": "retrieve"}),
name="view-download-submission",
),
re_path(
r"^(?P<username>\w+)/formUpload$",
BriefcaseViewset.as_view({"post": "create", "head": "create"}),
Expand Down

0 comments on commit 2221bbb

Please sign in to comment.