Repository: FOLIO-FSE/folio_migration_tools (language: python)

Issue:
Clarify handling of true/false values for User > addresses > primaryAddress
1. Understand current behaviour
2. Tweak current behaviour if necessary
3. Add an example value to https://github.com/FOLIO-FSE/migration_repo_template/blob/main/mapping_files/user_mapping.json

Current behaviour seems to be that lowercase true/false without quotes is translated to  "", uppercase "True"/"False" with quotes as True. Uppercase True/False without quotes is not valid json. 

Lowercase "true"/"false" seems to work: it becomes to true/"false", which FOLIO translates to true/false.


Discussion / hints:
First, I double-checked what FOLIO expects (bugfest-lotus):
true/"true" is parsed to true in mod-user-import
false/"false" is parsed to false in mod-user-import

Then, I verified what json.loads()  and json.dumps() does:
![image](https://user-images.githubusercontent.com/1894384/171359479-d0874aa1-9285-4c68-b05c-8e8d35fa8ba2.png)

Then, I wrote a test to check that maps with True values in them, are mapped correctly:
![image](https://user-images.githubusercontent.com/1894384/171359904-ef10ac7b-ba44-4fdc-8a74-3f559d53775c.png)

Then,  I wrote the same test, but parsing a json string instead of using a Python dictionary, making sure that the json parsing does not interfere:
![image](https://user-images.githubusercontent.com/1894384/171360550-a1f1b9f9-c5e2-41d1-8040-a4618fa68d58.png)

Then, I updated the user map in the example repo template:
![image](https://user-images.githubusercontent.com/1894384/171362262-51021278-ea82-4946-8da6-af59e34bad3a.png)

Then, I ran the example against bugfest, verifying that the mapping propagated:
![image](https://user-images.githubusercontent.com/1894384/171362583-d4bbeed2-a039-4dc1-b724-3995c2165840.png)

Then, I posted the user to bugfest-Lotus (https://bugfest-lotus.int.aws.folio.org/users/preview/acf72e76-1a31-4301-a681-66bdddadedf3?query=%20kuragin&sort=name)
![image](https://user-images.githubusercontent.com/1894384/171363470-59c86908-4fc5-43da-8683-d25c2e611dd4.png)

Since the behaviour works as expected, I checked the string representations as well:
![image](https://user-images.githubusercontent.com/1894384/171364109-470f4d37-8599-4ca4-9c31-7daa464810d9.png)
 gives 
![image](https://user-images.githubusercontent.com/1894384/171364152-33678b03-23a7-48c8-a96c-1fb02fa5a553.png)

![image](https://user-images.githubusercontent.com/1894384/171364447-d74ef7a5-2d4a-4276-9ca4-48fdffd64e84.png)
gives
![image](https://user-images.githubusercontent.com/1894384/171364501-0549d148-c1a3-4614-8748-e7034392e702.png)




It would be great with some examples and data to try to mimic the behaviour you are experiencing.
Updated example in repo template: https://github.com/FOLIO-FSE/migration_repo_template/commit/87c37a6301ac90b0feef05c2a6a56427c90a08a7

Fix patch:
diff --git a/setup.cfg b/setup.cfg
index f6cb02e4..1f2d86cf 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -23,7 +23,7 @@ ignore =
     W503
 max-complexity = 22
 max-line-length = 99
-exclude = .git,__pycache__,docs/source/conf.py,old,build,dist,tests, src/folio_migration_tools/report_blurbs.py, tests
+exclude = .git,__pycache__,docs/source/conf.py,old,build,dist,tests, src/folio_migration_tools/report_blurbs.py
 statistics = True
 [isort]
 profile=black


Test patch:
diff --git a/tests/test_user_transformer.py b/tests/test_user_transformer.py
index cb115747..e11672aa 100644
--- a/tests/test_user_transformer.py
+++ b/tests/test_user_transformer.py
@@ -1,8 +1,10 @@
+import json
 import logging
 import uuid
 from unittest.mock import MagicMock
 from unittest.mock import Mock
 
+import pytest
 from folioclient import FolioClient
 
 from folio_migration_tools.library_configuration import LibraryConfiguration
@@ -52,6 +54,420 @@ def test_basic():
     assert folio_user["username"] == "user_name_1"
 
 
+def test_boolean_values_explicitly_true():
+    user_map = {
+        "data": [
+            {
+                "folio_field": "username",
+                "legacy_field": "user_name",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "externalSystemId",
+                "legacy_field": "ext_id",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressLine1",
+                "legacy_field": "HOMEADDRESS1",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressLine2",
+                "legacy_field": "HOMEADDRESS2",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressTypeId",
+                "legacy_field": "Not mapped",
+                "value": "Home",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].city",
+                "legacy_field": "HOMECITY",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].postalCode",
+                "legacy_field": "HOMEZIP",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].region",
+                "legacy_field": "HOMESTATE",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].primaryAddress",
+                "legacy_field": "Not mapped",
+                "value": True,
+                "description": "",
+            },
+        ]
+    }
+    legacy_user_record = {
+        "ext_id": "externalid_1",
+        "user_name": "user_name_1",
+        "HOMEADDRESS1": "Line 1",
+        "HOMEADDRESS2": "Line 2",
+        "HOMEZIP": "12345",
+        "HOMESTATE": "Sjuhärad",
+        "HOMECITY": "Fritsla",
+    }
+    mock_library_conf = Mock(spec=LibraryConfiguration)
+    mock_task_config = Mock(spec=UserTransformer.TaskConfiguration)
+    mock_task_config.multi_field_delimiter = "<delimiter>"
+    mock_folio = Mock(spec=FolioClient)
+    mock_folio.okapi_url = "okapi_url"
+    mock_folio.folio_get_single_object = MagicMock(
+        return_value={
+            "instances": {"prefix": "pref", "startNumber": "1"},
+            "holdings": {"prefix": "pref", "startNumber": "1"},
+        }
+    )
+    user_mapper = UserMapper(mock_folio, mock_task_config, mock_library_conf, None, None)
+    folio_user = user_mapper.do_map(legacy_user_record, user_map, "001")
+    assert folio_user["personal"]["addresses"][0]["primaryAddress"] is True
+
+
+def test_boolean_values_explicitly_true_json_string():
+    user_map_str = '{ "data": [ { "folio_field": "username", "legacy_field": "user_name", "value": "", "description": "" }, { "folio_field": "externalSystemId", "legacy_field": "ext_id", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].addressLine1", "legacy_field": "HOMEADDRESS1", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].addressLine2", "legacy_field": "HOMEADDRESS2", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].addressTypeId", "legacy_field": "Not mapped", "value": "Home", "description": "" }, { "folio_field": "personal.addresses[0].city", "legacy_field": "HOMECITY", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].postalCode", "legacy_field": "HOMEZIP", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].region", "legacy_field": "HOMESTATE", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].primaryAddress", "legacy_field": "Not mapped", "value": true, "description": "" } ] }'  # noqa
+    user_map = json.loads(user_map_str)
+    legacy_user_record = {
+        "ext_id": "externalid_1",
+        "user_name": "user_name_1",
+        "HOMEADDRESS1": "Line 1",
+        "HOMEADDRESS2": "Line 2",
+        "HOMEZIP": "12345",
+        "HOMESTATE": "Sjuhärad",
+        "HOMECITY": "Fritsla",
+    }
+    mock_library_conf = Mock(spec=LibraryConfiguration)
+    mock_task_config = Mock(spec=UserTransformer.TaskConfiguration)
+    mock_task_config.multi_field_delimiter = "<delimiter>"
+    mock_folio = Mock(spec=FolioClient)
+    mock_folio.okapi_url = "okapi_url"
+    mock_folio.folio_get_single_object = MagicMock(
+        return_value={
+            "instances": {"prefix": "pref", "startNumber": "1"},
+            "holdings": {"prefix": "pref", "startNumber": "1"},
+        }
+    )
+    user_mapper = UserMapper(mock_folio, mock_task_config, mock_library_conf, None, None)
+    folio_user = user_mapper.do_map(legacy_user_record, user_map, "001")
+    assert folio_user["personal"]["addresses"][0]["primaryAddress"] is True
+
+
+def test_boolean_values_explicitly_false_json_string():
+    user_map_str = '{ "data": [ { "folio_field": "username", "legacy_field": "user_name", "value": "", "description": "" }, { "folio_field": "externalSystemId", "legacy_field": "ext_id", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].addressLine1", "legacy_field": "HOMEADDRESS1", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].addressLine2", "legacy_field": "HOMEADDRESS2", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].addressTypeId", "legacy_field": "Not mapped", "value": "Home", "description": "" }, { "folio_field": "personal.addresses[0].city", "legacy_field": "HOMECITY", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].postalCode", "legacy_field": "HOMEZIP", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].region", "legacy_field": "HOMESTATE", "value": "", "description": "" }, { "folio_field": "personal.addresses[0].primaryAddress", "legacy_field": "Not mapped", "value": false, "description": "" } ] }'  # noqa: E501, B950
+    user_map = json.loads(user_map_str)
+    legacy_user_record = {
+        "ext_id": "externalid_1",
+        "user_name": "user_name_1",
+        "HOMEADDRESS1": "Line 1",
+        "HOMEADDRESS2": "Line 2",
+        "HOMEZIP": "12345",
+        "HOMESTATE": "Sjuhärad",
+        "HOMECITY": "Fritsla",
+    }
+    mock_library_conf = Mock(spec=LibraryConfiguration)
+    mock_task_config = Mock(spec=UserTransformer.TaskConfiguration)
+    mock_task_config.multi_field_delimiter = "<delimiter>"
+    mock_folio = Mock(spec=FolioClient)
+    mock_folio.okapi_url = "okapi_url"
+    mock_folio.folio_get_single_object = MagicMock(
+        return_value={
+            "instances": {"prefix": "pref", "startNumber": "1"},
+            "holdings": {"prefix": "pref", "startNumber": "1"},
+        }
+    )
+    user_mapper = UserMapper(mock_folio, mock_task_config, mock_library_conf, None, None)
+    folio_user = user_mapper.do_map(legacy_user_record, user_map, "001")
+    assert folio_user["personal"]["addresses"][0]["primaryAddress"] is False
+
+
+def test_boolean_values_explicitly_true_string():
+    user_map = {
+        "data": [
+            {
+                "folio_field": "username",
+                "legacy_field": "user_name",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "externalSystemId",
+                "legacy_field": "ext_id",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressLine1",
+                "legacy_field": "HOMEADDRESS1",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressLine2",
+                "legacy_field": "HOMEADDRESS2",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressTypeId",
+                "legacy_field": "Not mapped",
+                "value": "Home",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].city",
+                "legacy_field": "HOMECITY",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].postalCode",
+                "legacy_field": "HOMEZIP",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].region",
+                "legacy_field": "HOMESTATE",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].primaryAddress",
+                "legacy_field": "Not mapped",
+                "value": "true",
+                "description": "",
+            },
+        ]
+    }
+    legacy_user_record = {
+        "ext_id": "externalid_1",
+        "user_name": "user_name_1",
+        "HOMEADDRESS1": "Line 1",
+        "HOMEADDRESS2": "Line 2",
+        "HOMEZIP": "12345",
+        "HOMESTATE": "Sjuhärad",
+        "HOMECITY": "Fritsla",
+    }
+    mock_library_conf = Mock(spec=LibraryConfiguration)
+    mock_task_config = Mock(spec=UserTransformer.TaskConfiguration)
+    mock_task_config.multi_field_delimiter = "<delimiter>"
+    mock_folio = Mock(spec=FolioClient)
+    mock_folio.okapi_url = "okapi_url"
+    mock_folio.folio_get_single_object = MagicMock(
+        return_value={
+            "instances": {"prefix": "pref", "startNumber": "1"},
+            "holdings": {"prefix": "pref", "startNumber": "1"},
+        }
+    )
+    user_mapper = UserMapper(mock_folio, mock_task_config, mock_library_conf, None, None)
+    folio_user = user_mapper.do_map(legacy_user_record, user_map, "001")
+
+    assert folio_user["personal"]["addresses"][0]["primaryAddress"] == "true"
+
+
+def test_boolean_values_explicitly_false_string():
+    user_map = {
+        "data": [
+            {
+                "folio_field": "username",
+                "legacy_field": "user_name",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "externalSystemId",
+                "legacy_field": "ext_id",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressLine1",
+                "legacy_field": "HOMEADDRESS1",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressLine2",
+                "legacy_field": "HOMEADDRESS2",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressTypeId",
+                "legacy_field": "Not mapped",
+                "value": "Home",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].city",
+                "legacy_field": "HOMECITY",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].postalCode",
+                "legacy_field": "HOMEZIP",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].region",
+                "legacy_field": "HOMESTATE",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].primaryAddress",
+                "legacy_field": "Not mapped",
+                "value": "false",
+                "description": "",
+            },
+        ]
+    }
+    legacy_user_record = {
+        "ext_id": "externalid_1",
+        "user_name": "user_name_1",
+        "HOMEADDRESS1": "Line 1",
+        "HOMEADDRESS2": "Line 2",
+        "HOMEZIP": "12345",
+        "HOMESTATE": "Sjuhärad",
+        "HOMECITY": "Fritsla",
+    }
+    mock_library_conf = Mock(spec=LibraryConfiguration)
+    mock_task_config = Mock(spec=UserTransformer.TaskConfiguration)
+    mock_task_config.multi_field_delimiter = "<delimiter>"
+    mock_folio = Mock(spec=FolioClient)
+    mock_folio.okapi_url = "okapi_url"
+    mock_folio.folio_get_single_object = MagicMock(
+        return_value={
+            "instances": {"prefix": "pref", "startNumber": "1"},
+            "holdings": {"prefix": "pref", "startNumber": "1"},
+        }
+    )
+    user_mapper = UserMapper(mock_folio, mock_task_config, mock_library_conf, None, None)
+    folio_user = user_mapper.do_map(legacy_user_record, user_map, "001")
+
+    assert folio_user["personal"]["addresses"][0]["primaryAddress"] == "false"
+
+
+def test_boolean_values_explicitly_false():
+    user_map = {
+        "data": [
+            {
+                "folio_field": "username",
+                "legacy_field": "user_name",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "externalSystemId",
+                "legacy_field": "ext_id",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressLine1",
+                "legacy_field": "HOMEADDRESS1",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressLine2",
+                "legacy_field": "HOMEADDRESS2",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].addressTypeId",
+                "legacy_field": "Not mapped",
+                "value": "Home",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].city",
+                "legacy_field": "HOMECITY",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].postalCode",
+                "legacy_field": "HOMEZIP",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].region",
+                "legacy_field": "HOMESTATE",
+                "value": "",
+                "description": "",
+            },
+            {
+                "folio_field": "personal.addresses[0].primaryAddress",
+                "legacy_field": "Not mapped",
+                "value": False,
+                "description": "",
+            },
+        ]
+    }
+    legacy_user_record = {
+        "ext_id": "externalid_1",
+        "user_name": "user_name_1",
+        "HOMEADDRESS1": "Line 1",
+        "HOMEADDRESS2": "Line 2",
+        "HOMEZIP": "12345",
+        "HOMESTATE": "Sjuhärad",
+        "HOMECITY": "Fritsla",
+    }
+    mock_library_conf = Mock(spec=LibraryConfiguration)
+    mock_task_config = Mock(spec=UserTransformer.TaskConfiguration)
+    mock_task_config.multi_field_delimiter = "<delimiter>"
+    mock_folio = Mock(spec=FolioClient)
+    mock_folio.okapi_url = "okapi_url"
+    mock_folio.folio_get_single_object = MagicMock(
+        return_value={
+            "instances": {"prefix": "pref", "startNumber": "1"},
+            "holdings": {"prefix": "pref", "startNumber": "1"},
+        }
+    )
+    user_mapper = UserMapper(mock_folio, mock_task_config, mock_library_conf, None, None)
+    folio_user = user_mapper.do_map(legacy_user_record, user_map, "001")
+
+    assert folio_user["personal"]["addresses"][0]["primaryAddress"] is False
+
+
+def test_json_load_s_booleans():
+    json_str = '{"a": true, "b": "true", "c": false, "d": "false", "e": "True", "f": "False"}'
+    my_json = json.loads(json_str)
+    assert my_json["a"] is True
+    assert my_json["b"] is not True
+    assert my_json["c"] is False
+    assert my_json["d"] is not True and my_json["d"] is not False
+    assert my_json["e"] is not True and my_json["e"] is not False
+    assert my_json["f"] is not True and my_json["f"] is not False
+    serialized_json = json.dumps(my_json)
+    assert json_str == serialized_json
+
+
+def test_json_load_s_booleans_bad_json():
+    json_str = '{"a": True}'
+    with pytest.raises(Exception):
+        json.loads(json_str)
+
+
 def test_notes(caplog):
     caplog.set_level(25)
     user_map = {