Repository: Sage-Bionetworks/Genie

Issue:
Make validation of SEQ_ASSAY_IDs case-insensitive
The SEQ_ASSAY_IDs in the assay_information.yaml are being compared to the 'Sample Clinical Database' table (syn7517674), which converts the SEQ_ASSAY_IDs to all uppercase.  If the SEQ_ASSAY_IDs assay_information.yaml contain lowercase characters, they will be erroneously flagged.  

Discussion:

Patch:
diff --git a/genie/process_functions.py b/genie/process_functions.py
index ef0b745..3cd5e65 100644
--- a/genie/process_functions.py
+++ b/genie/process_functions.py
@@ -876,18 +876,21 @@ def check_col_and_values(
                 final.extend(value.split(sep))
             check_values = pd.Series(final)
         if not check_values.isin(possible_values).all():
-            error = "{filename}: Please double check your {col} column.  " "This column must only be these values: {possible_vals}\n".format(
-                filename=filename,
-                col=col,
-                possible_vals=", ".join(
-                    [
-                        # This is done because of pandas typing.
-                        # An integer column with one NA/blank value
-                        # will be cast as a double.
-                        str(value).replace(".0", "")
-                        for value in possible_values
-                    ]
-                ),
+            error = (
+                "{filename}: Please double check your {col} column.  "
+                "This column must only be these values: {possible_vals}\n".format(
+                    filename=filename,
+                    col=col,
+                    possible_vals=", ".join(
+                        [
+                            # This is done because of pandas typing.
+                            # An integer column with one NA/blank value
+                            # will be cast as a double.
+                            str(value).replace(".0", "")
+                            for value in possible_values
+                        ]
+                    ),
+                )
             )
     return (warning, error)
 
diff --git a/genie_registry/assay.py b/genie_registry/assay.py
index 5cf70ad..eba1ec7 100644
--- a/genie_registry/assay.py
+++ b/genie_registry/assay.py
@@ -141,7 +141,11 @@ class Assayinfo(FileTypeFormat):
         warning = ""
 
         if process_functions.checkColExist(assay_info_df, "SEQ_ASSAY_ID"):
-            all_seq_assays = assay_info_df.SEQ_ASSAY_ID.unique()
+            all_seq_assays = (
+                assay_info_df.SEQ_ASSAY_ID.replace({"_": "-"}, regex=True)
+                .str.upper()
+                .unique()
+            )
             if not all([assay.startswith(self.center) for assay in all_seq_assays]):
                 total_error += (
                     "Assay_information.yaml: Please make sure all your "
@@ -160,7 +164,12 @@ class Assayinfo(FileTypeFormat):
             )
             # These are all the SEQ_ASSAY_IDs that are in the clinical database
             # but not in the assay_information file
-            missing_seqs = uniq_seq_df["seq"][~uniq_seq_df["seq"].isin(all_seq_assays)]
+            missing_seqs = uniq_seq_df["seq"][
+                ~uniq_seq_df["seq"]
+                .replace({"_": "-"}, regex=True)
+                .str.upper()
+                .isin(all_seq_assays)
+            ]
             missing_seqs_str = ", ".join(missing_seqs)
             if missing_seqs.to_list():
                 total_error += (


Test patch:
diff --git a/tests/test_assay.py b/tests/test_assay.py
index 4572808..b6054e7 100644
--- a/tests/test_assay.py
+++ b/tests/test_assay.py
@@ -85,6 +85,78 @@ def test_validinput__validate():
         patch_get_gdc.assert_called()
 
 
+def test_case__validate():
+    """Valid input should have no errors or warnings"""
+    assay_info_dict = {
+        'SEQ_ASSAY_ID': ['sage-1', 'SAGE-3'],
+        'is_paired_end': [True, False],
+        'library_strategy': ['value1', 'value2'],
+        'library_selection': ['value1', 'value2'],
+        'platform': ['value1', 'value2'],
+        'instrument_model': ['value1', 'value2'],
+        'target_capture_kit': ['value1', 'value2'],
+        'variant_classifications': ['Frame_Shift_Ins', 'Frame_Shift_Ins'],
+        'read_length': [22, float('nan')],
+        'number_of_genes': [5, 20],
+        'gene_padding': [10, None],
+        'calling_strategy': ['tumor_only', 'tumor_normal'],
+        'specimen_tumor_cellularity': ['>10%', '>20%'],
+        'alteration_types': ['snv;small_indels', 'intragenic_cna'],
+        'preservation_technique': ['FFPE', 'FFPE;fresh_frozen'],
+        'coverage': ['hotspot_regions;introns', 'introns']}
+    uniq_seq_df = pd.DataFrame({"seq": ["SAGE-1", "SAGE-3"]})
+    assay_info_df = pd.DataFrame(assay_info_dict)
+    test_dict = copy.deepcopy(GDC_DATA_DICT)
+    with patch.object(process_functions, "get_synid_database_mappingdf",
+                      return_value="syn123"),\
+         patch.object(process_functions, "getDatabaseSynId",
+                      return_value="syn1234"),\
+         patch.object(process_functions, "get_syntabledf",
+                      return_value=uniq_seq_df),\
+         patch.object(process_functions, "get_gdc_data_dictionary",
+                      return_value=test_dict) as patch_get_gdc:
+        error, warning = ASSAY_INFO._validate(assay_info_df, "syn9999")
+        assert error == ''
+        assert warning == ''
+        patch_get_gdc.assert_called()
+
+
+def test_underscore__validate():
+    """Valid input should have no errors or warnings"""
+    assay_info_dict = {
+        'SEQ_ASSAY_ID': ['SAGE_1', 'SAGE-3'],
+        'is_paired_end': [True, False],
+        'library_strategy': ['value1', 'value2'],
+        'library_selection': ['value1', 'value2'],
+        'platform': ['value1', 'value2'],
+        'instrument_model': ['value1', 'value2'],
+        'target_capture_kit': ['value1', 'value2'],
+        'variant_classifications': ['Frame_Shift_Ins', 'Frame_Shift_Ins'],
+        'read_length': [22, float('nan')],
+        'number_of_genes': [5, 20],
+        'gene_padding': [10, None],
+        'calling_strategy': ['tumor_only', 'tumor_normal'],
+        'specimen_tumor_cellularity': ['>10%', '>20%'],
+        'alteration_types': ['snv;small_indels', 'intragenic_cna'],
+        'preservation_technique': ['FFPE', 'FFPE;fresh_frozen'],
+        'coverage': ['hotspot_regions;introns', 'introns']}
+    uniq_seq_df = pd.DataFrame({"seq": ["SAGE-1", "SAGE-3"]})
+    assay_info_df = pd.DataFrame(assay_info_dict)
+    test_dict = copy.deepcopy(GDC_DATA_DICT)
+    with patch.object(process_functions, "get_synid_database_mappingdf",
+                      return_value="syn123"),\
+         patch.object(process_functions, "getDatabaseSynId",
+                      return_value="syn1234"),\
+         patch.object(process_functions, "get_syntabledf",
+                      return_value=uniq_seq_df),\
+         patch.object(process_functions, "get_gdc_data_dictionary",
+                      return_value=test_dict) as patch_get_gdc:
+        error, warning = ASSAY_INFO._validate(assay_info_df, "syn9999")
+        assert error == ''
+        assert warning == ''
+        patch_get_gdc.assert_called()
+
+
 def test__missingcols__validate():
     """Test missing columns"""
     assay_info_df = pd.DataFrame()