From 1dd341afd3162ca5ce138abed54dacd5e1eb1700 Mon Sep 17 00:00:00 2001 From: Samuel Johnson Date: Thu, 5 Feb 2026 16:57:07 -0500 Subject: [PATCH 1/7] titlecase operator, tests --- .../check_operators/dataframe_operators.py | 35 +- .../enums/dataset_label_acronyms.py | 5 + requirements.txt | 1 + resources/schema/Operator.json | 10 + resources/schema/Operator.md | 22 ++ .../test_titlecase_checks.py | 365 ++++++++++++++++++ 6 files changed, 437 insertions(+), 1 deletion(-) create mode 100644 cdisc_rules_engine/enums/dataset_label_acronyms.py create mode 100644 tests/unit/test_check_operators/test_titlecase_checks.py diff --git a/cdisc_rules_engine/check_operators/dataframe_operators.py b/cdisc_rules_engine/check_operators/dataframe_operators.py index 151ab41a7..7557bf76f 100644 --- a/cdisc_rules_engine/check_operators/dataframe_operators.py +++ b/cdisc_rules_engine/check_operators/dataframe_operators.py @@ -15,7 +15,7 @@ apply_rounding, is_in, ) - +from cdisc_rules_engine.enums.dataset_label_acronyms import DatasetLabelAcronyms from cdisc_rules_engine.constants import NULL_FLAVORS from cdisc_rules_engine.utilities.utils import dates_overlap, parse_date import numpy as np @@ -23,6 +23,7 @@ import pandas as pd import re import operator +from titlecase import titlecase from uuid import uuid4 from cdisc_rules_engine.models.dataset.dask_dataset import DaskDataset from cdisc_rules_engine.models.dataset.dataset_interface import DatasetInterface @@ -1922,3 +1923,35 @@ def check_order(row): @type_operator(FIELD_DATAFRAME) def is_not_ordered_subset_of(self, other_value: dict): return ~self.is_ordered_subset_of(other_value) + + @log_operator_execution + @type_operator(FIELD_DATAFRAME) + def is_title_case(self, other_value: dict): + """ + Checks if target column values are in proper title case. + """ + target = self.replace_prefix(other_value.get("target")) + acronyms = DatasetLabelAcronyms.Acronyms.value + + def acronym_callback(word, **kwargs): + if any(word.upper() == acr.upper() for acr in acronyms): + return word.upper() + return None + + def check_title_case(value): + if pd.isna(value) or value == "" or value in NULL_FLAVORS: + return True + str_value = str(value).strip() + expected = titlecase(str_value, callback=acronym_callback) + return str_value == expected + + results = self.value[target].apply(check_title_case) + return self.value.convert_to_series(results) + + @log_operator_execution + @type_operator(FIELD_DATAFRAME) + def is_not_title_case(self, other_value: dict): + """ + Checks if target column values are NOT in proper title case. + """ + return ~self.is_title_case(other_value) diff --git a/cdisc_rules_engine/enums/dataset_label_acronyms.py b/cdisc_rules_engine/enums/dataset_label_acronyms.py new file mode 100644 index 000000000..6d3c1f66c --- /dev/null +++ b/cdisc_rules_engine/enums/dataset_label_acronyms.py @@ -0,0 +1,5 @@ +from cdisc_rules_engine.enums.base_enum import BaseEnum + + +class DatasetLabelAcronyms(BaseEnum): + Acronyms = ["ID"] diff --git a/requirements.txt b/requirements.txt index 4e130d38f..4481051f6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,3 +23,4 @@ pyyaml==6.0.2 redis==4.5.0 requests~=2.32.3 setuptools~=75.6.0 +titlecase==2.4.1 \ No newline at end of file diff --git a/resources/schema/Operator.json b/resources/schema/Operator.json index 6abac2bfd..4c6bb6c87 100644 --- a/resources/schema/Operator.json +++ b/resources/schema/Operator.json @@ -525,6 +525,16 @@ "properties": { "operator": { "const": "is_not_ordered_subset_of" } }, "required": ["operator", "value"], "type": "object" + }, + { + "properties": { "operator": { "const": "is_title_case" } }, + "required": ["operator"], + "type": "object" + }, + { + "properties": { "operator": { "const": "is_not_title_case" } }, + "required": ["operator"], + "type": "object" } ], "properties": { diff --git a/resources/schema/Operator.md b/resources/schema/Operator.md index f4659ffb3..a1acee482 100644 --- a/resources/schema/Operator.md +++ b/resources/schema/Operator.md @@ -446,6 +446,28 @@ Complement of `split_parts_have_equal_length`. Returns True when parts have uneq separator: "/" ``` +### is_title_case + +Validates that variable labels follow proper title case formatting rules using the titlecase PyPi library. Title case capitalizes the first word and all major words, while keeping articles (a, an, the), conjunctions (and, but, or), and prepositions (in, of, for) in lowercase unless they are the first word. + +> Check that AELABEL values are in proper title case + +```yaml +- name: AELABEL + operator: is_title_case +``` + +### is_not_title_case + +Complement of `is_title_case`. Returns True when values are NOT in proper title case. + +> Flag AELABEL values that violate title case rules + +```yaml +- name: AELABEL + operator: is_not_title_case +``` + ## Date Date and time specific operations for comparing dates, validating date completeness, checking date formats, and validating ISO-8601 durations. diff --git a/tests/unit/test_check_operators/test_titlecase_checks.py b/tests/unit/test_check_operators/test_titlecase_checks.py new file mode 100644 index 000000000..964bd5245 --- /dev/null +++ b/tests/unit/test_check_operators/test_titlecase_checks.py @@ -0,0 +1,365 @@ +import pytest +from cdisc_rules_engine.check_operators.dataframe_operators import DataframeType +from cdisc_rules_engine.models.dataset.dask_dataset import DaskDataset +from cdisc_rules_engine.models.dataset.pandas_dataset import PandasDataset + + +@pytest.mark.parametrize( + "data,dataset_type,expected_result", + [ + ( + {"target": ["Subject ID", "Adverse Event", "Date of First Dose"]}, + PandasDataset, + [True, True, True], + ), + ( + {"target": ["SUBJECT ID", "ADVERSE EVENT", "DATE OF FIRST DOSE"]}, + PandasDataset, + [False, False, False], + ), + ( + {"target": ["subject id", "adverse event", "date of first dose"]}, + PandasDataset, + [False, False, False], + ), + ( + {"target": ["Subject ID", "ADVERSE EVENT", "date of first dose"]}, + PandasDataset, + [True, False, False], + ), + ( + {"target": ["Subject ID", "Adverse Event", "Date of First Dose"]}, + DaskDataset, + [True, True, True], + ), + ( + {"target": ["SUBJECT ID", "ADVERSE EVENT"]}, + DaskDataset, + [False, False], + ), + ], +) +def test_is_title_case_basic(data, dataset_type, expected_result): + df = dataset_type.from_dict(data) + dataframe_type = DataframeType({"value": df}) + result = dataframe_type.is_title_case({"target": "target"}) + assert result.equals(df.convert_to_series(expected_result)) + + +@pytest.mark.parametrize( + "data,dataset_type,expected_result", + [ + ( + {"target": ["Subject ID", "Study ID", "Patient ID"]}, + PandasDataset, + [True, True, True], + ), + ( + {"target": ["Subject Id", "Study id", "Patient Id"]}, + PandasDataset, + [False, False, False], + ), + ( + {"target": ["Unique Subject ID", "Primary Study ID"]}, + PandasDataset, + [True, True], + ), + ( + {"target": ["Subject ID", "Study ID"]}, + DaskDataset, + [True, True], + ), + ], +) +def test_is_title_case_id_acronym(data, dataset_type, expected_result): + df = dataset_type.from_dict(data) + dataframe_type = DataframeType({"value": df}) + result = dataframe_type.is_title_case({"target": "target"}) + assert result.equals(df.convert_to_series(expected_result)) + + +@pytest.mark.parametrize( + "data,dataset_type,expected_result", + [ + ( + { + "target": [ + "Date of First Dose", + "End of Study", + "Reason for No Treatment", + ] + }, + PandasDataset, + [True, True, True], + ), + ( + { + "target": [ + "Date Of First Dose", + "End Of Study", + "Reason For No Treatment", + ] + }, + PandasDataset, + [False, False, False], + ), + ( + {"target": ["Of the Study", "For No Reason"]}, + PandasDataset, + [True, True], + ), + ( + {"target": ["Study Of The Year", "Reason For The Record"]}, + PandasDataset, + [False, False], + ), + ( + {"target": ["Date of First Dose", "End of Study"]}, + DaskDataset, + [True, True], + ), + ], +) +def test_is_title_case_small_words(data, dataset_type, expected_result): + df = dataset_type.from_dict(data) + dataframe_type = DataframeType({"value": df}) + result = dataframe_type.is_title_case({"target": "target"}) + assert result.equals(df.convert_to_series(expected_result)) + + +@pytest.mark.parametrize( + "data,dataset_type,expected_result", + [ + ( + {"target": ["Epi/Pandemic", "Date/Time", "Start/End"]}, + PandasDataset, + [True, True, True], + ), + ( + {"target": ["epi/pandemic", "date/time", "start/end"]}, + PandasDataset, + [False, False, False], + ), + ( + {"target": ["EPI/PANDEMIC", "DATE/TIME", "START/END"]}, + PandasDataset, + [False, False, False], + ), + ( + {"target": ["Subject ID/Number", "Study ID/Code"]}, + PandasDataset, + [True, True], + ), + ( + {"target": ["Epi/Pandemic Pre-Specified"]}, + PandasDataset, + [True], + ), + ( + {"target": ["epi/pandemic pre-specified"]}, + PandasDataset, + [False], + ), + ( + {"target": ["EPI/PANDEMIC PRE-SPECIFIED"]}, + PandasDataset, + [False], + ), + ( + {"target": ["Epi/Pandemic", "Date/Time"]}, + DaskDataset, + [True, True], + ), + ], +) +def test_is_title_case_slash_separated(data, dataset_type, expected_result): + df = dataset_type.from_dict(data) + dataframe_type = DataframeType({"value": df}) + result = dataframe_type.is_title_case({"target": "target"}) + assert result.equals(df.convert_to_series(expected_result)) + + +@pytest.mark.parametrize( + "data,dataset_type,expected_result", + [ + ( + {"target": ["Pre-Specified", "Post-Treatment", "Non-Serious"]}, + PandasDataset, + [True, True, True], + ), + ( + {"target": ["pre-specified", "post-treatment", "non-serious"]}, + PandasDataset, + [False, False, False], + ), + ( + {"target": ["PRE-SPECIFIED", "POST-TREATMENT", "NON-SERIOUS"]}, + PandasDataset, + [False, False, False], + ), + ( + {"target": ["Pre-Specified Event", "Post-Treatment Assessment"]}, + PandasDataset, + [True, True], + ), + ( + {"target": ["Pre-Specified", "Post-Treatment"]}, + DaskDataset, + [True, True], + ), + ], +) +def test_is_title_case_hyphenated(data, dataset_type, expected_result): + df = dataset_type.from_dict(data) + dataframe_type = DataframeType({"value": df}) + result = dataframe_type.is_title_case({"target": "target"}) + assert result.equals(df.convert_to_series(expected_result)) + + +@pytest.mark.parametrize( + "data,dataset_type,expected_result", + [ + ( + {"target": ["", "", ""]}, + PandasDataset, + [True, True, True], + ), + ( + {"target": [None, None, None]}, + PandasDataset, + [True, True, True], + ), + ( + {"target": ["Subject ID", "", None, "ADVERSE EVENT"]}, + PandasDataset, + [True, True, True, False], + ), + ( + {"target": ["", None]}, + DaskDataset, + [True, True], + ), + ], +) +def test_is_title_case_null_empty(data, dataset_type, expected_result): + df = dataset_type.from_dict(data) + dataframe_type = DataframeType({"value": df}) + result = dataframe_type.is_title_case({"target": "target"}) + assert result.equals(df.convert_to_series(expected_result)) + + +@pytest.mark.parametrize( + "data,dataset_type,expected_result", + [ + ( + { + "target": [ + "Subject ID", + "Adverse Event ID", + "Date of First Dose", + "Reason for No Treatment", + "Pre-Specified Event", + "Epi/Pandemic Study", + ] + }, + PandasDataset, + [True, True, True, True, True, True], + ), + ( + { + "target": [ + "subject id", + "ADVERSE EVENT ID", + "Date Of First Dose", + "reason for no treatment", + "PRE-SPECIFIED EVENT", + "epi/pandemic study", + ] + }, + PandasDataset, + [False, False, False, False, False, False], + ), + ( + { + "target": [ + "Subject ID", + "adverse event id", + "Date of First Dose", + "REASON FOR NO TREATMENT", + ] + }, + PandasDataset, + [True, False, True, False], + ), + ( + { + "target": [ + "Subject ID", + "Date of First Dose", + "Pre-Specified Event", + ] + }, + DaskDataset, + [True, True, True], + ), + ], +) +def test_is_title_case_complex(data, dataset_type, expected_result): + df = dataset_type.from_dict(data) + dataframe_type = DataframeType({"value": df}) + result = dataframe_type.is_title_case({"target": "target"}) + assert result.equals(df.convert_to_series(expected_result)) + + +@pytest.mark.parametrize( + "data,dataset_type,expected_result", + [ + ( + {"target": ["Subject ID", "Adverse Event", "Date of First Dose"]}, + PandasDataset, + [False, False, False], + ), + ( + {"target": ["SUBJECT ID", "adverse event", "Date Of First Dose"]}, + PandasDataset, + [True, True, True], + ), + ( + {"target": ["Subject ID", "ADVERSE EVENT", "Date of First Dose"]}, + PandasDataset, + [False, True, False], + ), + ( + {"target": ["Subject ID", "ADVERSE EVENT"]}, + DaskDataset, + [False, True], + ), + ], +) +def test_is_not_title_case(data, dataset_type, expected_result): + df = dataset_type.from_dict(data) + dataframe_type = DataframeType({"value": df}) + result = dataframe_type.is_not_title_case({"target": "target"}) + assert result.equals(df.convert_to_series(expected_result)) + + +@pytest.mark.parametrize( + "data,dataset_type,expected_result", + [ + ( + {"target": [123, 456, 789]}, + PandasDataset, + [True, True, True], + ), + ( + {"target": ["Subject ID", 123, None, "ADVERSE EVENT"]}, + PandasDataset, + [True, True, True, False], + ), + ], +) +def test_is_title_case_numeric_values(data, dataset_type, expected_result): + df = dataset_type.from_dict(data) + dataframe_type = DataframeType({"value": df}) + result = dataframe_type.is_title_case({"target": "target"}) + assert result.equals(df.convert_to_series(expected_result)) From e75287c63dd90186b384da50ced0eb6fe4fc4921 Mon Sep 17 00:00:00 2001 From: Samuel Johnson Date: Wed, 11 Feb 2026 15:54:27 -0500 Subject: [PATCH 2/7] whitespace --- .../test_titlecase_checks.py | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tests/unit/test_check_operators/test_titlecase_checks.py b/tests/unit/test_check_operators/test_titlecase_checks.py index 964bd5245..2d713cfb4 100644 --- a/tests/unit/test_check_operators/test_titlecase_checks.py +++ b/tests/unit/test_check_operators/test_titlecase_checks.py @@ -363,3 +363,74 @@ def test_is_title_case_numeric_values(data, dataset_type, expected_result): dataframe_type = DataframeType({"value": df}) result = dataframe_type.is_title_case({"target": "target"}) assert result.equals(df.convert_to_series(expected_result)) + + +@pytest.mark.parametrize( + "data,dataset_type,expected_result", + [ + ( + { + "target": [ + " Subject ID ", + " Adverse Event ", + " Date of First Dose ", + ] + }, + PandasDataset, + [True, True, True], + ), + ( + { + "target": [ + " SUBJECT ID ", + " adverse event ", + " DATE OF FIRST DOSE ", + ] + }, + PandasDataset, + [False, False, False], + ), + ( + { + "target": [ + "\tSubject ID\t", + "\tAdverse Event\t", + "\tDate of First Dose\t", + ] + }, + PandasDataset, + [True, True, True], + ), + ( + {"target": ["\tSUBJECT ID\t", "\tadverse event\t"]}, + PandasDataset, + [False, False], + ), + ( + {"target": ["\t Subject ID \t", " \tAdverse Event\t "]}, + PandasDataset, + [True, True], + ), + ( + { + "target": [ + " Subject ID ", + "\tADVERSE EVENT\t", + " Date of First Dose ", + ] + }, + PandasDataset, + [True, False, True], + ), + ( + {"target": [" Subject ID ", "\tAdverse Event\t"]}, + DaskDataset, + [True, True], + ), + ], +) +def test_is_title_case_with_whitespace(data, dataset_type, expected_result): + df = dataset_type.from_dict(data) + dataframe_type = DataframeType({"value": df}) + result = dataframe_type.is_title_case({"target": "target"}) + assert result.equals(df.convert_to_series(expected_result)) From f69d7a634402c45ff49f62f9b70f4afbcd964715 Mon Sep 17 00:00:00 2001 From: Samuel Johnson Date: Wed, 18 Feb 2026 17:02:58 -0500 Subject: [PATCH 3/7] changes --- cdisc_rules_engine/check_operators/dataframe_operators.py | 7 +++++-- .../{dataset_label_acronyms.py => dataset_title_case.py} | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) rename cdisc_rules_engine/enums/{dataset_label_acronyms.py => dataset_title_case.py} (50%) diff --git a/cdisc_rules_engine/check_operators/dataframe_operators.py b/cdisc_rules_engine/check_operators/dataframe_operators.py index fff74e326..b121d0deb 100644 --- a/cdisc_rules_engine/check_operators/dataframe_operators.py +++ b/cdisc_rules_engine/check_operators/dataframe_operators.py @@ -15,7 +15,7 @@ apply_rounding, is_in, ) -from cdisc_rules_engine.enums.dataset_label_acronyms import DatasetLabelAcronyms +from cdisc_rules_engine.enums.dataset_title_case import DatasetTitleCase from cdisc_rules_engine.constants import NULL_FLAVORS from cdisc_rules_engine.utilities.utils import dates_overlap, parse_date import numpy as np @@ -1932,9 +1932,12 @@ def is_title_case(self, other_value: dict): Checks if target column values are in proper title case. """ target = self.replace_prefix(other_value.get("target")) - acronyms = DatasetLabelAcronyms.Acronyms.value + acronyms = DatasetTitleCase.Acronyms.value + lowercase_exceptions = DatasetTitleCase.Lowercase_Exceptions.value def acronym_callback(word, **kwargs): + if word.lower() in lowercase_exceptions: + return word.lower() if any(word.upper() == acr.upper() for acr in acronyms): return word.upper() return None diff --git a/cdisc_rules_engine/enums/dataset_label_acronyms.py b/cdisc_rules_engine/enums/dataset_title_case.py similarity index 50% rename from cdisc_rules_engine/enums/dataset_label_acronyms.py rename to cdisc_rules_engine/enums/dataset_title_case.py index 6d3c1f66c..05b34d3fc 100644 --- a/cdisc_rules_engine/enums/dataset_label_acronyms.py +++ b/cdisc_rules_engine/enums/dataset_title_case.py @@ -1,5 +1,6 @@ from cdisc_rules_engine.enums.base_enum import BaseEnum -class DatasetLabelAcronyms(BaseEnum): +class DatasetTitleCase(BaseEnum): Acronyms = ["ID"] + Lowercase_Exceptions = ["per", "and/or"] From 50c5ed69e404f53010aef42f5e952d46f35f096b Mon Sep 17 00:00:00 2001 From: Samuel Johnson Date: Wed, 18 Feb 2026 17:13:34 -0500 Subject: [PATCH 4/7] warning --- resources/schema/rule/Operator.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/schema/rule/Operator.md b/resources/schema/rule/Operator.md index a1acee482..e5118669d 100644 --- a/resources/schema/rule/Operator.md +++ b/resources/schema/rule/Operator.md @@ -448,7 +448,8 @@ Complement of `split_parts_have_equal_length`. Returns True when parts have uneq ### is_title_case -Validates that variable labels follow proper title case formatting rules using the titlecase PyPi library. Title case capitalizes the first word and all major words, while keeping articles (a, an, the), conjunctions (and, but, or), and prepositions (in, of, for) in lowercase unless they are the first word. +Validates that variable labels follow proper title case formatting rules using the titlecase PyPi library. Title case capitalizes the first word and all major words, while keeping articles (a, an, the), conjunctions (and, but, or), and prepositions (in, of, for) in lowercase unless they are the first word. +NOTE: The titlecase library may produce false positives or false negatives in syntactic edge cases (e.g. hyphenated words, slash-separated terms, uncommon prepositions). > Check that AELABEL values are in proper title case From 78567c3b5b1a68c8e6aea4469fd5f30901c7dc8f Mon Sep 17 00:00:00 2001 From: Samuel Johnson Date: Thu, 19 Feb 2026 12:40:52 -0500 Subject: [PATCH 5/7] exceptions --- .../check_operators/dataframe_operators.py | 9 ++++++--- cdisc_rules_engine/enums/dataset_title_case.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cdisc_rules_engine/check_operators/dataframe_operators.py b/cdisc_rules_engine/check_operators/dataframe_operators.py index d18d47135..198a79256 100644 --- a/cdisc_rules_engine/check_operators/dataframe_operators.py +++ b/cdisc_rules_engine/check_operators/dataframe_operators.py @@ -1067,7 +1067,7 @@ def not_contains_all(self, other_value: dict): @log_operator_execution @type_operator(FIELD_DATAFRAME) def invalid_date(self, other_value): - target = self.replace_prefix(other_value.get("target")) + target = other_value.get("target") results = ~vectorized_is_valid(self.value[target]) return self.value.convert_to_series(results) @@ -1134,7 +1134,7 @@ def is_incomplete_date(self, other_value): @log_operator_execution @type_operator(FIELD_DATAFRAME) def is_complete_date(self, other_value): - target = self.replace_prefix(other_value.get("target")) + target = other_value.get("target") results = vectorized_is_complete_date(self.value[target]) return self.value.convert_to_series(results) @@ -1874,7 +1874,7 @@ def is_title_case(self, other_value: dict): """ Checks if target column values are in proper title case. """ - target = self.replace_prefix(other_value.get("target")) + target = other_value.get("target") acronyms = DatasetTitleCase.Acronyms.value lowercase_exceptions = DatasetTitleCase.Lowercase_Exceptions.value @@ -1890,6 +1890,9 @@ def check_title_case(value): return True str_value = str(value).strip() expected = titlecase(str_value, callback=acronym_callback) + expected = expected[0].upper() + expected[1:] + if str_value != expected: + print(f"MISMATCH: {repr(str_value)} != {repr(expected)}") return str_value == expected results = self.value[target].apply(check_title_case) diff --git a/cdisc_rules_engine/enums/dataset_title_case.py b/cdisc_rules_engine/enums/dataset_title_case.py index 05b34d3fc..880cea9f1 100644 --- a/cdisc_rules_engine/enums/dataset_title_case.py +++ b/cdisc_rules_engine/enums/dataset_title_case.py @@ -3,4 +3,4 @@ class DatasetTitleCase(BaseEnum): Acronyms = ["ID"] - Lowercase_Exceptions = ["per", "and/or"] + Lowercase_Exceptions = ["per", "and/or", "is", "with"] From a6f53b52daee94ae69b11c2b636d78b057262b81 Mon Sep 17 00:00:00 2001 From: Samuel Johnson Date: Thu, 19 Feb 2026 16:41:39 -0500 Subject: [PATCH 6/7] remove print --- cdisc_rules_engine/check_operators/dataframe_operators.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cdisc_rules_engine/check_operators/dataframe_operators.py b/cdisc_rules_engine/check_operators/dataframe_operators.py index 198a79256..8be9332cf 100644 --- a/cdisc_rules_engine/check_operators/dataframe_operators.py +++ b/cdisc_rules_engine/check_operators/dataframe_operators.py @@ -1891,8 +1891,6 @@ def check_title_case(value): str_value = str(value).strip() expected = titlecase(str_value, callback=acronym_callback) expected = expected[0].upper() + expected[1:] - if str_value != expected: - print(f"MISMATCH: {repr(str_value)} != {repr(expected)}") return str_value == expected results = self.value[target].apply(check_title_case) From 6b870ec36224f1851b5447f7a4f75eb80f607944 Mon Sep 17 00:00:00 2001 From: Samuel Johnson Date: Thu, 19 Feb 2026 16:45:37 -0500 Subject: [PATCH 7/7] whitespace tests --- tests/unit/test_check_operators/test_titlecase_checks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_check_operators/test_titlecase_checks.py b/tests/unit/test_check_operators/test_titlecase_checks.py index 2d713cfb4..53268b638 100644 --- a/tests/unit/test_check_operators/test_titlecase_checks.py +++ b/tests/unit/test_check_operators/test_titlecase_checks.py @@ -60,12 +60,12 @@ def test_is_title_case_basic(data, dataset_type, expected_result): [False, False, False], ), ( - {"target": ["Unique Subject ID", "Primary Study ID"]}, + {"target": ["Unique Subject ID", " Primary Study ID"]}, PandasDataset, [True, True], ), ( - {"target": ["Subject ID", "Study ID"]}, + {"target": ["Subject ID", " Study ID"]}, DaskDataset, [True, True], ),