diff --git a/core.py b/core.py index ea660b867..ceb203d5b 100644 --- a/core.py +++ b/core.py @@ -154,8 +154,86 @@ def _validate_csv_data_paths( return list(filtered) +def _has_multiple_usdm_json_files( + standard: str, found_formats: set, dataset_paths: list +) -> bool: + """Returns True if USDM standard is used with more than one JSON file found.""" + is_usdm_standard = bool(standard) and standard.lower() == "usdm" + return ( + is_usdm_standard + and DataFormatTypes.JSON.value in found_formats + and len(dataset_paths) > 1 + ) + + +def _check_mixed_xlsx_formats(arg_name: str, found_formats: set) -> str | None: + """Returns the error message if XLSX is mixed with other formats, else None.""" + if DataFormatTypes.XLSX.value in found_formats and len(found_formats) > 1: + return ( + f"Argument {arg_name} contains XLSX files mixed with other formats ({', '.join(found_formats)}).\n" + f"Excel format (XLSX) validation only supports single files.\n" + f"Please provide either a single XLSX file or use other supported formats: " + f"{VALIDATION_FORMATS_MESSAGE}" + ) + return None + + +def _gather_csv_metadata_files(dataset_path: tuple[str]) -> list[str]: + """Gathers provided dataset paths plus any sibling datasets.csv/variables.csv files.""" + all_files_in_dp = [] + for dp in dataset_path: + all_files_in_dp.append(dp) + dp_path = Path(dp) + all_files_in_dp.extend( + [ + str(p) + for p in dp_path.parent.glob("*") + if p.is_file() and p.name in {"_datasets.csv", "_variables.csv"} + ] + ) + return all_files_in_dp + + +def _directory_empty_message(data: str, found_formats: set) -> str: + """Returns the appropriate error message when no valid dataset paths remain.""" + if DataFormatTypes.XLSX.value in found_formats and len(found_formats) == 1: + return ( + f"Multiple XLSX files found in directory: {data}\n" + f"Excel format (XLSX) validation only supports single files.\n" + f"Please provide either a single XLSX file or use other supported formats: " + f"{VALIDATION_FORMATS_MESSAGE}" + ) + return ( + f"No valid dataset files found in directory: {data}\n" + f"Supported formats: {VALIDATION_FORMATS_MESSAGE}\n" + f"Please ensure your directory contains files in one of these formats." + ) + + +def _dataset_path_empty_message(found_formats: set, filetype: str) -> str: + """Returns the appropriate error message when no valid dataset paths remain.""" + if DataFormatTypes.XLSX.value in found_formats and len(found_formats) == 1: + return ( + f"Multiple XLSX files provided.\n" + f"Excel format (XLSX) validation only supports single files.\n" + f"Please provide either a single XLSX file or use other supported formats: " + f"{VALIDATION_FORMATS_MESSAGE}" + ) + if filetype: + return ( + f"Provided dataset path does not match the specified file type.\n" + f"Specified format: {filetype}\n" + f"Please ensure the file extension matches the selected format." + ) + return ( + f"No valid dataset files provided.\n" + f"Supported formats: {VALIDATION_FORMATS_MESSAGE}\n" + f"Please ensure your files are in one of these formats." + ) + + def _validate_data_directory( - data: str, logger, filetype: str = None + data: str, logger, filetype: str = None, standard: str = None ) -> tuple[list, set]: """Validate data directory and return dataset paths and found formats.""" # Added filetype argument to filter files by extension if provided @@ -169,41 +247,34 @@ def _validate_data_directory( [str(p) for p in Path(data).rglob("*") if p.is_file()] ) - if DataFormatTypes.XLSX.value in found_formats and len(found_formats) > 1: + if _has_multiple_usdm_json_files(standard, found_formats, dataset_paths): logger.error( - f"Argument --data contains XLSX files mixed with other formats ({', '.join(found_formats)}).\n" - f"Excel format (XLSX) validation only supports single files.\n" - f"Please provide either a single XLSX file or use other supported formats: " - f"{VALIDATION_FORMATS_MESSAGE}" + f"Multiple JSON files found in directory: {data}\n" + f"USDM validation only supports a single JSON file." ) return [], set() - elif DataFormatTypes.CSV.value in found_formats: + + xlsx_mixed_message = _check_mixed_xlsx_formats("--data", found_formats) + if xlsx_mixed_message: + logger.error(xlsx_mixed_message) + return [], set() + + if DataFormatTypes.CSV.value in found_formats: try: dataset_paths = _validate_csv_data_paths(dataset_paths) except InvalidCSVFile as e: logger.error(e) return [], set() + if not dataset_paths: - if DataFormatTypes.XLSX.value in found_formats and len(found_formats) == 1: - logger.error( - f"Multiple XLSX files found in directory: {data}\n" - f"Excel format (XLSX) validation only supports single files.\n" - f"Please provide either a single XLSX file or use other supported formats: " - f"{VALIDATION_FORMATS_MESSAGE}" - ) - else: - logger.error( - f"No valid dataset files found in directory: {data}\n" - f"Supported formats: {VALIDATION_FORMATS_MESSAGE}\n" - f"Please ensure your directory contains files in one of these formats." - ) + logger.error(_directory_empty_message(data, found_formats)) return [], set() return dataset_paths, found_formats def _validate_dataset_paths( - dataset_path: tuple[str], logger, filetype: str + dataset_path: tuple[str], logger, filetype: str, standard ) -> tuple[list, set]: """Validate dataset paths and return dataset paths and found formats.""" if filetype: @@ -219,52 +290,29 @@ def _validate_dataset_paths( else: dataset_paths, found_formats = valid_data_file([dp for dp in dataset_path]) - if DataFormatTypes.XLSX.value in found_formats and len(found_formats) > 1: + if _has_multiple_usdm_json_files(standard, found_formats, dataset_paths): logger.error( - f"Argument --dataset-path contains XLSX files mixed with other formats ({', '.join(found_formats)}).\n" - f"Excel format (XLSX) validation only supports single files.\n" - f"Please provide either a single XLSX file or use other supported formats: " - f"{VALIDATION_FORMATS_MESSAGE}" + "Multiple JSON files provided for --dataset-path.\n" + "USDM validation only supports a single JSON file." ) return [], set() - elif DataFormatTypes.CSV.value in found_formats: - all_files_in_dp = [] - - for dp in dataset_path: - all_files_in_dp.append(dp) - dp_path = Path(dp) - all_files_in_dp.extend( - [ - str(p) - for p in dp_path.parent.glob("*") - if p.is_file() and p.name in {"datasets.csv", "variables.csv"} - ] - ) + + xlsx_mixed_message = _check_mixed_xlsx_formats("--dataset-path", found_formats) + if xlsx_mixed_message: + logger.error(xlsx_mixed_message) + return [], set() + + if DataFormatTypes.CSV.value in found_formats: try: - dataset_paths = _validate_csv_data_paths(all_files_in_dp) + dataset_paths = _validate_csv_data_paths( + _gather_csv_metadata_files(dataset_path) + ) except InvalidCSVFile as e: logger.error(e) return [], set() + if not dataset_paths: - if DataFormatTypes.XLSX.value in found_formats and len(found_formats) == 1: - logger.error( - f"Multiple XLSX files provided.\n" - f"Excel format (XLSX) validation only supports single files.\n" - f"Please provide either a single XLSX file or use other supported formats: " - f"{VALIDATION_FORMATS_MESSAGE}" - ) - elif filetype: - logger.error( - f"Provided dataset path does not match the specified file type.\n" - f"Specified format: {filetype}\n" - f"Please ensure the file extension matches the selected format." - ) - else: - logger.error( - f"No valid dataset files provided.\n" - f"Supported formats: {VALIDATION_FORMATS_MESSAGE}\n" - f"Please ensure your files are in one of these formats." - ) + logger.error(_dataset_path_empty_message(found_formats, filetype)) return [], set() return dataset_paths, found_formats @@ -656,12 +704,14 @@ def validate( # noqa "Argument --dataset-path cannot be used together with argument --data" ) ctx.exit(2) - dataset_paths, found_formats = _validate_data_directory(data, logger, filetype) + dataset_paths, found_formats = _validate_data_directory( + data, logger, filetype, standard + ) if not dataset_paths: ctx.exit(2) elif dataset_path: dataset_paths, found_formats = _validate_dataset_paths( - dataset_path, logger, filetype + dataset_path, logger, filetype, standard ) if not dataset_paths: ctx.exit(2) diff --git a/scripts/run_validation.py b/scripts/run_validation.py index e44d6aad2..594f173a0 100644 --- a/scripts/run_validation.py +++ b/scripts/run_validation.py @@ -159,6 +159,11 @@ def run_validation(args: Validation_args): shared_cache = get_cache_service(manager) engine_logger.info(f"Populating cache, cache path: {args.cache}") rules, skipped_rule_ids = get_rules(args) + if len(rules) == 0: + raise ValueError( + "No rules were selected for this standard/version — " + "nothing to execute, aborting before report generation" + ) library_metadata: LibraryMetadataContainer = get_library_metadata_from_cache( args ) diff --git a/tests/QARegressionTests/test_Issues/test_CoreIssue357.py b/tests/QARegressionTests/test_Issues/test_CoreIssue357.py deleted file mode 100644 index e1f223509..000000000 --- a/tests/QARegressionTests/test_Issues/test_CoreIssue357.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -import subprocess -import unittest -import pytest -from conftest import get_python_executable - - -@pytest.mark.regression -class TerminalCommandTestCase(unittest.TestCase): - @classmethod - def setUpClass(cls): - # Run the command in the terminal - command = [ - f"{get_python_executable()}", - "-m", - "core", - "validate", - "-s", - "sdtmig", - "-v", - "3.4", - "-r", - "tests/resources/CoreIssue357/SENDIG_266_rule.json", - "-dp", - "tests/resources/CoreIssue357/SENDIG_266_negative_testdata_datasets.json", - ] - subprocess.run(command, check=True) - - # Get the latest created Excel file - files = os.listdir() - excel_files = [ - file - for file in files - if file.startswith("CORE-Report-") and file.endswith(".xlsx") - ] - cls.excel_file_path = sorted(excel_files)[-1] - - def test_command_execution(self): - # Check if the Excel file is created - self.assertTrue( - os.path.exists(self.excel_file_path), - f"Excel file '{self.excel_file_path}' is not created.", - ) - - @classmethod - def tearDownClass(cls): - # Delete the Excel file - if os.path.exists(cls.excel_file_path): - os.remove(cls.excel_file_path) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/QARegressionTests/test_core/test_validate.py b/tests/QARegressionTests/test_core/test_validate.py index b3e6229bc..01ece13a7 100644 --- a/tests/QARegressionTests/test_core/test_validate.py +++ b/tests/QARegressionTests/test_core/test_validate.py @@ -180,7 +180,7 @@ def test_validate_local_rule(self): "-s", "sdtmig", "-v", - "3.4", + "3.2", "-dp", os.path.join("resources", "datasets", "ae.xpt"), "-lr", @@ -193,7 +193,7 @@ def test_validate_local_rule(self): self.assertNotIn("error", stderr.lower()) self.assertFalse(self.error_keyword in stdout) - def test_validate_local_exclude_rule(self): + def test_validate_no_rules(self): args = [ "python", "core.py", @@ -206,8 +206,31 @@ def test_validate_local_exclude_rule(self): os.path.join("resources", "datasets", "ae.xpt"), "-lr", os.path.join("tests", "resources", "rules"), - "-er", + "-r", "CORE-000473", + ] + exit_code, stdout, stderr = run_command(args, False) + self.assertEqual(exit_code, 1) + self.assertIn( + "no rules were selected for this standard/version", + stderr.lower(), + ) + + def test_validate_local_exclude_rule(self): + args = [ + "python", + "core.py", + "validate", + "-s", + "sdtmig", + "-v", + "3.2", + "-dp", + os.path.join("resources", "datasets", "ae.xpt"), + "-lr", + os.path.join("tests", "resources", "rules"), + "-er", + "CORE-000012", "-l", "error", ] diff --git a/tests/resources/CoreIssue357/SENDIG_266_negative_testdata_datasets.json b/tests/resources/CoreIssue357/SENDIG_266_negative_testdata_datasets.json deleted file mode 100644 index daf127747..000000000 --- a/tests/resources/CoreIssue357/SENDIG_266_negative_testdata_datasets.json +++ /dev/null @@ -1,896 +0,0 @@ -{ - "datasets": [{ - "filename": "vs.xpt", - "label": "Vital Signs", - "domain": "VS", - "variables": [ - { - "name": "STUDYID", - "label": "Study Identifier", - "type": "Char", - "length": 12 - }, - { - "name": "DOMAIN", - "label": "Domain Abbreviation", - "type": "Char", - "length": 2 - }, - { - "name": "USUBJID", - "label": "Unique Subject Identifier", - "type": "Char", - "length": 8 - }, - { - "name": "VSSEQ", - "label": "Sequence Number", - "type": "Num", - "length": 8 - }, - { - "name": "VSNOTEST", - "label": "Vital Signs Test Short Name", - "type": "Char", - "length": 8 - }, - { - "name": "VSTEST", - "label": "Vital Signs Test Name", - "type": "Char", - "length": 24 - }, - { - "name": "VSPOS", - "label": "Vital Signs Position of Subject", - "type": "Char", - "length": 8 - }, - { - "name": "VSORRES", - "label": "Result or Finding in Original Units", - "type": "Char", - "length": 8 - }, - { - "name": "VSORRESU", - "label": "Original Units", - "type": "Char", - "length": 9 - }, - { - "name": "VSSTRESC", - "label": "Character Result/Finding in Std Format", - "type": "Char", - "length": 200 - }, - { - "name": "VSSTRESN", - "label": "Numeric Result/Finding in Standard Units", - "type": "Num", - "length": 8 - }, - { - "name": "VSSTRESU", - "label": "Standard Units", - "type": "Char", - "length": 9 - }, - { - "name": "VSSTAT", - "label": "Completion Status", - "type": "Char", - "length": 8 - }, - { - "name": "VSLOC", - "label": "Location of Vital Signs Measurement", - "type": "Char", - "length": 11 - }, - { - "name": "VSLOBXFL", - "label": "Last Observation Before Exposure Flag", - "type": "Char", - "length": 1 - }, - { - "name": "VSREPNUM", - "label": "Repetition Number", - "type": "Num", - "length": 8 - }, - { - "name": "VISITNUM", - "label": "Visit Number", - "type": "Num", - "length": 8 - }, - { - "name": "VISIT", - "label": "Visit Name", - "type": "Char", - "length": 200 - }, - { - "name": "EPOCH", - "label": "Epoch", - "type": "Char", - "length": 9 - }, - { - "name": "VSDTC", - "label": "Date/Time of Measurements", - "type": "Char", - "length": 10 - }, - { - "name": "VSDY", - "label": "Study Day of Vital Signs", - "type": "Num", - "length": 8 - } - ], - "records": { - "STUDYID": [ - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01", - "CDISCPILOT01" - ], - "DOMAIN": [ - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS", - "VS" - ], - "USUBJID": [ - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001", - "CDISC001" - ], - "VSSEQ": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57 - ], - "VSNOTEST": [ - "DIABP_1", - "DIABP", - "DIABP", - "DIABP", - "DIABP", - "DIA_BP", - "DIABP", - "DIABP", - "DIABP", - "PULSE", - "PULSE123", - "PULSE", - "PULSE", - "PULSE", - "PULSE", - "PULSE", - "PULSE", - "PULSE", - "PULSE", - "SYSBP", - "SYSBP", - "SYSBP", - "TEMP", - "TEMP", - "TEMP", - "TEMP", - "TEMP", - "TEMP", - "WEIGHT", - "WEIGHT", - "WEIGHT", - "WEIGHT", - "WEIGHT", - "WEIGHT" - ], - "VSTEST": [ - "Diastolic Blood Pressure", - "Diastolic Blood Pressure", - "Diastolic Blood Pressure", - "Diastolic Blood Pressure", - "Diastolic Blood Pressure", - "Diastolic Blood Pressure", - "Diastolic Blood Pressure", - "Diastolic Blood Pressure", - "Diastolic Blood Pressure", - "Pulse Rate", - "Pulse Rate", - "Pulse Rate", - "Pulse Rate", - "Pulse Rate", - "Pulse Rate", - "Pulse Rate", - "Pulse Rate", - "Pulse Rate", - "Pulse Rate", - "Systolic Blood Pressure", - "Systolic Blood Pressure", - "Systolic Blood Pressure", - "Temperature", - "Temperature", - "Temperature", - "Temperature", - "Temperature", - "Temperature", - "Weight", - "Weight", - "Weight", - "Weight", - "Weight", - "Weight" - ], - "VSPOS": [ - "STANDING", - "STANDING", - "STANDING", - "STANDING", - "STANDING", - "SUPINE", - "STANDING", - "STANDING", - "SUPINE", - "STANDING", - "SUPINE", - "STANDING", - "STANDING", - "SUPINE", - "STANDING", - "STANDING", - "SUPINE", - "STANDING", - "STANDING", - "STANDING", - "STANDING", - "STANDING", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "VSORRES": [ - "71", - "71", - "83", - "79", - "68", - "77", - "71", - "69", - "76", - "51", - "52", - "62", - "62", - "50", - "52", - "54", - "79", - "98", - "94", - "137", - "137", - "130", - "98.0", - "98.0", - "97.7", - "97.6", - "97.6", - "97.4", - "173.5", - "174.0", - "174.0", - "173.0", - "173.0", - "173.0" - ], - "VSORRESU": [ - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "mmHg", - "mmHg", - "mmHg", - "F", - "F", - "F", - "F", - "F", - "F", - "LB", - "LB", - "LB", - "LB", - "LB", - "LB" - ], - "VSSTRESC": [ - "71", - "71", - "83", - "79", - "68", - "77", - "71", - "69", - "76", - "51", - "52", - "62", - "62", - "50", - "52", - "54", - "79", - "98", - "94", - "137", - "137", - "130", - "36.67", - "36.67", - "36.5", - "36.44", - "36.44", - "36.33", - "78.7", - "78.93", - "78.93", - "78.47", - "78.47", - "78.47" - ], - "VSSTRESN": [ - 71, - 71, - 83, - 79, - 68, - 77, - 71, - 69, - 76, - 51, - 52, - 62, - 62, - 50, - 52, - 54, - 79, - 98, - 94, - 137, - 137, - 130, - 36.68, - 36.68, - 36.6, - 36.45, - 36.45, - 36.34, - 78.8, - 78.94, - 78.94, - 78.48, - 78.48, - 78.48 - ], - "VSSTRESU": [ - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "mmHg", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "beats/min", - "mmHg", - "mmHg", - "mmHg", - "C", - "C", - "C", - "C", - "C", - "C", - "kg", - "kg", - "kg", - "kg", - "kg", - "kg" - ], - "VSSTAT": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ], - "VSLOC": [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "ORAL CAVITY", - "ORAL CAVITY", - "ORAL CAVITY", - "ORAL CAVITY", - "ORAL CAVITY", - "ORAL CAVITY", - "", - "", - "", - "", - "", - "" - ], - "VSLOBXFL": [ - "", - "", - "Y", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "Y", - "Y", - "", - "", - "", - "", - "", - "", - "Y", - "", - "", - "", - "" - ], - "VSREPNUM": [ - null, - null, - null, - null, - null, - 1, - 2, - 3, - 1, - null, - 1, - 2, - 3, - 1, - 2, - 3, - 1, - 2, - 3, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null - ], - "VISITNUM": [ - 1, - 2, - 3, - 4, - 5, - 7, - 7, - 7, - 8, - 5, - 7, - 7, - 7, - 8, - 8, - 8, - 201, - 201, - 201, - 1, - 2, - 3, - 3, - 4, - 5, - 7, - 8, - 201, - 1, - 3, - 4, - 5, - 7, - 8 - ], - "VISIT": [ - "SCREENING 1", - "SCREENING 2", - "BASELINE", - "WEEK 2", - "WEEK 4", - "WEEK 6", - "WEEK 6", - "WEEK 6", - "WEEK 8", - "WEEK 4", - "WEEK 6", - "WEEK 6", - "WEEK 6", - "WEEK 8", - "WEEK 8", - "WEEK 8", - "EARLY DISCONTINUATION RETRIEVAL", - "EARLY DISCONTINUATION RETRIEVAL", - "EARLY DISCONTINUATION RETRIEVAL", - "SCREENING 1", - "SCREENING 2", - "BASELINE", - "BASELINE", - "WEEK 2", - "WEEK 4", - "WEEK 6", - "WEEK 8", - "EARLY DISCONTINUATION RETRIEVAL", - "SCREENING 1", - "BASELINE", - "WEEK 2", - "WEEK 4", - "WEEK 6", - "WEEK 8" - ], - "EPOCH": [ - "SCREENING", - "SCREENING", - "SCREENING", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "SCREENING", - "SCREENING", - "SCREENING", - "SCREENING", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "SCREENING", - "SCREENING", - "TREATMENT", - "TREATMENT", - "TREATMENT", - "TREATMENT" - ], - "VSDTC": [ - "2012-11-23", - "2012-11-28", - "2012-11-30", - "2012-12-13", - "2012-12-26", - "2013-01-10", - "2013-01-10", - "2013-01-10", - "2013-01-23", - "2012-12-26", - "2013-01-10", - "2013-01-10", - "2013-01-10", - "2013-01-23", - "2013-01-23", - "2013-01-23", - "2013-05-20", - "2013-05-20", - "2013-05-20", - "2012-11-23", - "2012-11-28", - "2012-11-30", - "2012-11-30", - "2012-12-13", - "2012-12-26", - "2013-01-10", - "2013-01-23", - "2013-05-20", - "2012-11-23", - "2012-11-30", - "2012-12-13", - "2012-12-26", - "2013-01-10", - "2013-01-23" - ], - "VSDY": [ - -7, - -2, - 1, - 14, - 27, - 42, - 42, - 42, - 55, - 27, - 42, - 42, - 42, - 55, - 55, - 55, - 172, - 172, - 172, - -7, - -2, - 1, - 1, - 14, - 27, - 42, - 55, - 172, - -7, - 1, - 14, - 27, - 42, - 55 - ] - } - - }] - - } - \ No newline at end of file diff --git a/tests/resources/CoreIssue357/SENDIG_266_rule.json b/tests/resources/CoreIssue357/SENDIG_266_rule.json deleted file mode 100644 index 996f76f94..000000000 --- a/tests/resources/CoreIssue357/SENDIG_266_rule.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "Authority": { - "Organization": "CDISC" - }, - "Check": { - "all": [ - { - "name": "variable_name", - "operator": "is_not_contained_by", - "value": "$model_variables" - } - ] - }, - "Citations": [ - { - "Document": "TODO", - "Section": "TODO", - "Cited_Guidance": "TODO" - } - ], - "Core": { - "Id": "CDISC.SDTMIG.CG0013", - "Version": "1" - }, - "Description": "Trigger when variable cannot be found in the SDTM model (v.1.7 for SENDIG-3.1)", - "Operations": [ - { - "id": "$model_variables", - "operator": "get_column_order_from_library", - "name": "MODELVARIABLES" - } - ], - "Outcome": { - "Message": "Variable is not allowed in SDTM/SEND" - }, - "References": [ - { - "Origin": "SDTM and SENDIG Conformance Rules", - "Version": "2.0", - "Rule_Identifier": { - "Id": "266", - "Version": "1" - } - } - ], - "Scopes": { - "Classes": { - "Include": [ - "All" - ] - }, - "Domains": { - "Include": [ - "All" - ] - }, - "Standards": [ - { - "Name": "SDTMIG", - "Version": "3.4" - } - ] - }, - "Sensitivity": "Record", - "Severity": "Error", - "Rule_Type": "Variable Metadata Check" - } \ No newline at end of file diff --git a/tests/resources/rules/CORE-000012.yml b/tests/resources/rules/CORE-000012.yml new file mode 100644 index 000000000..959b6b98b --- /dev/null +++ b/tests/resources/rules/CORE-000012.yml @@ -0,0 +1,99 @@ +# Variable: AEOCCUR +# Condition: +# Rule: AEOCCUR not present in dataset +Authorities: + - Organization: CDISC + Standards: + - Name: SDTMIG + References: + - Citations: + - Cited Guidance: The following Qualifiers would not be used in AE; --OCCUR, + --STAT, and --REASND. They are the only Qualifiers from the + SDTM Events Class not in the AE domain. They are not permitted + because the AE domain contains only records for adverse events + that actually occurred. + Document: SDTMIG v3.4 + Item: Assumption 9 + Section: 6.2.1 + Origin: SDTM and SDTMIG Conformance Rules + Rule Identifier: + Id: CG0040 + Version: '1' + Version: '2.0' + Version: '3.4' + - Name: SDTMIG + References: + - Citations: + - Cited Guidance: The following Qualifiers would not be used in AE; --OCCUR, + --STAT, and--REASND. They are the only Qualifiers from the + SDTM Events Class not in the AE domain. They are not permitted + because the AE domain contains only records for adverse events + that actually occurred. + Document: SDTMIG v3.3 + Item: Assumption 9 + Section: 6.2.1 + Origin: SDTM and SDTMIG Conformance Rules + Rule Identifier: + Id: CG0040 + Version: '1' + Version: '2.0' + Version: '3.3' + - Name: SDTMIG + References: + - Citations: + - Cited Guidance: The following Qualifiers would not be used in AE; --OCCUR, + --STAT, and--REASND. They are the only Qualifiers from the + SDTM Events Class not in the AE domain. They are not permitted + because the AE domain contains only records for adverse events + that actually occurred. + Document: SDTMIG v3.2 + Item: Assumption 8 + Section: 6.2. + Origin: SDTM and SDTMIG Conformance Rules + Rule Identifier: + Id: CG0040 + Version: '1' + Version: '2.0' + Version: '3.2' + - Name: TIG + References: + - Citations: + - Cited Guidance: 'The following qualifiers would not be used in AE: --OCCUR, + --STAT, and--REASND. They are the only qualifiers from the + SDTM Events class not in the AE domain. They are not permitted + because the AE domain contains only records for adverse events + that actually occurred.''' + Document: TIG 1.0 + Item: Assumption 10 + Section: 2.8.10.1 + Origin: TIG Conformance Rules + Rule Identifier: + Id: TIG0319 + Version: '1' + Version: '1.0' + Substandard: SDTM + Version: '1.0' +Check: + all: + - name: AEOCCUR + operator: exists +Core: + Id: CORE-000012 + Status: Published + Version: '1' +Description: Raise an error when AEOCCUR exists in AE dataset. +Executability: Fully Executable +Outcome: + Message: AEOCCUR is present in AE dataset. + Output Variables: + - AEOCCUR +Rule Type: Record Data +Scope: + Classes: + Include: + - EVENTS + Domains: + Include: + - AE + Use Case: INDH +Sensitivity: Dataset