diff --git a/onadata/apps/logger/tests/models/test_instance.py b/onadata/apps/logger/tests/models/test_instance.py index 480e9055ed..1b0db9ea4e 100644 --- a/onadata/apps/logger/tests/models/test_instance.py +++ b/onadata/apps/logger/tests/models/test_instance.py @@ -9,6 +9,7 @@ from datetime import datetime, timedelta from unittest.mock import Mock, patch +from django.contrib.contenttypes.models import ContentType from django.http.request import HttpRequest from django.test import override_settings from django.utils.timezone import utc @@ -1231,8 +1232,18 @@ def test_repeat_columns_registered(self): | | Births | births | | """ xform = self._publish_markdown(md, self.user, project) - metadata = MetaData.objects.get(data_type="export_columns_register") - ordered_columns = json.loads(metadata.extra_data, object_pairs_hook=OrderedDict) + register = MetaData.objects.get( + data_type="export_columns_register", + object_id=xform.pk, + content_type=ContentType.objects.get_for_model(xform), + ) + # Default export columns are correctly registered + merged_multiples_columns = json.loads( + register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples_columns = json.loads( + register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) expected_columns = OrderedDict( [ ( @@ -1246,8 +1257,8 @@ def test_repeat_columns_registered(self): ("meta/instanceID", None), ] ) - # Default export columns are correctly registered - self.assertEqual(ordered_columns, expected_columns) + self.assertEqual(merged_multiples_columns, expected_columns) + self.assertEqual(split_multiples_columns, expected_columns) xml = ( '' @@ -1279,8 +1290,13 @@ def test_repeat_columns_registered(self): ) # Repeats are registered on creation instance = Instance.objects.create(xml=xml, user=self.user, xform=xform) - metadata.refresh_from_db() - ordered_columns = json.loads(metadata.extra_data, object_pairs_hook=OrderedDict) + register.refresh_from_db() + merged_multiples_columns = json.loads( + register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples_columns = json.loads( + register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) expected_columns = OrderedDict( [ ( @@ -1302,7 +1318,8 @@ def test_repeat_columns_registered(self): ] ) - self.assertEqual(ordered_columns, expected_columns) + self.assertEqual(merged_multiples_columns, expected_columns) + self.assertEqual(split_multiples_columns, expected_columns) # Repeats are registered on update xml = ( @@ -1344,8 +1361,13 @@ def test_repeat_columns_registered(self): instance.xml = xml instance.uuid = "51cb9e07-cfc7-413b-bc22-ee7adfa9dec4" instance.save() - metadata.refresh_from_db() - ordered_columns = json.loads(metadata.extra_data, object_pairs_hook=OrderedDict) + register.refresh_from_db() + merged_multiples_columns = json.loads( + register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples_columns = json.loads( + register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) expected_columns = OrderedDict( [ ( @@ -1373,4 +1395,5 @@ def test_repeat_columns_registered(self): ] ) - self.assertEqual(ordered_columns, expected_columns) + self.assertEqual(merged_multiples_columns, expected_columns) + self.assertEqual(split_multiples_columns, expected_columns) diff --git a/onadata/apps/viewer/models/data_dictionary.py b/onadata/apps/viewer/models/data_dictionary.py index 8b9721281e..b7cdd11994 100644 --- a/onadata/apps/viewer/models/data_dictionary.py +++ b/onadata/apps/viewer/models/data_dictionary.py @@ -452,13 +452,17 @@ def create_or_update_export_register(sender, instance=None, created=False, **kwa csv_builder.CSVDataFrameBuilder._build_ordered_columns( instance._get_survey(), ordered_columns ) + serialized_columns = json.dumps(ordered_columns) MetaData.objects.update_or_create( content_type=ContentType.objects.get_for_model(instance), object_id=instance.pk, data_type=EXPORT_COLUMNS_REGISTER, defaults={ "data_value": "", - "extra_data": json.dumps(ordered_columns), + "extra_data": { + "merged_multiples": serialized_columns, + "split_multiples": serialized_columns, + }, }, ) diff --git a/onadata/apps/viewer/models/tests/test_data_dictionary.py b/onadata/apps/viewer/models/tests/test_data_dictionary.py index ca27c88af6..d7a9b1d9c8 100644 --- a/onadata/apps/viewer/models/tests/test_data_dictionary.py +++ b/onadata/apps/viewer/models/tests/test_data_dictionary.py @@ -313,7 +313,15 @@ def test_cache_invalidated(self): def test_export_columns_register_created(self): """Export columns register is created when form is published""" - xform = self._publish_markdown(self.registration_form, self.user) + md = """ + | survey | + | | type | name | label | + | | text | name | First Name | + | settings| | | | + | | form_title | form_id | | + | | Students | students | | + """ + xform = self._publish_markdown(md, self.user) content_type = ContentType.objects.get_for_model(xform) exists = MetaData.objects.filter( data_type="export_columns_register", @@ -323,6 +331,27 @@ def test_export_columns_register_created(self): self.assertTrue(exists) + register = MetaData.objects.get( + data_type="export_columns_register", + object_id=xform.pk, + content_type=content_type, + ) + merged_multiples_columns = json.loads( + register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples_columns = json.loads( + register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) + expected_columns = OrderedDict( + [ + ("name", None), + ("meta/instanceID", None), + ] + ) + + self.assertEqual(merged_multiples_columns, expected_columns) + self.assertEqual(split_multiples_columns, expected_columns) + @patch("onadata.apps.logger.tasks.reconstruct_xform_export_register_async.delay") def test_export_columns_register_updated(self, mock_register_xform_columns): """Export columns register is updated when form is replaced""" @@ -341,14 +370,21 @@ def test_export_columns_register_updated(self, mock_register_xform_columns): object_id=xform.pk, content_type=content_type, ) - ordered_columns = json.loads(register.extra_data, object_pairs_hook=OrderedDict) + merged_multiples_columns = json.loads( + register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples_columns = json.loads( + register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) expected_columns = OrderedDict( [ ("name", None), ("meta/instanceID", None), ] ) - self.assertEqual(ordered_columns, expected_columns) + + self.assertEqual(merged_multiples_columns, expected_columns) + self.assertEqual(split_multiples_columns, expected_columns) # Replace form md = """ | survey | @@ -361,7 +397,12 @@ def test_export_columns_register_updated(self, mock_register_xform_columns): """ self._replace_form(md, xform) register.refresh_from_db() - ordered_columns = json.loads(register.extra_data, object_pairs_hook=OrderedDict) + merged_multiples_columns = json.loads( + register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples_columns = json.loads( + register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) expected_columns = OrderedDict( [ ("name", None), @@ -369,6 +410,8 @@ def test_export_columns_register_updated(self, mock_register_xform_columns): ("meta/instanceID", None), ] ) - self.assertEqual(ordered_columns, expected_columns) + + self.assertEqual(merged_multiples_columns, expected_columns) + self.assertEqual(split_multiples_columns, expected_columns) # Task is called to add columns for repeat data mock_register_xform_columns.assert_called_once_with(xform.pk) diff --git a/onadata/libs/tests/utils/test_csv_builder.py b/onadata/libs/tests/utils/test_csv_builder.py index 45b98c8dd9..f5fca15f08 100644 --- a/onadata/libs/tests/utils/test_csv_builder.py +++ b/onadata/libs/tests/utils/test_csv_builder.py @@ -130,6 +130,83 @@ def _publish_grouped_gps_form(self): # pylint: disable=attribute-defined-outside-init self.survey_name = "grouped_gps" + def _publish_select_multiples_grouped_repeating(self): + md = """ + | survey | + | | type | name | label | + | | text | name | Name | + | | integer | age | Age | + | | begin repeat | browser_use | Browser Use | + | | integer | year | Year | + | | select_multiple browsers | browsers | Browsers | + | | end repeat | | | + + | choices | + | | list name | name | label | + | | browsers | firefox | Firefox | + | | browsers | chrome | Chrome | + | | browsers | ie | Internet Explorer | + | | browsers | safari | Safari | + """ + xform = self._publish_markdown(md, self.user, id_string="browser_use") + return xform + + def _register_select_multiples_grouped_repeating(self, xform): + MetaData.objects.create( + content_type=ContentType.objects.get_for_model(xform), + object_id=xform.pk, + data_type="export_columns_register", + data_value="", + extra_data={ + "merged_multiples": json.dumps( + OrderedDict( + [ + ("name", None), + ("age", None), + ( + "browser_use", + ["browser_use[1]/year", "browser_use[1]/browsers"], + ), + ("meta/instanceID", None), + ] + ) + ), + "split_multiples": json.dumps( + OrderedDict( + [ + ("name", None), + ("age", None), + ( + "browser_use", + [ + "browser_use[1]/year", + "browser_use[1]/browsers/firefox", + "browser_use[1]/browsers/chrome", + "browser_use[1]/browsers/ie", + "browser_use[1]/browsers/safari", + ], + ), + ("meta/instanceID", None), + ] + ) + ), + }, + ) + cursor = [ + { + "name": "Tom", + "age": 23, + "browser_use": [ + { + "browser_use/year": "2010", + "browser_use/browsers": "firefox safari", + }, + ], + } + ] + + return cursor + def _csv_data_for_dataframe(self): csv_df_builder = CSVDataFrameBuilder( self.user.username, self.xform.id_string, include_images=False @@ -2174,93 +2251,10 @@ def test_extra_columns_dataview(self): header = next(csv_reader) self.assertEqual(header, ["age", extra_col]) - def test_registered_export_columns(self): - """Registered export columns are used to generate export""" - md_xform = """ - | survey | - | | type | name | label | - | | begin repeat | hospital_repeat | | - | | text | hospital | Hospital Name | - | | begin repeat | child_repeat | | - | | text | name | Child Name | - | | decimal | birthweight | Birth Weight | - | | select_one male_female | sex | Child sex | - | | end repeat | | | - | | end repeat | | | - | choices | | | | - | | list name | name | label | - | | male_female | male | Male | - | | male_female | female | Female | - """ - self._publish_markdown(md_xform, self.user, id_string="nested_repeats") - xform = XForm.objects.get(user=self.user, id_string="nested_repeats") - cursor = [ - { - "hospital_repeat": [ - { - "hospital_repeat/hospital": "Aga Khan", - "hospital_repeat/child_repeat": [ - { - "hospital_repeat/child_repeat/sex": "male", - "hospital_repeat/child_repeat/name": "Zakayo", - "hospital_repeat/child_repeat/birthweight": 3.3, - }, - { - "hospital_repeat/child_repeat/sex": "female", - "hospital_repeat/child_repeat/name": "Melania", - "hospital_repeat/child_repeat/birthweight": 3.5, - }, - ], - }, - { - "hospital_repeat/hospital": "Mama Lucy", - "hospital_repeat/child_repeat": [ - { - "hospital_repeat/child_repeat/sex": "female", - "hospital_repeat/child_repeat/name": "Winnie", - "hospital_repeat/child_repeat/birthweight": 3.1, - } - ], - }, - ], - } - ] - content_type = ContentType.objects.get_for_model(xform) - # Simulate registered export columns - MetaData.objects.create( - content_type=content_type, - object_id=xform.pk, - data_type="export_columns_register", - data_value="", - extra_data=json.dumps( - OrderedDict( - [ - ( - "hospital_repeat", - [ - "hospital_repeat[1]/hospital", - "hospital_repeat[2]/hospital", - ], - ), - ( - "hospital_repeat/child_repeat", - [ - "hospital_repeat[1]/child_repeat[1]/name", - "hospital_repeat[1]/child_repeat[1]/birthweight", - "hospital_repeat[1]/child_repeat[1]/sex", - "hospital_repeat[1]/child_repeat[2]/name", - "hospital_repeat[1]/child_repeat[2]/birthweight", - "hospital_repeat[1]/child_repeat[2]/sex", - "hospital_repeat[2]/child_repeat[1]/name", - "hospital_repeat[2]/child_repeat[1]/birthweight", - "hospital_repeat[2]/child_repeat[1]/sex", - ], - ), - ("meta/instanceID", None), - ] - ) - ), - ) + def test_export_register_split_multiples(self): + """Export register works with split multiples""" + xform = self._publish_select_multiples_grouped_repeating() + cursor = self._register_select_multiples_grouped_repeating(xform) builder = CSVDataFrameBuilder( self.user.username, xform.id_string, @@ -2272,17 +2266,13 @@ def test_registered_export_columns(self): csv_reader = csv.reader(csv_file) header = next(csv_reader) expected_header = [ - "hospital_repeat[1]/hospital", - "hospital_repeat[2]/hospital", - "hospital_repeat[1]/child_repeat[1]/name", - "hospital_repeat[1]/child_repeat[1]/birthweight", - "hospital_repeat[1]/child_repeat[1]/sex", - "hospital_repeat[1]/child_repeat[2]/name", - "hospital_repeat[1]/child_repeat[2]/birthweight", - "hospital_repeat[1]/child_repeat[2]/sex", - "hospital_repeat[2]/child_repeat[1]/name", - "hospital_repeat[2]/child_repeat[1]/birthweight", - "hospital_repeat[2]/child_repeat[1]/sex", + "name", + "age", + "browser_use[1]/year", + "browser_use[1]/browsers/firefox", + "browser_use[1]/browsers/chrome", + "browser_use[1]/browsers/ie", + "browser_use[1]/browsers/safari", "meta/instanceID", "_id", "_uuid", @@ -2300,17 +2290,13 @@ def test_registered_export_columns(self): self.assertCountEqual(header, expected_header) row = next(csv_reader) expected_row = [ - "Aga Khan", - "Mama Lucy", - "Zakayo", - "3.3", - "male", - "Melania", - "3.5", - "female", - "Winnie", - "3.1", - "female", + "Tom", + "23", + "2010", + "True", + "False", + "False", + "True", "n/a", "n/a", "n/a", @@ -2328,6 +2314,64 @@ def test_registered_export_columns(self): self.assertEqual(row, expected_row) csv_file.close() + def test_export_register_merged_multiples(self): + """Export register works with merged multiples""" + xform = self._publish_select_multiples_grouped_repeating() + cursor = self._register_select_multiples_grouped_repeating(xform) + builder = CSVDataFrameBuilder( + self.user.username, + xform.id_string, + include_images=False, + split_select_multiples=False, + ) + temp_file = NamedTemporaryFile(suffix=".csv", delete=False) + builder.export_to(temp_file.name, cursor) + csv_file = open(temp_file.name, "r") + csv_reader = csv.reader(csv_file) + header = next(csv_reader) + expected_header = [ + "name", + "age", + "browser_use[1]/year", + "browser_use[1]/browsers", + "meta/instanceID", + "_id", + "_uuid", + "_submission_time", + "_date_modified", + "_tags", + "_notes", + "_version", + "_duration", + "_submitted_by", + "_total_media", + "_media_count", + "_media_all_received", + ] + self.assertCountEqual(header, expected_header) + row = next(csv_reader) + expected_row = [ + "Tom", + "23", + "2010", + "firefox safari", + "n/a", + "n/a", + "n/a", + "n/a", + "n/a", + "n/a", + "n/a", + "n/a", + "n/a", + "n/a", + "n/a", + "n/a", + "n/a", + ] + self.assertCountEqual(row, expected_row) + csv_file.close() + def test_export_columns_register_missing(self): """Export columns register not found""" md_xform = """ diff --git a/onadata/libs/tests/utils/test_logger_tools.py b/onadata/libs/tests/utils/test_logger_tools.py index 08720909cc..12bdc1586f 100644 --- a/onadata/libs/tests/utils/test_logger_tools.py +++ b/onadata/libs/tests/utils/test_logger_tools.py @@ -1227,9 +1227,13 @@ def setUp(self): def test_columns_added(self): """Incoming columns are added to the register""" - ordered_columns = json.loads( - self.register.extra_data, object_pairs_hook=OrderedDict + merged_multiples = json.loads( + self.register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict ) + split_multiples = json.loads( + self.register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) + # Before Instance repeat columns are added expected_columns = OrderedDict( [ ( @@ -1243,13 +1247,18 @@ def test_columns_added(self): ("meta/instanceID", None), ] ) - self.assertEqual(ordered_columns, expected_columns) + self.assertEqual(merged_multiples, expected_columns) + self.assertEqual(split_multiples, expected_columns) register_instance_repeat_columns(self.instance) + # After Instance repeat columns are added self.register.refresh_from_db() - ordered_columns = json.loads( - self.register.extra_data, object_pairs_hook=OrderedDict + merged_multiples = json.loads( + self.register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples = json.loads( + self.register.extra_data["split_multiples"], object_pairs_hook=OrderedDict ) expected_columns = OrderedDict( [ @@ -1272,7 +1281,8 @@ def test_columns_added(self): ] ) - self.assertEqual(ordered_columns, expected_columns) + self.assertEqual(merged_multiples, expected_columns) + self.assertEqual(split_multiples, expected_columns) def test_register_not_found(self): """Nothing happens if export columns register is not found""" @@ -1282,6 +1292,113 @@ def test_register_not_found(self): exists = MetaData.objects.filter(data_type="export_columns_register").exists() self.assertFalse(exists) + def test_select_multiples(self): + """Columns for a form with select multiples are added""" + md = """ + | survey | + | | type | name | label | + | | text | name | Name | + | | integer | age | Age | + | | begin repeat | browser_use | Browser Use | + | | integer | year | Year | + | | select_multiple browsers | browsers | Browsers | + | | end repeat | | | + + | choices | + | | list name | name | label | + | | browsers | firefox | Firefox | + | | browsers | chrome | Chrome | + | | browsers | ie | Internet Explorer | + | | browsers | safari | Safari | + """ + xform = self._publish_markdown( + md, self.user, self.project, id_string="browser_use" + ) + register = MetaData.objects.get( + data_type="export_columns_register", + object_id=xform.pk, + content_type=ContentType.objects.get_for_model(xform), + ) + # Before Instance repeat columns are added + merged_multiples_columns = json.loads( + register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples_columns = json.loads( + register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) + expected_columns = OrderedDict( + [ + ("name", None), + ("age", None), + ("browser_use", []), + ("meta/instanceID", None), + ] + ) + + self.assertEqual(merged_multiples_columns, expected_columns) + self.assertEqual(split_multiples_columns, expected_columns) + + xml = ( + '' + '' + f"{xform.uuid}" + "John Doe" + "25" + "" + "2021" + "firefox chrome" + "" + "" + "uuid:cea7954a-60d5-4f40-b844-080733a74a34" + "" + "" + ) + instance = Instance.objects.create(xml=xml, user=self.user, xform=xform) + + register_instance_repeat_columns(instance) + + # After Instance repeat columns are added + register.refresh_from_db() + merged_multiples_columns = json.loads( + register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples_columns = json.loads( + register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) + + self.assertEqual( + split_multiples_columns, + OrderedDict( + [ + ("name", None), + ("age", None), + ( + "browser_use", + [ + "browser_use[1]/year", + "browser_use[1]/browsers/firefox", + "browser_use[1]/browsers/chrome", + "browser_use[1]/browsers/ie", + "browser_use[1]/browsers/safari", + ], + ), + ("meta/instanceID", None), + ] + ), + ) + self.assertEqual( + merged_multiples_columns, + OrderedDict( + [ + ("name", None), + ("age", None), + ("browser_use", ["browser_use[1]/year", "browser_use[1]/browsers"]), + ("meta/instanceID", None), + ] + ), + ) + class ReconstructXFormExportRegisterTestCase(TestBase): """Tests for method `reconstruct_xform_export_register`""" @@ -1369,9 +1486,13 @@ def setUp(self): def test_register(self): """Repeats from all instances are registered""" - ordered_columns = json.loads( - self.register.extra_data, object_pairs_hook=OrderedDict + merged_multiples_columns = json.loads( + self.register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict ) + split_multiples_columns = json.loads( + self.register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) + # Before reconstructing export columns register expected_columns = OrderedDict( [ ( @@ -1385,16 +1506,23 @@ def test_register(self): ("meta/instanceID", None), ] ) - self.assertEqual(ordered_columns, expected_columns) + + self.assertEqual(merged_multiples_columns, expected_columns) + self.assertEqual(split_multiples_columns, expected_columns) reconstruct_xform_export_register(self.xform) + # After reconstructing register self.register.refresh_from_db() - ordered_columns = json.loads( - self.register.extra_data, object_pairs_hook=OrderedDict + merged_multiples_columns = json.loads( + self.register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples_columns = json.loads( + self.register.extra_data["split_multiples"], object_pairs_hook=OrderedDict ) - self.assertEqual(ordered_columns, self.expected_columns) + self.assertEqual(merged_multiples_columns, self.expected_columns) + self.assertEqual(split_multiples_columns, self.expected_columns) def test_register_not_found(self): """Register is created if not found""" @@ -1410,6 +1538,12 @@ def test_register_not_found(self): object_id=self.xform.pk, content_type=ContentType.objects.get_for_model(self.xform), ) - ordered_columns = json.loads(register.extra_data, object_pairs_hook=OrderedDict) + merged_multiples_columns = json.loads( + register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples_columns = json.loads( + register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) - self.assertEqual(ordered_columns, self.expected_columns) + self.assertEqual(merged_multiples_columns, self.expected_columns) + self.assertEqual(split_multiples_columns, self.expected_columns) diff --git a/onadata/libs/utils/csv_builder.py b/onadata/libs/utils/csv_builder.py index 814a40b1b9..391ef4adb8 100644 --- a/onadata/libs/utils/csv_builder.py +++ b/onadata/libs/utils/csv_builder.py @@ -854,8 +854,15 @@ def export_to(self, path, cursor, dataview=None): reconstruct_xform_export_register_async.delay(self.xform.pk) else: + serialized_columns = columns_register.extra_data.get("split_multiples") + + if not self.split_select_multiples: + serialized_columns = columns_register.extra_data.get( + "merged_multiples" + ) + self.ordered_columns = json.loads( - columns_register.extra_data, object_pairs_hook=OrderedDict + serialized_columns, object_pairs_hook=OrderedDict ) self._add_ordered_columns_for_select_multiples() diff --git a/onadata/libs/utils/logger_tools.py b/onadata/libs/utils/logger_tools.py index 1d75272f0c..73b8cd3e31 100644 --- a/onadata/libs/utils/logger_tools.py +++ b/onadata/libs/utils/logger_tools.py @@ -1540,7 +1540,12 @@ def _register_instance_repeat_columns(instance: Instance, register: MetaData) -> # Only one process updates it at a time. This prevents race conditions # and updates extra_data atomically register = MetaData.objects.select_for_update().get(pk=register.pk) - ordered_columns = json.loads(register.extra_data, object_pairs_hook=OrderedDict) + merged_multiples = json.loads( + register.extra_data["merged_multiples"], object_pairs_hook=OrderedDict + ) + split_multiples = json.loads( + register.extra_data["split_multiples"], object_pairs_hook=OrderedDict + ) xform = instance.xform csv_builder = csv_builder.CSVDataFrameBuilder( xform=xform, username=xform.user.username, id_string=xform.id_string @@ -1552,13 +1557,26 @@ def _register_instance_repeat_columns(instance: Instance, register: MetaData) -> csv_builder._reindex( key, value, - ordered_columns, + split_multiples, data, - instance.xform, + xform, include_images=[], + split_select_multiples=True, + ) + csv_builder._reindex( + key, + value, + merged_multiples, + data, + xform, + include_images=[], + split_select_multiples=False, ) - register.extra_data = json.dumps(ordered_columns) + register.extra_data = { + "merged_multiples": json.dumps(merged_multiples), + "split_multiples": json.dumps(split_multiples), + } register.save() @@ -1596,13 +1614,17 @@ def reconstruct_xform_export_register(xform: XForm) -> None: csv_builder.CSVDataFrameBuilder._build_ordered_columns( xform._get_survey(), ordered_columns ) + serialized_columns = json.dumps(ordered_columns) metadata, _ = MetaData.objects.get_or_create( content_type=ContentType.objects.get_for_model(xform), object_id=xform.pk, data_type=EXPORT_COLUMNS_REGISTER, defaults={ "data_value": "", - "extra_data": json.dumps(ordered_columns), + "extra_data": { + "merged_multiples": serialized_columns, + "split_multiples": serialized_columns, + }, }, ) instance_qs = xform.instances.filter(deleted_at__isnull=True)