From e8aeb0db6aea6561ea35886a1955743817ca9c49 Mon Sep 17 00:00:00 2001 From: Yibei Chen Date: Mon, 25 Nov 2024 15:31:02 -0500 Subject: [PATCH] change options for slider (#79) * change options for slider * fix slider choice and comma error * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- reproschema/redcap2reproschema.py | 76 ++++++++++++++--------- reproschema/tests/test_process_choices.py | 17 +++++ 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/reproschema/redcap2reproschema.py b/reproschema/redcap2reproschema.py index 2e93a7d..e6adec3 100644 --- a/reproschema/redcap2reproschema.py +++ b/reproschema/redcap2reproschema.py @@ -200,40 +200,43 @@ def process_choices(choices_str, field_name): choices = [] choices_value_type = [] - for ii, choice in enumerate(choices_str.split("|")): - choice = ( - choice.strip() - ) # Strip leading/trailing whitespace for each choice - parts = [p.strip() for p in choice.split(",")] - - # Handle the case where the choice is something like "1," - if len(parts) == 1: + for choice in choices_str.split("|"): + choice = choice.strip() + + # Split only on the first comma to separate value from label + first_comma_split = choice.split(",", 1) + value_part = first_comma_split[0].strip() + + # Get the full label part (keeping all commas and equals signs) + if len(first_comma_split) > 1: + label_part = first_comma_split[1].strip() + else: + # Handle cases where there's no comma if choice.endswith(","): - parts = [parts[0][:-1], ""] + label_part = "" else: print( - f"Warning: Invalid choice format '{choice}' in a {field_name} field, adding integer as a value" + f"Warning: Invalid choice format '{choice}' in {field_name} field" ) - parts = [ii, parts[0]] + label_part = choice - # Determine if value should be treated as an integer or string - if parts[0] == "0": - # Special case for "0", treat it as an integer + # Determine value type + if value_part == "0": value = 0 choices_value_type.append("xsd:integer") - elif parts[0].isdigit() and parts[0][0] == "0": - # If it has leading zeros, treat it as a string - value = parts[0] + elif value_part.isdigit() and value_part[0] == "0": + value = value_part choices_value_type.append("xsd:string") else: try: - value = int(parts[0]) + value = int(value_part) choices_value_type.append("xsd:integer") except ValueError: - value = parts[0] + value = value_part choices_value_type.append("xsd:string") + choice_obj = { - "name": {"en": " ".join(parts[1:]).strip()}, + "name": {"en": label_part}, "value": value, } choices.append(choice_obj) @@ -308,15 +311,30 @@ def process_row( and value and input_type in ["radio", "select", "slider"] ): - choices, choices_val_type_l = process_choices( - value, field_name=field["Variable / Field Name"] - ) - rowData["responseOptions"].update( - { - "choices": choices, - "valueType": choices_val_type_l, - }, # updating value type for choices (can be int or str) - ) + if input_type == "slider": + # For sliders, add both choices and min/max values + choices, choices_val_type_l = process_choices( + value, field_name=field["Variable / Field Name"] + ) + rowData["responseOptions"].update( + { + "choices": choices, + "valueType": choices_val_type_l, + "minValue": 0, # hardcoded for redcap/now + "maxValue": 100, # hardcoded for redcap/now + } + ) + else: + # For radio and select, just process choices normally + choices, choices_val_type_l = process_choices( + value, field_name=field["Variable / Field Name"] + ) + rowData["responseOptions"].update( + { + "choices": choices, + "valueType": choices_val_type_l, + } + ) # for now adding only for numerics, sometimes can be string or date.. TODO elif ( SCHEMA_MAP.get(key) in RESPONSE_COND diff --git a/reproschema/tests/test_process_choices.py b/reproschema/tests/test_process_choices.py index 147e822..620c157 100644 --- a/reproschema/tests/test_process_choices.py +++ b/reproschema/tests/test_process_choices.py @@ -116,6 +116,23 @@ def test_process_choices_spaces_in_values(): assert sorted(value_types) == ["xsd:string"] +def test_process_choices_likert_scale(): + # Test Likert scale choices with number prefixes in descriptions + choices_str = "0, 0=None (Not at all) | 1, 1=Slight (Rare, less than a day or two) | 2, 2=Mild (Several days) | 3, 3=Moderate (More than half the days) | 4, 4=Severe (Nearly every day)" + choices, value_types = process_choices(choices_str, "likert_scale") + assert choices == [ + {"name": {"en": "0=None (Not at all)"}, "value": 0}, + { + "name": {"en": "1=Slight (Rare, less than a day or two)"}, + "value": 1, + }, + {"name": {"en": "2=Mild (Several days)"}, "value": 2}, + {"name": {"en": "3=Moderate (More than half the days)"}, "value": 3}, + {"name": {"en": "4=Severe (Nearly every day)"}, "value": 4}, + ] + assert value_types == ["xsd:integer"] + + # Run pytest if script is called directly if __name__ == "__main__": pytest.main()