def test_xform_has_uncommon_reference(self):
        """
        Test creating a merged dataset that has matching fields but with
        uncommon reference variable.
        """
        view = MergedXFormViewSet.as_view({
            'post': 'create',
        })
        # pylint: disable=attribute-defined-outside-init
        self.project = get_user_default_project(self.user)
        xform1 = self._publish_markdown(MD, self.user, id_string='a')
        xform2 = self._publish_markdown(
            REFERENCE_ISSUE, self.user, id_string='b')

        data = {
            'xforms': [
                "http://testserver/api/v1/forms/%s" % xform2.pk,
                "http://testserver/api/v1/forms/%s" % xform1.pk,
            ],
            'name':
            'Merged Dataset',
            'project':
            "http://testserver/api/v1/projects/%s" % self.project.pk,
        }

        request = self.factory.post('/', data=data, **self.extra)
        response = view(request)
        self.assertEqual(response.status_code, 400)
        error_message = (
            "There has been a problem trying to replace ${tunda} with the "
            "XPath to the survey element named 'tunda'. There is no survey "
            "element with this name.")
        self.assertIn('xforms', response.data)
        self.assertIn(error_message, response.data['xforms'])
    def test_md_has_deleted_xforms(self):
        """
        Test creating a merged dataset that includes a soft deleted form.
        """
        view = MergedXFormViewSet.as_view({
            'post': 'create',
        })
        # pylint: disable=attribute-defined-outside-init
        self.project = get_user_default_project(self.user)
        xform1 = self._publish_markdown(MD, self.user, id_string='a')
        xform2 = self._publish_markdown(MD, self.user, id_string='b')
        xform2.soft_delete()

        data = {
            'xforms': [
                "http://testserver/api/v1/forms/%s" % xform1.pk,
                "http://testserver/api/v1/forms/%s" % xform2.pk,
            ],
            'name':
            'Merged Dataset',
            'project':
            "http://testserver/api/v1/projects/%s" % self.project.pk,
        }

        request = self.factory.post('/', data=data, **self.extra)
        response = view(request)
        self.assertEqual(response.status_code, 400)
        self.assertEqual(
            response.data,
            {'xforms': [u'Invalid hyperlink - Object does not exist.']})
    def test_md_has_no_matching_fields(self):
        """
        Test creating a merged dataset that has no matching fields.
        """
        view = MergedXFormViewSet.as_view({
            'post': 'create',
        })
        # pylint: disable=attribute-defined-outside-init
        self.project = get_user_default_project(self.user)
        xform1 = self._publish_markdown(MD, self.user, id_string='a')
        xform2 = self._publish_markdown(NOT_MATCHING, self.user, id_string='b')

        data = {
            'xforms': [
                "http://testserver/api/v1/forms/%s" % xform1.pk,
                "http://testserver/api/v1/forms/%s" % xform2.pk,
            ],
            'name':
            'Merged Dataset',
            'project':
            "http://testserver/api/v1/projects/%s" % self.project.pk,
        }

        request = self.factory.post('/', data=data, **self.extra)
        response = view(request)
        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.data,
                         {'xforms': [u'No matching fields in xforms.']})
    def test_merged_datasets_form_xml(self):
        """Test retrieving the XLSForm XForm of a merged dataset"""
        # create a merged dataset
        merged_dataset = self._create_merged_dataset()

        view = MergedXFormViewSet.as_view({'get': 'form'})
        request = self.factory.get('/', **self.extra)
        response = view(request, pk=merged_dataset['id'], format='xml')
        self.assertEqual(response.status_code, 200)

        response.render()
        self.assertEqual('text/xml; charset=utf-8', response['Content-Type'])
    def test_merged_datasets_retrieve(self):
        """Test retrieving a specific merged dataset"""
        merged_dataset = self._create_merged_dataset(geo=True)
        merged_xform = MergedXForm.objects.get(pk=merged_dataset['id'])

        # make submission to form b
        form_b = merged_xform.xforms.all()[1]
        xml = '<data id="b"><fruit>mango</fruit></data>'
        instance = Instance(xform=form_b, xml=xml)
        instance.save()
        form_b.refresh_from_db()
        form_b.last_submission_time = instance.date_created
        form_b.save()
        view = MergedXFormViewSet.as_view({'get': 'retrieve'})
        request = self.factory.get('/')

        # status_code is 404 when the pk doesn't exist
        response = view(request, pk=(1000 * merged_dataset['id']))
        self.assertEqual(response.status_code, 404)

        # status_code is 404 when: pk exists, user is not authenticated
        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 404)

        # status_code is 200 when: pk exists, user is authenticated
        request = self.factory.get('/', **self.extra)
        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)

        # data has expected fields
        self.assertIn('id', response.data)
        self.assertIn('title', response.data)
        self.assertIn('xforms', response.data)
        self.assertEqual(response.data['num_of_submissions'], 1)
        self.assertEqual(response.data['last_submission_time'],
                         form_b.last_submission_time.isoformat())

        # merged dataset should be available at api/forms/[pk] endpoint
        request = self.factory.get('/', **self.extra)
        view = XFormViewSet.as_view({'get': 'retrieve'})
        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(merged_dataset['id'], response.data['formid'])
        self.assertIn('is_merged_dataset', response.data)
        self.assertTrue(response.data['is_merged_dataset'])
        self.assertTrue(response.data['instances_with_geopoints'])
        self.assertEqual(response.data['num_of_submissions'], 1)
        self.assertEqual(response.data['last_submission_time'],
                         form_b.last_submission_time.isoformat())
    def _create_merged_dataset(self, geo=False):
        view = MergedXFormViewSet.as_view({
            'post': 'create',
        })
        # pylint: disable=attribute-defined-outside-init
        self.project = get_user_default_project(self.user)
        xform1 = self._publish_markdown(MD, self.user, id_string='a')
        xform2 = self._publish_markdown(MD, self.user, id_string='b')
        if geo:
            xform2.instances_with_geopoints = True
            xform2.save(update_fields=['instances_with_geopoints'])

        data = {
            'xforms': [
                "http://testserver/api/v1/forms/%s" % xform1.pk,
                "http://testserver/api/v1/forms/%s" % xform2.pk,
            ],
            'name':
            'Merged Dataset',
            'project':
            "http://testserver/api/v1/projects/%s" % self.project.pk,
        }
        # anonymous user
        request = self.factory.post('/', data=data)
        response = view(request)
        self.assertEqual(response.status_code, 401)

        request = self.factory.post('/', data=data, **self.extra)
        response = view(request)
        self.assertEqual(response.status_code, 201)
        self.assertIn('id', response.data)
        self.assertIn('title', response.data)
        self.assertIn('xforms', response.data)
        expected_xforms_data = {
            'id': xform1.pk,
            'title': xform1.title,
            'id_string': xform1.id_string,
            'url': "http://testserver/api/v1/forms/%s" % xform1.pk,
            'num_of_submissions': xform1.num_of_submissions,
            'owner': xform1.user.username,
            'project_id': self.project.pk,
            'project_name': self.project.name
        }
        self.assertEqual(response.data['xforms'][0], expected_xforms_data)
        self.assertIsNotNone(response.data['uuid'])
        self.assertEqual(len(response.data['uuid']), 32)

        return response.data
    def test_merged_datasets_form_json(self):
        """Test retrieving the XLSForm JSON of a merged dataset"""
        # create a merged dataset
        merged_dataset = self._create_merged_dataset()

        view = MergedXFormViewSet.as_view({'get': 'form'})
        request = self.factory.get('/', **self.extra)
        response = view(request, pk=merged_dataset['id'], format='json')
        self.assertEqual(response.status_code, 200)

        response.render()
        self.assertEqual('application/json', response['Content-Type'])

        data = json.loads(response.content)
        self.assertIsInstance(data, dict)
        for key in ['children', 'id_string', 'name', 'default_language']:
            self.assertIn(key, data)
    def _create_merged_dataset(self, geo=False):
        view = MergedXFormViewSet.as_view({
            'post': 'create',
        })
        # pylint: disable=attribute-defined-outside-init
        self.project = get_user_default_project(self.user)
        xform1 = self._publish_markdown(MD, self.user, id_string='a')
        xform2 = self._publish_markdown(MD, self.user, id_string='b')
        if geo:
            xform2.instances_with_geopoints = True
            xform2.save(update_fields=['instances_with_geopoints'])

        data = {
            'xforms': [
                "http://testserver/api/v1/forms/%s" % xform1.pk,
                "http://testserver/api/v1/forms/%s" % xform2.pk,
            ],
            'name':
            'Merged Dataset',
            'project':
            "http://testserver/api/v1/projects/%s" % self.project.pk,
        }
        # anonymous user
        request = self.factory.post('/', data=data)
        response = view(request)
        self.assertEqual(response.status_code, 401)

        request = self.factory.post('/', data=data, **self.extra)
        response = view(request)
        self.assertEqual(response.status_code, 201)
        self.assertIn('id', response.data)
        self.assertIn('title', response.data)
        self.assertIn('xforms', response.data)
        expected_xforms_data = {
            'id': xform1.pk,
            'title': xform1.title,
            'id_string': xform1.id_string,
            'url': "http://testserver/api/v1/forms/%s" % xform1.pk,
            'num_of_submissions': xform1.num_of_submissions,
            'owner': xform1.user.username,
            'project_id': self.project.pk,
            'project_name': self.project.name
        }
        self.assertEqual(response.data['xforms'][0], expected_xforms_data)

        return response.data
    def test_merged_datasets_form_json(self):
        """Test retrieving the XLSForm JSON of a merged dataset"""
        # create a merged dataset
        merged_dataset = self._create_merged_dataset()

        view = MergedXFormViewSet.as_view({'get': 'form'})
        request = self.factory.get('/', **self.extra)
        response = view(request, pk=merged_dataset['id'], format='json')
        self.assertEqual(response.status_code, 200)

        response.render()
        self.assertEqual('application/json', response['Content-Type'])

        data = json.loads(response.content)
        self.assertIsInstance(data, dict)
        for key in ['children', 'id_string', 'name', 'default_language']:
            self.assertIn(key, data)
    def test_merged_datasets_list(self):
        """Test list endpoint of a merged dataset"""
        view = MergedXFormViewSet.as_view({
            'get': 'list',
        })
        request = self.factory.get('/')

        # Empty list when there are no merged datasets
        response = view(request)
        self.assertEqual(response.status_code, 200)
        self.assertIsInstance(response.data, list)
        self.assertEqual([], response.data)

        # create a merged dataset
        merged_dataset = self._create_merged_dataset()

        # Empty list for anonymous user
        response = view(request)
        self.assertEqual(response.status_code, 200)
        self.assertIsInstance(response.data, list)
        self.assertEqual([], response.data)

        # A list containing the merged datasets for user bob
        request = self.factory.get('/', **self.extra)
        response = view(request)
        self.assertEqual(response.status_code, 200)
        self.assertIsInstance(response.data, list)
        self.assertIn(merged_dataset, response.data)

        # merged dataset included in api/forms endpoint
        request = self.factory.get('/', **self.extra)
        view = XFormViewSet.as_view({'get': 'list'})
        response = view(request)
        self.assertEqual(response.status_code, 200)
        self.assertIsInstance(response.data, list)
        self.assertEqual(len(response.data), 3)
        self.assertIn(merged_dataset['id'],
                      [d['formid'] for d in response.data])
        data = [
            _ for _ in response.data if _['formid'] == merged_dataset['id']
        ][0]
        self.assertIn('is_merged_dataset', data)
        self.assertTrue(data['is_merged_dataset'])
    def test_merged_datasets_list(self):
        """Test list endpoint of a merged dataset"""
        view = MergedXFormViewSet.as_view({
            'get': 'list',
        })
        request = self.factory.get('/')

        # Empty list when there are no merged datasets
        response = view(request)
        self.assertEqual(response.status_code, 200)
        self.assertIsInstance(response.data, list)
        self.assertEqual([], response.data)

        # create a merged dataset
        merged_dataset = self._create_merged_dataset()

        # Empty list for anonymous user
        response = view(request)
        self.assertEqual(response.status_code, 200)
        self.assertIsInstance(response.data, list)
        self.assertEqual([], response.data)

        # A list containing the merged datasets for user bob
        request = self.factory.get('/', **self.extra)
        response = view(request)
        self.assertEqual(response.status_code, 200)
        self.assertIsInstance(response.data, list)
        self.assertIn(merged_dataset, response.data)

        # merged dataset included in api/forms endpoint
        request = self.factory.get('/', **self.extra)
        view = XFormViewSet.as_view({'get': 'list'})
        response = view(request)
        self.assertEqual(response.status_code, 200)
        self.assertIsInstance(response.data, list)
        self.assertEqual(len(response.data), 3)
        self.assertIn(merged_dataset['id'],
                      [d['formid'] for d in response.data])
        data = [
            _ for _ in response.data if _['formid'] == merged_dataset['id']
        ][0]
        self.assertIn('is_merged_dataset', data)
        self.assertTrue(data['is_merged_dataset'])
    def test_merged_datasets_deleted_parent_retrieve(self):
        """Test retrieving a specific merged dataset when the parent is deleted
        """
        merged_dataset = self._create_merged_dataset(geo=True)
        merged_xform = MergedXForm.objects.get(pk=merged_dataset['id'])

        # make submission to form b
        form_b = merged_xform.xforms.all()[1]
        xml = '<data id="b"><fruit>mango</fruit></data>'
        instance = Instance(xform=form_b, xml=xml)
        instance.save()
        form_b.refresh_from_db()
        form_b.last_submission_time = instance.date_created
        form_b.save()
        view = MergedXFormViewSet.as_view({'get': 'retrieve'})

        # status_code is 200 when: pk exists, user is authenticated

        request = self.factory.get('/', **self.extra)
        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)

        # delete parents
        [parent.delete() for parent in merged_xform.xforms.all()]
        merged_xform.refresh_from_db()

        # merged dataset should be available at api/forms/[pk] endpoint
        request = self.factory.get('/', **self.extra)
        view = XFormViewSet.as_view({'get': 'retrieve'})
        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(merged_dataset['id'], response.data['formid'])
        self.assertTrue(response.data['is_merged_dataset'])
        self.assertTrue(response.data['instances_with_geopoints'])
        # deleted parents, 0 submissions
        self.assertEqual(response.data['num_of_submissions'], 0)
    def test_merged_datasets_deleted_parent_retrieve(self):
        """Test retrieving a specific merged dataset when the parent is deleted
        """
        merged_dataset = self._create_merged_dataset(geo=True)
        merged_xform = MergedXForm.objects.get(pk=merged_dataset['id'])

        # make submission to form b
        form_b = merged_xform.xforms.all()[1]
        xml = '<data id="b"><fruit>mango</fruit></data>'
        instance = Instance(xform=form_b, xml=xml)
        instance.save()
        form_b.refresh_from_db()
        form_b.last_submission_time = instance.date_created
        form_b.save()
        view = MergedXFormViewSet.as_view({'get': 'retrieve'})

        # status_code is 200 when: pk exists, user is authenticated

        request = self.factory.get('/', **self.extra)
        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)

        # delete parents
        [parent.delete() for parent in merged_xform.xforms.all()]
        merged_xform.refresh_from_db()

        # merged dataset should be available at api/forms/[pk] endpoint
        request = self.factory.get('/', **self.extra)
        view = XFormViewSet.as_view({'get': 'retrieve'})
        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(merged_dataset['id'], response.data['formid'])
        self.assertTrue(response.data['is_merged_dataset'])
        self.assertTrue(response.data['instances_with_geopoints'])
        # deleted parents, 0 submissions
        self.assertEqual(response.data['num_of_submissions'], 0)
    def test_merged_datasets_data(self):
        """Test retrieving data of a merged dataset"""
        merged_dataset = self._create_merged_dataset()
        request = self.factory.get('/', **self.extra)
        view = MergedXFormViewSet.as_view({'get': 'data'})
        merged_xform = MergedXForm.objects.get(pk=merged_dataset['id'])
        detail_view = MergedXFormViewSet.as_view({
            'get': 'retrieve',
        })
        xform_detail_view = XFormViewSet.as_view({
            'get': 'retrieve',
        })

        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data), 0)

        # check num_of_submissions
        response = detail_view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['num_of_submissions'], 0)

        # make submission to form a
        form_a = merged_xform.xforms.all()[0]
        xml = '<data id="a"><fruit>orange</fruit></data>'
        Instance(xform=form_a, xml=xml).save()
        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data), 1)

        fruit = [d['fruit'] for d in response.data]
        expected_fruit = ['orange']
        self.assertEqual(fruit, expected_fruit)

        # check num_of_submissions
        response = detail_view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['num_of_submissions'], 1)

        # make submission to form b
        form_b = merged_xform.xforms.all()[1]
        xml = '<data id="b"><fruit>mango</fruit></data>'
        last_submission = Instance(xform=form_b, xml=xml)
        last_submission.save()
        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data), 2)

        fruit = [d['fruit'] for d in response.data]
        expected_fruit = ['orange', 'mango']
        self.assertEqual(fruit, expected_fruit)

        # check num_of_submissions /merged-datasets/[pk]
        response = detail_view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['num_of_submissions'], 2)

        # check last_submission_time
        self.assertEqual(response.data['last_submission_time'],
                         last_submission.date_created.isoformat())

        # check num_of_submissions /forms/[pk]
        response = xform_detail_view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['num_of_submissions'], 2)

        # check last_submission_time
        self.assertEqual(response.data['last_submission_time'],
                         last_submission.date_created.isoformat())
    def test_merged_datasets_data(self):
        """Test retrieving data of a merged dataset"""
        merged_dataset = self._create_merged_dataset()
        request = self.factory.get('/', **self.extra)
        view = MergedXFormViewSet.as_view({'get': 'data'})
        merged_xform = MergedXForm.objects.get(pk=merged_dataset['id'])
        detail_view = MergedXFormViewSet.as_view({
            'get': 'retrieve',
        })
        xform_detail_view = XFormViewSet.as_view({
            'get': 'retrieve',
        })

        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data), 0)

        # check num_of_submissions
        response = detail_view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['num_of_submissions'], 0)

        # make submission to form a
        form_a = merged_xform.xforms.all()[0]
        xml = '<data id="a"><fruit>orange</fruit></data>'
        Instance(xform=form_a, xml=xml).save()
        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data), 1)

        fruit = [d['fruit'] for d in response.data]
        expected_fruit = ['orange']
        self.assertEqual(fruit, expected_fruit)

        # check num_of_submissions
        response = detail_view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['num_of_submissions'], 1)

        # make submission to form b
        form_b = merged_xform.xforms.all()[1]
        xml = '<data id="b"><fruit>mango</fruit></data>'
        last_submission = Instance(xform=form_b, xml=xml)
        last_submission.save()
        response = view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(response.data), 2)

        fruit = [d['fruit'] for d in response.data]
        expected_fruit = ['orange', 'mango']
        self.assertEqual(fruit, expected_fruit)

        # check num_of_submissions /merged-datasets/[pk]
        response = detail_view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['num_of_submissions'], 2)

        # check last_submission_time
        self.assertEqual(response.data['last_submission_time'],
                         last_submission.date_created.isoformat())

        # check num_of_submissions /forms/[pk]
        response = xform_detail_view(request, pk=merged_dataset['id'])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.data['num_of_submissions'], 2)

        # check last_submission_time
        self.assertEqual(response.data['last_submission_time'],
                         last_submission.date_created.isoformat())