Skip to content

Commit

Permalink
create entities FollowUpForm on publish
Browse files Browse the repository at this point in the history
On publish, we check if a form references a dataset that is an EntityList and create a FollowUpForm object
  • Loading branch information
kelvin-muchiri committed Nov 10, 2023
1 parent 5d2698a commit 094bd66
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 6 deletions.
2 changes: 1 addition & 1 deletion onadata/apps/api/tests/viewsets/test_abstract_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ def _publish_markdown(self, md, user, project=None, **kwargs):
created_by=user,
user=user,
xml=survey.to_xml(),
json=survey.to_json(),
json=json.loads(survey.to_json()),
project=project,
)
xform.save()
Expand Down
57 changes: 53 additions & 4 deletions onadata/apps/api/tests/viewsets/test_xform_viewset.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
XForm,
EntityList,
RegistrationForm,
FollowUpForm,
)
from onadata.apps.logger.models.xform_version import XFormVersion
from onadata.apps.logger.xform_instance_parser import XLSFormError
Expand Down Expand Up @@ -697,8 +698,8 @@ def test_external_choice_integer_name_xlsform(self):
for index, row in enumerate(csv_reader):
self.assertEqual(row, expected_data[index])

def test_publish_xlsform_w_entities(self):
"""Publishing an XLSForm with entities definition works"""
def test_publish_entities_registration_form(self):
"""Publishing an XLSForm that creates entities works"""
with HTTMock(enketo_mock):
xforms = XForm.objects.count()
path = os.path.join(
Expand All @@ -720,9 +721,9 @@ def test_publish_xlsform_w_entities(self):
self.assertEqual(RegistrationForm.objects.count(), 1)
entity_list = EntityList.objects.first()
reg_form = RegistrationForm.objects.first()
xform = XForm.objects.all().order_by("pk").first()
latest_form = XForm.objects.all().order_by("-pk").first()
self.assertEqual(entity_list.name, "trees")
self.assertEqual(reg_form.xform, xform)
self.assertEqual(reg_form.xform, latest_form)
self.assertEqual(
reg_form.save_to,
{
Expand All @@ -733,6 +734,54 @@ def test_publish_xlsform_w_entities(self):
)
self.assertEqual(reg_form.entity_list, entity_list)

def test_publish_entities_follow_up_form(self):
"""Publishing an XLSForm that consumes entities works"""
# Publish registration form
md = """
| survey |
| | type | name | label | save_to |
| | geopoint | location | Tree location | geometry |
| | select_one species | species | Tree species | species |
| | integer | circumference | Tree circumference in cm | circumference_cm |
| choices | | | | |
| | list_name | name | label | |
| | species | wallaba | Wallaba | |
| | species | mora | Mora | |
| | species | purpleheart | Purpleheart | |
| | species | greenheart | Greenheart | |
| settings | | | | |
| | form_title | form_id | | |
| | Trees registration | trees_reg | | |
| entities | | | | |
| | list_name | label | | |
| | trees | foo | | |"""
self._publish_markdown(md, self.user)

with HTTMock(enketo_mock):
xforms = XForm.objects.count()
path = os.path.join(
settings.PROJECT_ROOT,
"apps",
"main",
"tests",
"fixtures",
"entities",
"trees_follow_up.xlsx",
)

with open(path, "rb") as xls_file:
post_data = {"xls_file": xls_file}
request = self.factory.post("/", data=post_data, **self.extra)
response = self.view(request)
self.assertEqual(xforms + 1, XForm.objects.count())
self.assertEqual(response.status_code, 201)
latest_form = XForm.objects.all().order_by("-pk").first()
self.assertTrue(
FollowUpForm.objects.filter(
entity_list__name="trees", xform=latest_form
).exists()
)


class TestXFormViewSet(XFormViewSetBaseTestCase):
"""Test XFormViewSet"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 3.2.20 on 2023-11-10 09:12

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('logger', '0012_alter_registrationform_unique_together'),
]

operations = [
migrations.AlterUniqueTogether(
name='followupform',
unique_together={('entity_list', 'xform')},
),
]
6 changes: 6 additions & 0 deletions onadata/apps/logger/models/follow_up_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ class FollowUpForm(AbstractBase):
No changes are made to any entities
"""

class Meta(AbstractBase.Meta):
unique_together = (
"entity_list",
"xform",
)

entity_list = models.ForeignKey(
EntityList,
related_name="follow_up_forms",
Expand Down
16 changes: 16 additions & 0 deletions onadata/apps/logger/tests/models/test_follow_up_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import pytz
from datetime import datetime
from unittest.mock import patch

from django.db.utils import IntegrityError

from onadata.apps.main.tests.test_base import TestBase
from onadata.apps.logger.models import FollowUpForm, EntityList

Expand Down Expand Up @@ -42,3 +45,16 @@ def test_related_name(self):
)
self.assertEqual(self.entity_list.follow_up_forms.count(), 1)
self.assertEqual(self.xform.follow_up_lists.count(), 1)

def test_no_duplicate_entity_list_xform(self):
"""No duplicates allowed for existing entity_list and xform"""
FollowUpForm.objects.create(
entity_list=self.entity_list,
xform=self.xform,
)

with self.assertRaises(IntegrityError):
FollowUpForm.objects.create(
entity_list=self.entity_list,
xform=self.xform,
)
32 changes: 31 additions & 1 deletion onadata/apps/viewer/models/data_dictionary.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from pyxform.xls2json import parse_file_to_json
from pyxform.xls2json_backends import xlsx_value_to_str

from onadata.apps.logger.models import EntityList, RegistrationForm
from onadata.apps.logger.models import EntityList, RegistrationForm, FollowUpForm
from onadata.apps.logger.models.xform import XForm, check_version_set, check_xform_uuid
from onadata.apps.logger.xform_instance_parser import XLSFormError
from onadata.libs.utils.cache_tools import (
Expand Down Expand Up @@ -299,3 +299,33 @@ def create_entity_list(sender, instance=None, created=False, **kwargs):
sender=DataDictionary,
dispatch_uid="create_entity_list_datadictionary",
)


def create_follow_up_form(sender, instance=None, created=False, **kwargs):
"""Create a FollowUpForm for a form that consumes entities
Check if a form consumes data from a dataset that is an EntityList. If so,
we create a FollowUpForm
"""
children = instance.json.get("children", [])

for child in children:
if child["type"] == "select one" and "itemset" in child:
dataset_name = child["itemset"].split(".")[0]

try:
entity_list = EntityList.objects.get(
name=dataset_name, project=instance.project
)

except EntityList.DoesNotExist:
continue

FollowUpForm.objects.get_or_create(entity_list=entity_list, xform=instance)


post_save.connect(
create_follow_up_form,
sender=DataDictionary,
dispatch_uid="create_follow_up_datadictionary",
)

0 comments on commit 094bd66

Please sign in to comment.