From c07a184a427e6bdceea0219e3e0d5854a298dee8 Mon Sep 17 00:00:00 2001 From: "Niemes, Adam" Date: Wed, 5 Aug 2026 17:30:23 -0400 Subject: [PATCH 1/4] Add consecutive ordered set operator for skip-free sequences --- .../check_operators/dataframe_operators.py | 166 ++++++ resources/schema/rule-merged/CORE-base.json | 150 +++-- resources/schema/rule-merged/Operations.json | 301 ++++++++-- resources/schema/rule-merged/Operator.json | 513 ++++++++++++++---- .../rule-merged/Organization_CDISC.json | 112 +++- .../rule-merged/Organization_Custom.json | 53 +- .../schema/rule-merged/Organization_FDA.json | 38 +- resources/schema/rule/Operator.json | 10 + resources/schema/rule/Operator.md | 32 ++ .../test_value_set_checks.py | 91 ++++ 10 files changed, 1238 insertions(+), 228 deletions(-) diff --git a/cdisc_rules_engine/check_operators/dataframe_operators.py b/cdisc_rules_engine/check_operators/dataframe_operators.py index 66d2d8fdf..70404f39e 100644 --- a/cdisc_rules_engine/check_operators/dataframe_operators.py +++ b/cdisc_rules_engine/check_operators/dataframe_operators.py @@ -1402,6 +1402,172 @@ def is_ordered_set(self, other_value): def is_not_ordered_set(self, other_value): return ~self.is_ordered_set(other_value) + def _coerce_sequence_value_to_int(self, value): + """ + Attempts to coerce a sequence value to an integer for continuity checks. + + Accepted as valid: + - int values + - float values that represent whole numbers (for example 2.0) + - numeric strings matching optional sign and digits (for example "-1", "03") + + Rejected as invalid: + - null/NaN/empty values + - non-integer floats (for example 2.5) + - non-numeric strings (for example "A1", "UNK") + + Returns: + - (True, int_value) when conversion is valid + - (False, None) when value cannot be used in consecutive sequence logic + """ + if value is None or value == "" or pd.isna(value): + return False, None + if isinstance(value, (int, np.integer)): + return True, int(value) + if isinstance(value, (float, np.floating)): + if float(value).is_integer(): + return True, int(value) + return False, None + if isinstance(value, str): + stripped = value.strip() + if re.fullmatch(r"[+-]?\d+", stripped): + return True, int(stripped) + return False, None + + def _check_consecutive_partition(self, partition: pd.Series) -> pd.Series: + """ + Validates consecutive sequence rules for one grouped partition of target values. + + The partition is evaluated in its existing row order (same order used by + is_ordered_set checks). For each row: + - First valid numeric value initializes the running previous value. + - Next valid value must be equal to previous (duplicate allowed) or + previous + 1 (strictly consecutive step). + - Any null/empty/non-numeric value is marked False (strict behavior). + - Any numeric jump greater than 1 or decrease is marked False. + + Returns: + - A boolean Series aligned to partition index, where each element + indicates whether that row satisfies the consecutive rule. + """ + result = pd.Series(True, index=partition.index, dtype="bool") + prev_val = None + has_prev = False + + for idx, raw in partition.items(): + valid, current = self._coerce_sequence_value_to_int(raw) + if not valid: + result.at[idx] = False + continue + + if not has_prev: + prev_val = current + has_prev = True + continue + + if current == prev_val or current == prev_val + 1: + prev_val = current + else: + result.at[idx] = False + prev_val = current + + return result + + def _check_ordered_partition_strict(self, partition: pd.Series) -> pd.Series: + """ + Validates ascending order for one grouped partition using strict numeric rules. + + Rules: + - Values are evaluated in existing row order. + - Null/empty/non-numeric values are marked False. + - Valid numeric values must be non-decreasing (duplicates allowed). + """ + result = pd.Series(True, index=partition.index, dtype="bool") + prev_val = None + has_prev = False + + for idx, raw in partition.items(): + valid, current = self._coerce_sequence_value_to_int(raw) + if not valid: + result.at[idx] = False + continue + + if not has_prev: + prev_val = current + has_prev = True + continue + + if current < prev_val: + result.at[idx] = False + + prev_val = current + + return result + + @log_operator_execution + @type_operator(FIELD_DATAFRAME) + def is_consecutive_ordered_set(self, other_value): + """ + Checks whether the values in the target column are consecutive and ordered + within each group defined by the comparator. + + This operator extends is_ordered_set by adding continuity validation: + after grouping rows by comparator, target values must be in ascending order + and each next value must be either: + - the same as the previous value (duplicates allowed), or + - exactly previous + 1 (no skips allowed). + + Strict behavior: + - Null, empty, or non-numeric target values are marked False. + - These rows are not silently ignored, so data quality issues are visible. + + Parameters in other_value: + - target: the column containing sequence values to validate. + - comparator: one grouping column (string) or multiple grouping columns (list). + + Example: + - Group USUBJID = 01 with SEQ [1, 1, 2, 3] -> all True + - Group USUBJID = 01 with SEQ [1, 2, 4] -> row with 4 is False (skip at 3) + - Group USUBJID = 01 with SEQ [1, None, 2] -> row with None is False + """ + target = other_value.get("target") + value = other_value.get("comparator") + + if not isinstance(value, (str, list)): + raise Exception("Comparator must be a String or list of Strings") + if isinstance(value, list) and not all(isinstance(v, str) for v in value): + raise Exception("All comparator values must be Strings") + + grouping = [value] if isinstance(value, str) else value + + # keep existing ordering semantics + data = self.value.get(grouping + [target]) + ordered_result = ( + data.groupby(grouping, dropna=False)[target] + .transform(self._check_ordered_partition_strict) + .sort_index() + ) + + # compute strict consecutive semantics on realized dataframe + consecutive_result = ( + data.groupby(grouping, dropna=False)[target] + .transform(self._check_consecutive_partition) + .sort_index() + ) + + ordered_result = self.value.convert_to_series(ordered_result).sort_index() + consecutive_result = self.value.convert_to_series(consecutive_result).astype("bool") + + return ordered_result & consecutive_result + + @log_operator_execution + @type_operator(FIELD_DATAFRAME) + def is_not_consecutive_ordered_set(self, other_value): + """ + Complement of is_consecutive_ordered_set. + """ + return ~self.is_consecutive_ordered_set(other_value) + @log_operator_execution @type_operator(FIELD_DATAFRAME) def has_next_corresponding_record(self, other_value: dict): diff --git a/resources/schema/rule-merged/CORE-base.json b/resources/schema/rule-merged/CORE-base.json index c7ce4f5e8..adb800f8e 100644 --- a/resources/schema/rule-merged/CORE-base.json +++ b/resources/schema/rule-merged/CORE-base.json @@ -9,7 +9,9 @@ "$ref": "#/$defs/CheckItems" } }, - "required": ["all"], + "required": [ + "all" + ], "type": "object" }, { @@ -19,7 +21,9 @@ "$ref": "#/$defs/CheckItems" } }, - "required": ["any"], + "required": [ + "any" + ], "type": "object" }, { @@ -29,7 +33,9 @@ "$ref": "#/$defs/CheckItem" } }, - "required": ["not"], + "required": [ + "not" + ], "type": "object" } ] @@ -128,13 +134,18 @@ "$ref": "#/$defs/Domains" }, "include_split_datasets": { - "enum": [true] + "enum": [ + true + ] } }, "type": "object" }, "JoinType": { - "enum": ["inner", "left"], + "enum": [ + "inner", + "left" + ], "type": "string" }, "LeftRightKeys": { @@ -161,7 +172,10 @@ ] } }, - "required": ["Left", "Right"], + "required": [ + "Left", + "Right" + ], "type": "object" }, "EntityName": { @@ -202,7 +216,13 @@ "type": "string" }, "USDMPrimitiveType": { - "enum": ["string", "integer", "boolean", "decimal", "date"] + "enum": [ + "string", + "integer", + "boolean", + "decimal", + "date" + ] }, "VariableReference": { "anyOf": [ @@ -259,7 +279,10 @@ "type": "string" } }, - "required": ["Document", "Cited Guidance"], + "required": [ + "Document", + "Cited Guidance" + ], "type": "object" }, "type": "array" @@ -268,10 +291,14 @@ "additionalProperties": false, "anyOf": [ { - "required": ["Logical Expression"] + "required": [ + "Logical Expression" + ] }, { - "required": ["Plain Language Expression"] + "required": [ + "Plain Language Expression" + ] } ], "properties": { @@ -285,18 +312,25 @@ "type": "string" } }, - "required": ["Rule"], + "required": [ + "Rule" + ], "type": "object" }, "Plain Language Expression": { "type": "string" }, "Type": { - "enum": ["Failure", "Success"], + "enum": [ + "Failure", + "Success" + ], "type": "string" } }, - "required": ["Type"], + "required": [ + "Type" + ], "type": "object" }, "Origin": { @@ -310,11 +344,18 @@ "type": "string" }, "Relationship": { - "enum": ["Predecessor", "Related", "Successor"], + "enum": [ + "Predecessor", + "Related", + "Successor" + ], "type": "string" } }, - "required": ["Id", "Relationship"], + "required": [ + "Id", + "Relationship" + ], "type": "object" }, "type": "array" @@ -332,7 +373,9 @@ "type": "string" } }, - "required": ["Id"], + "required": [ + "Id" + ], "type": "object" }, "Validator Rule Message": { @@ -342,7 +385,11 @@ "type": "string" } }, - "required": ["Origin", "Rule Identifier", "Version"], + "required": [ + "Origin", + "Rule Identifier", + "Version" + ], "type": "object" }, "minItems": 1, @@ -355,7 +402,11 @@ "type": "string" } }, - "required": ["Name", "References", "Version"], + "required": [ + "Name", + "References", + "Version" + ], "type": "object" }, "minItems": 1, @@ -378,7 +429,10 @@ "$ref": "Organization_Custom.json" } ], - "required": ["Organization", "Standards"], + "required": [ + "Organization", + "Standards" + ], "type": "object" }, "minItems": 1, @@ -418,10 +472,15 @@ "const": "Published" } }, - "required": ["Id"] + "required": [ + "Id" + ] } ], - "required": ["Status", "Version"], + "required": [ + "Status", + "Version" + ], "type": "object" }, "Description": { @@ -476,7 +535,9 @@ "type": "string" } }, - "required": ["Name"], + "required": [ + "Name" + ], "type": "object" }, "minItems": 1, @@ -502,7 +563,9 @@ "type": "array" } }, - "required": ["Message"], + "required": [ + "Message" + ], "type": "object" }, "Rule Type": { @@ -520,7 +583,9 @@ "$ref": "#/$defs/Classes" } }, - "required": ["Include"], + "required": [ + "Include" + ], "type": "object" }, { @@ -530,7 +595,9 @@ "$ref": "#/$defs/Classes" } }, - "required": ["Exclude"], + "required": [ + "Exclude" + ], "type": "object" } ] @@ -544,7 +611,9 @@ "$ref": "#/$defs/DataStructures" } }, - "required": ["Include"], + "required": [ + "Include" + ], "type": "object" }, { @@ -554,7 +623,9 @@ "$ref": "#/$defs/DataStructures" } }, - "required": ["Exclude"], + "required": [ + "Exclude" + ], "type": "object" } ] @@ -616,10 +687,14 @@ }, "anyOf": [ { - "required": ["Exclude"] + "required": [ + "Exclude" + ] }, { - "required": ["Include"] + "required": [ + "Include" + ] } ], "type": "object" @@ -641,13 +716,20 @@ }, "oneOf": [ { - "required": ["Classes", "Domains"] + "required": [ + "Classes", + "Domains" + ] }, { - "required": ["Data Structures"] + "required": [ + "Data Structures" + ] }, { - "required": ["Entities"] + "required": [ + "Entities" + ] } ], "type": "object" @@ -682,7 +764,9 @@ } }, "then": { - "required": ["Grouping_Variables"] + "required": [ + "Grouping_Variables" + ] }, "type": "object" } diff --git a/resources/schema/rule-merged/Operations.json b/resources/schema/rule-merged/Operations.json index 8c03f1c5a..0eef6a6b3 100644 --- a/resources/schema/rule-merged/Operations.json +++ b/resources/schema/rule-merged/Operations.json @@ -10,7 +10,10 @@ "markdownDescription": "\nReturns a Series indicating whether a specified codelist is extensible. Used in conjunction with codelist_terms to determine if values outside the codelist are acceptable. From the above example, $extensible will contain a bool if the codelist PKUDUG is extensible in all rows of the column.\n\nIf ct_package_type, version, and codelist_code parameters are provided, it will instead attach a new column containing the extensible value for each combination provided in the source dataset.\n\nFor example, given the current dataset:\n\n```\nid \tcodeSystemVersion \t$codelist_code\n1 \t2024-09-27 \tC201264\n2 \t2024-09-27 \tC201265\n3 \t2023-03-29 \tC127262\n```\n\nand the following operation:\n\n```yaml\n- id: $codelist_extensible\n operator: codelist_extensible\n ct_package_type: DDF\n version: codeSystemVersion\n codelist_code: $codelist_code\n```\n\nThis will result in the following dataset:\n\n```\nid \tcodeSystemVersion \t$codelist_code \t$codelist_extensible\n1 \t2024-09-27 \tC201264 \tfalse\n2 \t2024-09-27 \tC201265 \tfalse\n3 \t2023-03-29 \tC127262 \ttrue\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -20,7 +23,10 @@ "markdownDescription": "\nReturns a list of valid codelist/term values. Used for evaluating whether NCI codes, submission values or NCI preferred terms are valid based on controlled terminology. Expects three parameters: `codelists` which is a list of the codelist submission value(s) to retrieve, `level` which is the level of data (either \"codelist\" or \"term\") at which to return data from, and `returntype` which is the type of values to return: \"code\" for NCI Code(s), \"value\" for submission value(s), or \"pref_term\" for NCI preferred term(s).\n\n```yaml\n- Check:\n - all:\n - name: PPSTRESU\n operator: is_not_contained_by\n value: $terms\n - name: $extensible\n operator: equal_to\n value: true\n- Operations:\n - id: $terms\n operator: codelist_terms\n codelists:\n - PKUDUG\n level: term\n returntype: value\n - id: $extensible\n codelist: PKUDUG\n operator: codelist_extensible\n```\n\nIf `ct_package_type`, `version`, and `codelist_code` parameters are provided, it will instead attach a new column containing the term for each combination provided in the source dataset. If a column name is provided as:\n\n- `term_code`, it will find term information using the term codes in the specified column.\n- `term_value`, it will find term information using the term submission values in the specified column.\n- `term_pref_term`, it will find term information using the term preferred terms in the specified column.\n\nOnly one of `term_code`, `term_value` or `term_pref_term` can be provided. The term information returned will depend on the value of the `returntype` parameter, as described above. If `returntype` is not specified, specifying `term_code` will return the term submission value and specifying either `term_value` or `term_pref_term` will return the term code.\n\nFor example, given the current dataset:\n\n```\nid \tcodeSystemVersion $codelist_code code decode\n1 \t2024-09-27 C201264 C201356 After\n2 \t2024-09-27 C201265 C201352 End to End\n3 \t2023-03-29 C127262 C51282 CLINIC\n```\n\nand the following operations:\n\n```yaml\n- id: $found_term_value\n operator: codelist_terms\n ct_package_type: DDF\n version: codeSystemVersion\n codelist_code: $codelist_code\n term_code: code\n- id: $found_term_pref_term\n operator: codelist_terms\n ct_package_type: DDF\n version: codeSystemVersion\n codelist_code: $codelist_code\n term_code: code\n returntype: pref_term\n```\n\nThis will result in the following dataset:\n\n```\n id codeSystemVersion $codelist_code code decode $found_term_value $found_term_pref_term\n 1 2024-09-27 C201264 C201356 After After After Timing Type\n 2 2024-09-27 C201265 C201352 End to End End to End End to End\n 3 2023-03-31 C127262 C51282 CLINIC CLINIC Clinic\n```\n\nConversely, if given the same dataset, and the following operations:\n\n```yaml\n- id: $found_term_code1\n operator: codelist_terms\n ct_package_type: DDF\n version: codeSystemVersion\n codelist_code: $codelist_code\n term_value: decode\n- id: $found_term_code2\n operator: codelist_terms\n ct_package_type: DDF\n version: codeSystemVersion\n codelist_code: $codelist_code\n term_pref_term: decode\n```\n\nThis will result in the following dataset:\n\n```\n id codeSystemVersion $codelist_code code decode $found_term_code1 $found_term_code2\n 1 2024-09-27 C201264 C201356 After C201356\n 2 2024-09-27 C201265 C201352 End to End C201352 C201352\n 3 2023-03-31 C127262 C51282 CLINIC C51282 C51282\n```\n\nNote that `$found_term_code2` is:\n\n- `null` for the first record because \"After\" does not match any NCI preferred term in the C201264 codelist.\n- populated for the third record because matching is case-insensitive (i.e., \"CLINIC\" matches \"Clinic\").\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -30,7 +36,11 @@ "markdownDescription": "\nReturns a list of valid extensible codelist term's submission values. Used for evaluating whether submission values are valid based on controlled terminology. Expects the parameter codelists which is a list of the codelist submission value(s) to retrieve. If the codelist argument is [\"All\"] will return all extensible terms for the CT in a list.\n\n```yaml\n{\n \"id\": \"$ext_value\",\n \"codelist\": [\"ALL\"],\n \"operator\": \"define_extensible_codelists\",\n}\n```\n" } }, - "required": ["id", "operator", "codelists"], + "required": [ + "id", + "operator", + "codelists" + ], "type": "object" }, { @@ -40,7 +50,11 @@ "markdownDescription": "\nIf a target variable name is specified, returns the specified metadata in the define for the specified target variable.\n\nInput\n\n```yaml\n- operator: define_variable_metadata\n attribute_name: define_variable_label\n name: LBTESTCD\n id: $LBTESTCD_VARIABLE_LABEL\n```\n\nOutput\n\n```\nLaboratory Test Code\n```\n\nIf no target variable name specified, returns a dictionary containing the specified metadata in the define for all variables.\n\nInput\n\n```yaml\n- operator: define_variable_metadata\n attribute_name: define_variable_label\n id: $VARIABLE_LABEL\n```\n\nOutput\n\n```\n{\n \"STUDYID\": \"Study Identifier\",\n \"USUBJID\": \"Unique Subject Identifier\",\n \"LBTESTCD\": \"Laboratory Test Code\",\n \"...\": \"...\"\n}\n```\n" } }, - "required": ["id", "operator", "attribute_name"], + "required": [ + "id", + "operator", + "attribute_name" + ], "type": "object" }, { @@ -50,7 +64,11 @@ "markdownDescription": "\nGet a distinct list of values for the given name.\n\nIf a group list is specified, the distinct value list will be grouped by the variables within group.\nIf a filter object is provided, only values for records that match the filter criteria are included in the distinct values.\nIf `value_is_reference` is set to true, the target column contains the names of other columns, and the operation will check the referenced columns to ensure they exist in the associated dataset before adding them to the distinct list.\n\nIf group is provided, group_aliases may also be provided to assign new grouping variable names so that results grouped by the values in one set of grouping variables can be merged onto a dataset according to the same grouping value(s) stored in different set of grouping variables. When both group and group_aliases are provided, columns are renamed according to corresponding list position (i.e., the 1st column in group is renamed to the 1st column in group_aliases, etc.). If there are more columns listed in group than in group_aliases, only the group columns with corresponding group_aliases columns will be renamed. If there are more columns listed in group_aliases than in group, the extra column names in group_aliases will be ignored. See record_count for an example of the use of group_aliases.\n\n```yaml\nCheck:\n all:\n - name: SSSTRESC\n operator: equal_to\n value: DEAD\n value_is_literal: true\n - name: $ds_dsdecod\n operator: does_not_contain\n value: DEATH\n value_is_literal: true\nOperations:\n - operator: distinct\n domain: DS\n name: DSDECOD\n id: $ds_dsdecod\n group:\n - USUBJID\n filter:\n CAT: \"CATEGORY 1\"\n SCAT: \"SUBCATEGORY A\"\n```\n\n> below, `IDVAR` contains column names, the operation retrieves the value from each column for that row, checks the dataset associated with that column using the CO RDOMAIN. Columns that exist are added to the returns the distinct set.\n\n```yaml\nOperations:\n - domain: CO\n id: $rdomain_variables\n name: IDVAR\n operator: distinct\n value_is_reference: true\n```\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -60,7 +78,10 @@ "markdownDescription": "\nChecks whether the domain is in the set of domains within the provided standard.\n\nInput\n\nTarget Domain: XY\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\nOperations:\n - operator: domain_is_custom\n id: $domain_is_custom\n```\n\nOutput\n\n```\ntrue\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -70,7 +91,10 @@ "markdownDescription": "\nChecks whether the related domain (for example, the parent domain of a SUPP or RELREC dataset) is not present in the set of standard domains for the provided standard and version. This is useful for determining whether relationships point to non-standard or custom domains.\n\nInput\n\nTarget Domain: SUPPEX\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\nOperations:\n - operator: related_domain_is_custom\n id: $related_domain_is_custom\n```\n\nOutput\n\n```\ntrue\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -80,7 +104,10 @@ "markdownDescription": "\nReturns the label for the domain the operation is executing on within the provided standard.\n\nInput.\n\nTarget Domain: LB\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\nOperations:\n - operator: domain_label\n id: $domain_label\n```\n\nOutput\n\n```\nLaboratory Test Results\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -90,7 +117,11 @@ "markdownDescription": "\nCalculates the number of days between the DTC and RFSTDTC. The Study Day value is incremented by 1 for each date following RFSTDTC. Dates prior to RFSTDTC are decreased by 1, with the date preceding RFSTDTC designated as Study Day -1 (there is no Study Day 0). All Study Day values are integers. Thus, to calculate Study Day:\n\n- --DY = (date portion of --DTC) - (date portion of RFSTDTC) + 1 if --DTC is on or after RFSTDTC\n- --DY = (date portion of --DTC) - (date portion of RFSTDTC) if --DTC precedes RFSTDTC\n\nThis algorithm should be used across all domains.\n\n```yaml\nCheck:\n all:\n - name: --DY\n operator: non_empty\n - name: --DTC\n operator: is_complete_date\n - name: RFSTDTC\n operator: is_complete_date\n - name: --DY\n operator: not_equal_to\n value: $dy\nOperations:\n - name: --DTC\n operator: dy\n id: $dy\nMatch Datasets:\n - Name: DM\n Keys:\n - USUBJID\n```\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -100,7 +131,11 @@ "markdownDescription": "\nReturns the requested dataset level metadata value for the current dataset. Possible name values are:\n\n- dataset_size\n- dataset_location\n- dataset_name\n- dataset_label\n- domain\n- is_ap\n- ap_suffix\n\nExample\n\nInput:\n\nTarget domain: LB\n\n```yaml\n- name: dataset_label\n operator: extract_metadata\n id: $dataset_label\n```\n\nOutput:\n\n```\nLaboratory Test Results\n```\n\nExample: ap_suffix\n\nExtracts the domain suffix (characters 3-4) from AP-related domains. For example, \"FA\" from \"APFA\" DOMAIN value.\n\nInput:\n\nTarget domain: APFA\n\n```yaml\n- name: ap_suffix\n operator: extract_metadata\n id: $ap_suffix\n```\n\nOutput:\n\n```\nFA\n```\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -110,7 +145,10 @@ "markdownDescription": "\nReturns the expected (\"Core\" = Exp ) variables for the domain in the current standard Variable Metadata for custom domains will pull from the model while non-custom domains will be from the IG and Model.\n\nInput:\n\nTarget Domain: LB\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\n- operator: expected_variables\n id: $expected_variables\n```\n\nOutput:\n\n```\n[\"LBCAT\", \"LBORRES\", \"LBORRESU\", \"...\"]\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -120,7 +158,13 @@ "markdownDescription": "\nFetches controlled terminology attribute values from CT packages based on row-specific CT package and version references. The operation constructs CT package names based on the standard being validated and the values in the `name` and `version` columns (e.g., SDTMIG \u2192 \"sdtmct-{version}\"). When the `name` column contains \"CDISC\" or \"CDISC CT\", it uses the validation run's standard to determine the package prefix and the version found in the cell of the specified column. The operation extracts all codes matching the specified ct_attribute from the package.\n\n**Required Parameters:**\n\n- `ct_attribute`: Attribute to extract - `\"Term CCODE\"`, `\"Codelist CCODE\"`, `\"Term Value\"`, `\"Codelist Value\"`, or `\"Term Preferred Term\"`\n- `name`: Column containing CT reference (e.g., \"TSVCDREF\") - identifies which terminology system is referenced\n- `version`: Column containing CT version (e.g., \"TSVCDVER\")\n\n```yaml\n- id: $VALID_TERM_CODES\n name: TSVCDREF\n operator: get_codelist_attributes\n ct_attribute: Term CCODE\n version: TSVCDVER\n```\n\n**Note:** if using this operator with excel data, you must put the ctpackage versions contained within your data in the library tab for it work properly.\n" } }, - "required": ["id", "operator", "name", "ct_attribute", "version"], + "required": [ + "id", + "operator", + "name", + "ct_attribute", + "version" + ], "type": "object" }, { @@ -130,7 +174,10 @@ "markdownDescription": "\nReturns list of dataset columns in order\n\n```yaml\nCheck:\n all:\n - name: $column_order_from_dataset\n operator: is_not_ordered_by\n value: $column_order_from_library\nOperations:\n - id: $column_order_from_library\n operator: get_column_order_from_library\n - id: $column_order_from_dataset\n operator: get_column_order_from_dataset\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -140,7 +187,10 @@ "markdownDescription": "\nFetches column order for a given domain from the CDISC library. The lists with column names are sorted in accordance to \"ordinal\" key of library metadata.\nOptionally Filters variables based on specified metadata criteria.\n\nRule Type: Variable Metadata Check\n\n```yaml\nCheck:\n all:\n - name: variable_name\n operator: is_not_contained_by\n value: $ig_variables\nOperations:\n - id: $ig_variables\n operator: get_column_order_from_library\n key_name: \"role\" # role, core, etc\n key_value: \"Exp\" # Timing, Req, Exp, Perm, etc\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -150,7 +200,10 @@ "markdownDescription": "\nReturns the list of domains for a given class from the CDISC Library Implementation Guide. This operation retrieves all domains that belong to a specified class (e.g., \"TRIAL DESIGN\", \"FINDINGS\", \"EVENTS\") based on the current standard and version. The operation uses the standard and version from the validation context as well as the optional `domain_class` parameter which is the name of the class to filter by (e.g., \"TRIAL DESIGN\", \"FINDINGS\", \"EVENTS\", \"INTERVENTIONS). NOTE: Class names are case-sensitive and should match the Library metadata format. If no `domain_class` parameter is provided, the operation returns all domains across all classes in the Implementation Guide:\n\n```yaml\n- operator: get_library_class_domains\n id: $trial_design_domains\n domain_class: \"TRIAL DESIGN\"\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -160,7 +213,10 @@ "markdownDescription": "\nFetches column order for a given model class from the CDISC library. The lists with column names are sorted in accordance to \"ordinal\" key of library metadata.\n\nRule Type: Variable Metadata Check\n\n```yaml\nCheck:\n all:\n - name: variable_name\n operator: is_not_contained_by\n value: $model_variables\nOperations:\n - id: $model_variables\n operator: get_model_column_order\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -170,7 +226,12 @@ "markdownDescription": "\nFetches variable level library model properties filtered by the provided key_name and key_value\n\nExample\n\nInput\n\nTarget Domain: LB\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\n- operator: get_model_filtered_variables\n id: $model_filtered_variables\n key_name: \"role\"\n key_value: \"Timing\"\n```\n\nOutput\n\n```\n[\"VISITNUM\", \"VISIT\", \"VISITDY\", \"TAETORD\", \"...\"]\n```\n" } }, - "required": ["id", "operator", "key_name", "key_value"], + "required": [ + "id", + "operator", + "key_name", + "key_value" + ], "type": "object" }, { @@ -180,7 +241,10 @@ "markdownDescription": "\nFetches column order for a given SUPP's parent model class from the CDISC library. The lists with column names are sorted in accordance to \"ordinal\" key of library metadata.\n\n```yaml\nCheck:\n all:\n - operator: is_not_contained_by\n value: $parent_model_variables\nOperations:\n - id: $parent_model_variables\n operator: get_parent_model_column_order\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -190,7 +254,12 @@ "markdownDescription": "\nFilters variables from the dataset based on specified metadata criteria. Returns a list of variable names that exist in the dataset and match the filter criteria.\n\n```yaml\n- operator: get_dataset_filtered_variables\n id: $timing_variables\n key_name: \"role\"\n key_value: \"Timing\"\n```\n" } }, - "required": ["id", "operator", "key_name", "key_value"], + "required": [ + "id", + "operator", + "key_name", + "key_value" + ], "type": "object" }, { @@ -200,7 +269,11 @@ "markdownDescription": "\nGenerates a dataframe where each record in the dataframe is the library ig variable metadata corresponding with the variable label found in the column provided in name. The metadata column names are prefixed with the string provided in `id`.\n\nInput\n\nTarget Dataset: SUPPLB\n\nProduct: sdtmig\n\nVersion: 3-4\n\nDataset:\n\n```\n{\n \"STUDYID\": [\"STUDY1\", \"STUDY1\", \"STUDY1\"],\n \"USUBJID\": [\"SUBJ1\", \"SUBJ1\", \"SUBJ1\"],\n \"QLABEL\": [\"Toxicity\", \"Viscosity\", \"Analysis Method\"]\n}\n```\n\nRule:\n\n```yaml\n- operator: label_referenced_variable_metadata\n id: $qlabel_referenced_variable_metadata\n name: \"QLABEL\"\n```\n\nOutput\n\n```\n{\n \"STUDYID\": [\"STUDY1\", \"STUDY1\", \"STUDY1\"],\n \"USUBJID\": [\"SUBJ1\", \"SUBJ1\", \"SUBJ1\"],\n \"QLABEL\": [\"Toxicity\", \"Viscosity\", \"Analysis Method\"],\n \"$qlabel_referenced_variable_metadata_name\": [\"LBTOX\", null, \"LBANMETH\"],\n \"$qlabel_referenced_variable_metadata_role\": [\n \"Variable Qualifier\",\n null,\n \"Record Qualifier\"\n ],\n \"$qlabel_referenced_variable_metadata_ordinal\": [44, null, 38],\n \"$qlabel_referenced_variable_metadata_core\": [\"Req\", \"Req\", \"Req\"],\n \"$qlabel_referenced_variable_metadata_label\": [\"Toxicity\", null, \"Analysis Method\"]\n}\n```\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -210,7 +283,11 @@ "markdownDescription": "\nAllows the creation of a lookup table to take the values from multiple input columns and map them to values in an output column. The map parameter contains a list of objects. Each dictionary contains column names as properties that match the column names in the source dataset and an output property that will be returned as a result.\n\nIf map has a single object and output is the only property specified on that object, this will function as a direct assignment.\n\nFor example, given the following current dataset:\n\n```\nid \tparent_entity \tparent_rel\n1 \tTiming \trelativeToFrom\n2 \tSomething \trelativeToFrom\n3 \tTiming \ttype\n```\n\nand the following operation:\n\n```yaml\nOperations:\n - id: $codelist_code\n operator: map\n map:\n - parent_entity: Timing\n parent_rel: type\n output: C201264\n - parent_entity: Timing\n parent_rel: relativeToFrom\n output: C201265\n```\n\nThis will result in the following dataset:\n\n```\nid \tparent_entity \tparent_rel \t$codelist_code\n1 \tTiming \trelativeToFrom \tC201265\n2 \tSomething \trelativeToFrom \tNone\n3 \tTiming \ttype \tC201264\n```\n\nThe following operation:\n\n```yaml\nOperations:\n - id: $codelist_code\n operator: map\n map:\n - output: C201264\n```\n\nWill result in the following dataset:\n\n```\nid \tparent_entity \tparent_rel \t$codelist_code\n1 \tTiming \trelativeToFrom \tC201264\n2 \tSomething \trelativeToFrom \tC201264\n3 \tTiming \ttype \tC201264\n```\n" } }, - "required": ["id", "operator", "map"], + "required": [ + "id", + "operator", + "map" + ], "type": "object" }, { @@ -220,7 +297,11 @@ "markdownDescription": "\nIf no group is provided, returns the max value in name. If group is provided, returns the max value in name, within each unique set of the grouping variables.\n\n```yaml\nCheck:\n all:\n - name: \"$max_age\"\n operator: \"greater_than\"\n value: \"MAXAGE\"\nOperations:\n - operator: \"max\"\n domain: \"DM\"\n name: \"AGE\"\n id: \"$max_age\"\n```\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -230,7 +311,11 @@ "markdownDescription": "\nIf no group is provided, returns the max date value in name. If group is provided, returns the max date value in name, within each unique set of the grouping variables.\n\n```yaml\nCheck:\n all:\n - name: USUBJID\n operator: is_contained_by\n value: $ex_usubjid\n - name: RFXENDTC\n operator: not_equal_to\n value: $max_ex_exstdtc\n - name: RFXENDTC\n operator: not_equal_to\n value: $max_ex_exendtc\nOperations:\n - operator: distinct\n domain: EX\n name: USUBJID\n id: $ex_usubjid\n - operator: max_date\n domain: EX\n name: EXSTDTC\n id: $max_ex_exstdtc\n group:\n - USUBJID\n - operator: max_date\n domain: EX\n name: EXENDTC\n id: $max_ex_exendtc\n group:\n - USUBJID\n```\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -240,7 +325,11 @@ "markdownDescription": "\nExample: AAGE > mean(DM.AGE), where AAGE is a fictitious NSV\n\n```yaml\nCheck:\n all:\n - name: \"AAGE\"\n operator: \"greater_than\"\n value: \"$average_age\"\nOperations:\n - operator: \"mean\"\n domain: \"DM\"\n name: \"AGE\"\n id: \"$average_age\"\n```\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -250,7 +339,11 @@ "markdownDescription": "\nIf no group is provided, returns the min value in name. If group is provided, returns the min value in name, within each unique set of the grouping variables.\n\n```yaml\nCheck:\n all:\n - name: \"$min_age\"\n operator: \"less_than\"\n value: \"MINAGE\"\nOperations:\n - operator: \"min\"\n domain: \"DM\"\n name: \"AGE\"\n id: \"$min_age\"\n```\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -260,7 +353,11 @@ "markdownDescription": "\nIf no group is provided, returns the min date value in name. If group is provided, returns the min date value in name, within each unique set of the grouping variables.\n\nExample: RFSTDTC is greater than min AE.AESTDTC for the current USUBJID\n\n```yaml\nCheck:\n all:\n - name: \"RFSTDTC\"\n operator: \"date_greater_than\"\n value: \"$ae_aestdtc\"\nOperations:\n - operator: \"min_date\"\n domain: \"AE\"\n name: \"AESTDTC\"\n id: \"$ae_aestdtc\"\n group:\n - USUBJID\n```\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -270,7 +367,12 @@ "markdownDescription": "\nComputes set difference: elements in `name` that are not in `subtract`. By default a standard [set difference]() semantics (A \u2216 B) is applied. Optional `order_insensitive` property allows to have the element order to be taken into consideration and only those `name` elements are removed which follow the same order as in `subtract` . Preserves order from the first list. Both `name` and `subtract` must reference other operation results (e.g., `$expected_variables`, `$dataset_variables`). When `subtract` is empty or missing, returns all elements from `name`. Can be computed and added to output variables to display missing elements in error results.\n\n```yaml\nOperations:\n - id: $expected_variables\n operator: expected_variables\n - id: $dataset_variables\n operator: get_column_order_from_dataset\n - id: $expected_minus_dataset\n name: $expected_variables\n operator: minus\n subtract: $dataset_variables\n order_insensitive: false\n```\n" } }, - "required": ["id", "operator", "name", "subtract"], + "required": [ + "id", + "operator", + "name", + "subtract" + ], "type": "object" }, { @@ -280,7 +382,11 @@ "markdownDescription": "\nGenerates a dataframe where each record in the dataframe is the library ig variable metadata corresponding with the variable name found in the column provided in name. The metadata column names are prefixed with the string provided in `id`.\n\nInput\n\nTarget Dataset: SUPPLB\n\nProduct: sdtmig\n\nVersion: 3-4\n\nDataset:\n\n```\n{\n \"STUDYID\": [\"STUDY1\", \"STUDY1\", \"STUDY1\"],\n \"USUBJID\": [\"SUBJ1\", \"SUBJ1\", \"SUBJ1\"],\n \"QNAM\": [\"Toxicity\", \"LBVISCOS\", \"Analysis Method\"]\n}\n```\n\nRule:\n\n```yaml\n- operator: name_referenced_variable_metadata\n id: $qnam_referenced_variable_metadata\n name: \"QNAM\"\n```\n\nOutput\n\n```\n{\n \"STUDYID\": [\"STUDY1\", \"STUDY1\", \"STUDY1\"],\n \"USUBJID\": [\"SUBJ1\", \"SUBJ1\", \"SUBJ1\"],\n \"QNAM\": [\"LBTOX\", \"LBVISCOS\", \"LBANMETH\"],\n \"$qnam_referenced_variable_metadata_name\": [\"LBTOX\", null, \"LBANMETH\"],\n \"$qnam_referenced_variable_metadata_role\": [\n \"Variable Qualifier\",\n null,\n \"Record Qualifier\"\n ],\n \"$qnam_referenced_variable_metadata_ordinal\": [44, null, 38],\n \"$qnam_referenced_variable_metadata_core\": [\"Req\", \"Req\", \"Req\"],\n \"$qnam_referenced_variable_metadata_label\": [\"Toxicity\", null, \"Analysis Method\"]\n}\n```\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -290,7 +396,10 @@ "markdownDescription": "\nReturns the permissible variables (\"Core\" = Perm ) for a given domain and standard Variable Metadata for custom domains will pull from the model while non-custom domains will be from the IG and Model.\n\nInput:\n\nTarget Domain: LB\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\n- operator: permissible_variables\n id: $permissible_variables\n```\n\nOutput:\n\n```\n[\"LBGRPID\", \"LBREFID\", \"LBSPID\", \"...\"]\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -300,7 +409,10 @@ "markdownDescription": "\nIf no filter or group is provided, returns the number of records in the dataset. If filter is provided, returns the number of records in the dataset that contain the value(s) in the corresponding column(s) provided in the filter. Filter can have a wildcard `&` that when added to the end of the filter value will look for all instances of that prefix (see 4th example below). If group is provided, returns the number of rows matching each unique set of the grouping variables. These can be static column name(s) or can be derived from other operations like get_dataset_filtered_variables.\n\nIf both filter and group are provided, returns the number of records in the dataset that contain the value(s) in the corresponding column(s) provided in the filter that also match each unique set of the grouping variables.\n\n**Wildcard Filtering:** Filter values ending with % will match any records where the column value starts with the specified prefix. For example, RACE% will match RACE1, RACE2, RACE3, etc. This is useful for matching related variables with numeric or alphabetic suffixes.\n\n**Regex Transformation:** If regex is provided along with group, the regex pattern will be applied to transform grouping column values before grouping. The regex is only applied to columns where the pattern matches the data type. For example, using regex `^\\d{4}-\\d{2}-\\d{2}` on a column containing `2022-01-14T08:00` will extract `2022-01-14` for grouping purposes.\n\nIf group is provided, group_aliases may also be provided to assign new grouping variable names so that results grouped by the values in one set of grouping variables can be merged onto a dataset according to the same grouping value(s) stored in different set of grouping variables. When both group and group_aliases are provided, columns are renamed according to corresponding list position (i.e., the 1st column in group is renamed to the 1st column in group_aliases, etc.). If there are more columns listed in group than in group_aliases, only the group columns with corresponding group_aliases columns will be renamed. If there are more columns listed in group_aliases than in group, the extra column names in group_aliases will be ignored.\n\nExample: return the number of records in a dataset.\n\n```yaml\n- operator: record_count\n id: $records_in_dataset\n```\n\nExample: return the number of records where STUDYID = \"CDISC01\" and FLAGVAR = \"Y\".\n\n```yaml\n- operator: record_count\n id: $flagged_cdisc01_records_in_dataset\n filter:\n STUDYID: \"CDISC01\"\n FLAGVAR: \"Y\"\n```\n\nExample: return the number of records grouped by USUBJID and timing variables, extracting only the date portion from datetime values.\n\n```yaml\n- operator: record_count\n id: $records_per_usubjid_date\n group:\n - USUBJID\n - --TESTCD\n - $TIMING_VARIABLES\n regex: \"^\\d{4}-\\d{2}-\\d{2}\"\n```\n\nExample: return the number of records where QNAM starts with \"RACE\" (matches RACE1, RACE2, RACE3, etc.) per USUBJID.\n\n```yaml\n- operator: record_count\n id: $race_records_in_dataset\n filter:\n QNAM: \"RACE&\"\n group:\n - \"USUBJID\"\n```\n\nExample: return the number of records grouped by USUBJID.\n\n```yaml\n- operator: record_count\n id: $records_per_usubjid\n group:\n - USUBJID\n```\n\nExample: return the number of records grouped by USUBJID where FLAGVAR = \"Y\".\n\n```yaml\n- operator: record_count\n id: $flagged_records_per_usubjid\n group:\n - USUBJID\n filter:\n FLAGVAR: \"Y\"\n```\n\nExample: return the number of records grouped by USUBJID and IDVARVAL where QNAM = \"TEST1\" and IDVAR = \"GROUPID\", renaming the IDVARVAL column to GROUPID for subsequent merging.\n\n```yaml\n- operator: record_count\n id: $test1_records_per_usubjid_groupid\n group:\n - USUBJID\n - IDVARVAL\n filter:\n QNAM: \"TEST1\"\n IDVAR: \"GROUPID\"\n group_aliases:\n - USUBJID\n - GROUPID\n```\n\nExample: Group the StudyIdentifier dataset by parent_id and merge the result back to the context dataset StudyVersion using StudyVersion.id == StudyIdentifier.parent_id\n\n```yaml\nScope:\n Entities:\n Include:\n - StudyVersion\nOperations:\n - domain: StudyIdentifier\n filter:\n parent_entity: \"StudyVersion\"\n parent_rel: \"studyIdentifiers\"\n rel_type: \"definition\"\n studyIdentifierScope.organizationType.code: \"C70793\"\n studyIdentifierScope.organizationType.codeSystem: \"http://www.cdisc.org\"\n group:\n - parent_id\n group_aliases:\n - id\n id: $num_sponsor_ids\n operator: record_count\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -310,7 +422,10 @@ "markdownDescription": "\nReturns the required variables ( \"Core\" = Req ) for a given domain and standard Variable Metadata for custom domains will pull from the model while non-custom domains will be from the IG and Model.\n\nInput:\n\nTarget Domain: LB\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\n- operator: required_variables\n id: $required_variables\n```\n\nOutput:\n\n```\n[\"STUDYID\", \"DOMAIN\", \"USUBJID\", \"LBSEQ\", \"LBTESTCD\", \"LBTEST\"]\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -320,7 +435,12 @@ "markdownDescription": "\nSplits a dataset column by a given delimiter\n\n```yaml\nOperations:\n - name: PPSPEC\n delimiter: ;\n id: $ppspec_value\n operator: split_by\n```\n" } }, - "required": ["id", "operator", "delimiter", "name"], + "required": [ + "id", + "operator", + "delimiter", + "name" + ], "type": "object" }, { @@ -330,7 +450,10 @@ "markdownDescription": "\nReturns a list of the domains in the study\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -340,7 +463,10 @@ "markdownDescription": "\nReturns a list of the submitted dataset filenames in all uppercase\n\nex. if TS.xpt, AE.xpt, EC.xpt, and SUPPEC.xpt are submitted -> [TS, AE, EC, SUPPEC] will be returned\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -350,7 +476,10 @@ "markdownDescription": "\nReturns a list of valid SDTM domain names from the standard metadata. This can be used to compare extracted suffixes from DOMAIN values or dataset names.\n\nInput\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\nOperations:\n - operator: standard_domains\n id: $valid_domain_names\n```\n\nOutput\n\n```\n[\"AE\", \"CM\", \"DM\", \"FA\", \"LB\", \"QS\", ...]\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -360,7 +489,10 @@ "markdownDescription": "\nReturns the valid terminology package dates for a given standard.\n\nGiven a list of terminology packages:\n\n```\n[\n \"sdtmct-2023-10-26\",\n \"sdtmct-2023-12-13\",\n \"adamct-2023-12-13\",\n \"cdashct-2023-05-19\"\n]\n```\n\nand standard: sdtmig\n\nthe operation will return:\n\n```\n[\"2023-10-26\", \"2023-12-13\"]\n```\n\nBy default, the standard is as specified when running validation - as the validation runtime parameter and/or as specified in the rule header - and the list of terminology packages is obtained from the current cache. If required, the default standard may be overridden using the optional ct_package_types parameter. For example, given the same list of terminology packages, the following operation:\n\n```yaml\nOperations:\n - operator: valid_codelist_dates\n id: $valid_dates\n ct_package_types:\n - SDTM\n - CDASH\n```\n\nwill return:\n\n```\n[\"2023-05-19\", \"2023-10-26\", \"2023-12-13\"]\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -370,7 +502,11 @@ "markdownDescription": "\nReturns true if the version of an external dictionary provided in the define.xml file matches the version parsed from the dictionary files.\n\nInput:\n\n```yaml\nOperations:\n - operator: valid_define_external_dictionary_version\n id: $is_valid_loinc_version\n external_dictionary_type: loinc\n```\n\nOutput:\n\n```\n[true, true, true, true]\n```\n" } }, - "required": ["id", "operator", "external_dictionary_type"], + "required": [ + "id", + "operator", + "external_dictionary_type" + ], "type": "object" }, { @@ -428,7 +564,10 @@ "markdownDescription": "\nDetermines whether the values are valid in the following variables:\n\n- --SOCCD (System Organ Class Code)\n- --HLGTCD (High Level Group Term Code)\n- --HLTCD (High Level Term Code)\n- --PTCD (Preferred Term Code)\n- --LLTCD (Lowest Level Term Code)\n\nInput:\n\n```yaml\nOperations:\n - id: $is_valid_meddra_codes\n operator: valid_meddra_code_references\n```\n\nOutput:\n\n```\n[true, false, true, true]\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -438,7 +577,10 @@ "markdownDescription": "\nDetermines whether the values are valid in the following variable pairs:\n\n- --SOCCD, --SOC (System Organ Class Code and Term)\n- --HLGTCD, --HLGT (High Level Group Term Code and Term)\n- --HLTCD, --HLT (High Level Term Code and Term)\n- --PTCD, --DECOD (Preferred Term Code and Dictionary-Derived Term)\n- --LLTCD, --LLT (Lowest Level Term Code and Term)\n\nInput:\n\n```yaml\nOperations:\n - id: $is_valid_meddra_pairs\n operator: valid_meddra_code_term_pairs\n```\n\nOutput:\n\n```\n[true, true, false, true, true]\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -448,7 +590,10 @@ "markdownDescription": "\nDetermines whether the values are valid in the following variables:\n\n- --SOC (System Organ Class)\n- --HLGT (High Level Group Term)\n- --HLT (High Level Term)\n- --DECOD (Dictionary-Derived Term)\n- --LLT (Lowest Level Term)\n\nInput:\n\n```yaml\nOperations:\n - id: $is_valid_meddra_terms\n operator: valid_meddra_term_references\n```\n\nOutput:\n\n```\n[true, true, false, true, true]\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -458,7 +603,11 @@ "markdownDescription": "\nChecks if a reference to whodrug term in name points to the existing code in Atc Text (INA) file.\n\nInput:\n\n```yaml\nOperations:\n - id: $whodrug_refs_valid\n operator: valid_whodrug_references\n```\n\nOutput:\n\n```\n[true, false, true, true]\n```\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -468,7 +617,10 @@ "markdownDescription": "\nReturns a mapping of variable names to the number of times that variable appears in a domain within the study.\n\nInput\n\n```\n{\n \"AE\": [\"STUDYID\", \"DOMAIN\", \"USUBJID\", \"AETERM\", \"AEENDTC\"],\n \"LB\": [\"STUDYID\", \"DOMAIN\", \"USUBJID\", \"LBTESTCD\", \"LBENDTC\"]\n}\n```\n\nOutput\n\n```\n{\n \"STUDYID\": 2,\n \"DOMAIN\": 2,\n \"USUBJID\": 2,\n \"--TERM\": 1,\n \"--TESTCD\": 1,\n \"--ENDTC\": 2\n}\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -478,7 +630,10 @@ "markdownDescription": "\nOperation operates only on original submission datasets regardless of rule type. Flags an error if a column exists is in the submission dataset currently being evaluated.\n\nRule Type: Domain Presence Check\n\n```yaml\nCheck:\n all:\n - name: $MIDS_EXISTS\n operator: equal_to\n value: true\n - name: TM\n operator: not_exists\nOperations:\n - id: $MIDS_EXISTS\n name: MIDS\n operator: variable_exists\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -488,7 +643,10 @@ "markdownDescription": "\nReturns true if a variable is missing from the dataset or if all values within the variable are null or empty string. This operation first checks if the target variable exists in the dataset, and if it does exist, evaluates whether all its values are null or empty.\nThe operation supports two sources via the `source` parameter:\n\n- **`submission`** : checks against the raw submission dataset\n- **`evaluation`** (default): checks against the evaluation dataset built based on the rule type\n\n```yaml" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -498,7 +656,10 @@ "markdownDescription": "\nReturns the set of variable names from the library for the given standard. This operation extracts all variable names across all domains in the specified standard's library metadata.\n\nInput:\n\nValidation Standard: sdtmig\nValidation Version: 3-4\n\n```yaml\n- operator: variable_names\n id: $all_variables\n```\n\nOutput:\n\n```\n[\"STUDYID\", \"DOMAIN\", \"USUBJID\", \"SUBJID\", \"RFSTDTC\", \"RFENDTC\", \"SITEID\", \"AGE\", \"AGEU\", \"SEX\", \"RACE\", \"ETHNIC\", \"ARMCD\", \"ARM\", \"ACTARMCD\", \"ACTARM\", \"COUNTRY\", \"DMDTC\", \"DMDY\", \"AETERM\", \"AEDECOD\", \"AECAT\", \"AESCAT\", \"AEPRESP\", \"AEBODSYS\", \"AEBDSYCD\", \"AESOC\", \"AESOCCD\", \"AELLT\", \"AELLTCD\", \"AEHLT\", \"AEHLTCD\", \"AEHLGT\", \"AEHLGTCD\", \"AEPTCD\", \"AESTDTC\", \"AEENDTC\", \"AESTDY\", \"AEENDY\", \"AEDUR\", \"AESER\", \"AESEV\", \"AEACN\", \"AEREL\", \"AEOUT\", \"AESCAN\", \"AESCONG\", \"AESDISAB\", \"AESDTH\", \"AESHOSP\", \"AESLIFE\", \"AESOD\", \"AECONTRT\", \"AETOXGR\", \"LBTESTCD\", \"LBTEST\", \"LBCAT\", \"LBSCAT\", \"LBSPEC\", \"LBMETHOD\", \"LBORRES\", \"LBORRESU\", \"LBORNRLO\", \"LBORNRHI\", \"LBSTRESC\", \"LBSTRESN\", \"LBSTRESU\", \"LBSTNRLO\", \"LBSTNRHI\", \"LBNRIND\", \"LBNAM\", \"LBSPEC\", \"LBANTREG\", \"LBFAST\", \"LBDRVFL\", \"LBTOX\", \"LBTOXGR\", \"LBSTDTC\", \"LBENDTC\", \"LBSTDY\", \"LBENDY\", \"LBTPT\", \"LBTPTNUM\", \"LBELTM\", \"LBTPTREF\", \"LBRFTDTC\", \"...\"]\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -508,7 +669,11 @@ "markdownDescription": "\nGiven a variable name, returns a mapping of variable values to the number of times that value appears in the variable within all datasets in the study.\n" } }, - "required": ["id", "operator", "name"], + "required": [ + "id", + "operator", + "name" + ], "type": "object" }, { @@ -518,7 +683,10 @@ "markdownDescription": "\nDetermines whether the values are valid and in the correct hierarchical structure in the following variables:\n\n- --DECOD\n- --CLAS\n- --CLASCD\n\nInput:\n\n```yaml\nOperations:\n - id: $valid_whodrug_codes\n operator: whodrug_code_hierarchy\n```\n" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" }, { @@ -528,7 +696,12 @@ "markdownDescription": "\nValidates XHTML fragments in the target column against the specified namespace.\n\n```yaml\nOperations:\n - id: $xhtml_errors\n name: text\n operator: get_xhtml_errors\n namespace: http://www.cdisc.org/ns/usdm/xhtml/v1.0\n```\n\nNote that a local XSD file is required for validation. The file must be stored in the folder indicated by the value of the `LOCAL_XSD_FILE_DIR` default file path and the mapping between the namespace and the local XSD file's `sub-folder/name` must be included in the value of the `LOCAL_XSD_FILE_MAP` default file path.\n" } }, - "required": ["id", "operator", "name", "namespace"], + "required": [ + "id", + "operator", + "name", + "namespace" + ], "type": "object" } ], @@ -591,7 +764,13 @@ "type": "string" }, "dictionary_term_type": { - "enum": ["LLT", "PT", "HLT", "HLGT", "SOC"] + "enum": [ + "LLT", + "PT", + "HLT", + "HLGT", + "SOC" + ] }, "domain": { "anyOf": [ @@ -607,7 +786,9 @@ ] }, "external_dictionary_type": { - "enum": ["meddra"] + "enum": [ + "meddra" + ] }, "filter": { "type": "object" @@ -652,7 +833,10 @@ }, "level": { "type": "string", - "enum": ["codelist", "term"] + "enum": [ + "codelist", + "term" + ] }, "map": { "type": "array", @@ -663,7 +847,9 @@ "type": "string" } }, - "required": ["output"] + "required": [ + "output" + ] } }, "name": { @@ -680,7 +866,11 @@ }, "returntype": { "type": "string", - "enum": ["code", "value", "pref_term"] + "enum": [ + "code", + "value", + "pref_term" + ] }, "source": { "type": "string" @@ -707,6 +897,9 @@ "type": "string" } }, - "required": ["id", "operator"], + "required": [ + "id", + "operator" + ], "type": "object" } diff --git a/resources/schema/rule-merged/Operator.json b/resources/schema/rule-merged/Operator.json index e1b988e6a..b1dfc4773 100644 --- a/resources/schema/rule-merged/Operator.json +++ b/resources/schema/rule-merged/Operator.json @@ -9,7 +9,9 @@ "const": "additional_columns_empty" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -18,7 +20,9 @@ "const": "additional_columns_not_empty" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -28,7 +32,10 @@ "markdownDescription": "\nWill return True if the value in `value` is contained within the collection/iterable in the target column, or if there's an exact match for non-iterable data.\n\nThe operator checks if every value in a column is a list or set. If yes, it compares row-by-row. If any value is blank or a different type (like a string or number), it compares each value against the entire column instead.\n\nExample:\n\n```yaml\n- name: \"--TOXGR\" # Column containing lists like ['GRADE', 'SEVERITY', 'ONSET']\n operator: \"contains\"\n value: \"GRADE\" # True if 'GRADE' is an element in the list\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -38,7 +45,9 @@ "markdownDescription": "\nTrue if all values in `value` are contained within the variable `name`.\n\n> All of ('Screen Failure', 'Not Assigned', 'Not Treated', 'Unplanned Treatment') in ACTARM\n\n```yaml\n- name: \"ACTARM\"\n operator: \"contains_all\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n - \"Not Treated\"\n - \"Unplanned Treatment\"\n```\n\nThe operator also supports lists:\n\n```yaml\n- name: \"$spec_codelist\"\n operator: \"contains_all\"\n value: \"$ppspec_value\"\n```\n\nWhere:\n\n| $spec_codelist | $ppspec_value |\n| :-------------------------- | :----------------: |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE1\", \"CODE2\"] |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE2\", \"CODE3\"] |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE1\"] |\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -48,7 +57,10 @@ "markdownDescription": "\nTrue if the value in `value` is contained within the collection/iterable in the target column, performing case-insensitive comparison.\n\nExample:\n\n```yaml\n- name: \"--TOXGR\" # Column containing lists like ['Grade', 'Severity', 'Onset']\n operator: \"contains_case_insensitive\"\n value: \"grade\" # True if 'Grade'/'GRADE'/'grade' exists in the list\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -58,7 +70,10 @@ "markdownDescription": "\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified.\n\nThe `date_component` parameter accepts: `\"year\"`, `\"month\"`, `\"day\"`, `\"hour\"`, `\"minute\"`, `\"second\"`, `\"microsecond\"`, or `\"auto\"`.\n\nWhen `date_component: \"auto\"` is used, the operator automatically detects the precision of both dates and compares at the common (less precise) level.\n\n```yaml\n- name: \"AESTDTC\"\n operator: \"date_equal_to\"\n value: \"RFSTDTC\"\n date_component: \"auto\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -68,7 +83,10 @@ "markdownDescription": "\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified. Supports `date_component: \"auto\"`.\n\n> Year part of BRTHDTC > 2021\n\n```yaml\n- name: \"BRTHDTC\"\n operator: \"date_greater_than\"\n date_component: \"year\"\n value: \"2021\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -78,7 +96,10 @@ "markdownDescription": "\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified. Supports `date_component: \"auto\"`.\n\n> Year part of BRTHDTC >= 2021\n\n```yaml\n- name: \"BRTHDTC\"\n operator: \"date_greater_than_or_equal_to\"\n date_component: \"year\"\n value: \"2021\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -88,7 +109,10 @@ "markdownDescription": "\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified. Supports `date_component: \"auto\"`.\n\n> AEENDTC < AESTDTC\n\n```yaml\n- name: \"AEENDTC\"\n operator: \"date_less_than\"\n value: \"AESTDTC\"\n```\n\n> SSDTC < all DS.DSSTDTC when SSSTRESC = \"DEAD\"\n\n```yaml\nCheck:\n all:\n - name: \"SSSTRESC\"\n operator: \"equal_to\"\n value: \"DEAD\"\n - name: \"SSDTC\"\n operator: \"date_less_than\"\n value: \"$max_ds_dsstdtc\"\nOperations:\n - operator: \"max_date\"\n domain: \"DS\"\n name: \"DSSTDTC\"\n id: \"$max_ds_dsstdtc\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -98,7 +122,10 @@ "markdownDescription": "\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified. Supports `date_component: \"auto\"`.\n\n> AEENDTC <= AESTDTC\n\n```yaml\n- name: \"AEENDTC\"\n operator: \"date_less_than_or_equal_to\"\n value: \"AESTDTC\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -108,7 +135,10 @@ "markdownDescription": "\nComplement of `date_equal_to`\n\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified. Supports `date_component: \"auto\"`.\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -118,7 +148,10 @@ "markdownDescription": "\nComplement of `contains`. Returns True when the value is NOT contained within the target collection.\n\n```yaml\n- name: \"--TOXGR\"\n operator: \"does_not_contain\"\n value: \"GRADE\" # True if 'GRADE' is NOT an element in the list\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -128,7 +161,10 @@ "markdownDescription": "\nComplement of `contains_case_insensitive`. Returns True when the value is NOT contained within the target collection (case-insensitive).\n\nExample:\n\n```yaml\n- name: \"--TOXGR\"\n operator: \"does_not_contain_case_insensitive\"\n value: \"grade\" # True if no case variation of 'grade' exists in the list\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -141,7 +177,11 @@ "type": "boolean" } }, - "required": ["operator", "value", "regex"], + "required": [ + "operator", + "value", + "regex" + ], "type": "object" }, { @@ -151,7 +191,12 @@ "markdownDescription": "\nComplement of `has_next_corresponding_record`\n" } }, - "required": ["operator", "ordering", "value", "within"], + "required": [ + "operator", + "ordering", + "value", + "within" + ], "type": "object" }, { @@ -161,7 +206,9 @@ "markdownDescription": "\nValue presence\n\n> --OCCUR = null\n\n```yaml\n- name: --OCCUR\n operator: empty\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -171,7 +218,10 @@ "markdownDescription": "\n> SEENDTC is not empty when it is not the last record, grouped by USUBJID, sorted by SESTDTC\n\n```yaml\n- name: SEENDTC\n operator: empty_within_except_last_row\n ordering: SESTDTC\n value: USUBJID\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -181,7 +231,10 @@ "markdownDescription": "\nSubstring matching\n\n> DOMAIN ending with 'FOOBAR'\n\n```yaml\n- name: \"DOMAIN\"\n operator: \"ends_with\"\n value: \"FOOBAR\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -200,7 +253,10 @@ "type": "boolean" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -219,7 +275,10 @@ "type": "boolean" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -232,7 +291,11 @@ "type": "boolean" } }, - "required": ["operator", "value", "regex"], + "required": [ + "operator", + "value", + "regex" + ], "type": "object" }, { @@ -242,7 +305,9 @@ "markdownDescription": "\nTrue if the column exists in the current dataframe. (Works for datasets and variables)\n\n> --OCCUR is present in dataset\n\n```yaml\n- name: \"--OCCUR\"\n operator: \"exists\"\n```\n\n> Domain SJ exists\n\n```yaml\nRule Type: Domain Presence Check\nCheck:\n all:\n - name: \"SJ\"\n operator: \"exists\"\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -252,7 +317,10 @@ "markdownDescription": "\nValue comparison\n\n> TSVAL > 0\n\n```yaml\n- name: TSVAL\n operator: greater_than\n value: 0\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -262,7 +330,10 @@ "markdownDescription": "\nValue comparison\n\n> TSVAL >= 0\n\n```yaml\n- name: TSVAL\n operator: greater_than_or_equal_to\n value: 1\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -272,7 +343,9 @@ "markdownDescription": "\nComplement of `has_same_values`\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -282,7 +355,9 @@ "markdownDescription": "\nLength comparison\n\n> Check whether variable values has equal length of another variable.\n\n```yaml\n- name: SEENDTC\n operator: has_equal_length\n value: SESTDTC\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -292,7 +367,12 @@ "markdownDescription": "\nEnsures that a value of a variable `name` in one record is equal to the value of another variable `value` in the next corresponding record. The rows are grouped by `within` and ordered by `ordering`.\n\n> SEENDTC is equal to the SESTDTC of the next record within a USUBJID. Ordered by SESEQ\n\n```yaml\n- name: SEENDTC\n operator: has_next_corresponding_record\n value: SESTDTC\n within: USUBJID\n ordering: SESEQ\n```\n" } }, - "required": ["operator", "ordering", "value", "within"], + "required": [ + "operator", + "ordering", + "value", + "within" + ], "type": "object" }, { @@ -302,7 +382,9 @@ "markdownDescription": "\nComplement of `has_equal_length`\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -312,7 +394,9 @@ "markdownDescription": "\nTrue if all values in `name` are the same\n\n> Condition: MHCAT ^= null\n> Rule: MHCAT ^= the same value for all records\n\n```yaml\nCheck:\n all:\n - name: MHCAT\n operator: non_empty\n - name: MHCAT\n operator: has_same_values\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -322,7 +406,10 @@ "markdownDescription": "\nDuration ISO-8601 check, returns True if a duration is not in ISO-8601 format. The negative parameter must be specified to indicate if negative durations are either allowed (True) or disallowed (False)\n\n> DURVAR is invalid (negative durations disallowed)\n\n```yaml\n- name: \"DURVAR\"\n operator: \"invalid_duration\"\n negative: False\n```\n" } }, - "required": ["operator", "negative"], + "required": [ + "operator", + "negative" + ], "type": "object" }, { @@ -332,7 +419,9 @@ "markdownDescription": "\nThe operator performs date validation against complete and partial dates with uncertainty in the following order:\n\n1. Attempts to parse using [dateutil.parser.isoparse()](https://dateutil.readthedocs.io/en/stable/parser.html)\n2. If parsing fails and the string contains uncertainty indicators (`/`, `--`, `-:`), validates against an extended ISO 8601 dates regex pattern\n3. If parsing succeeds, dates are still validated against the regex pattern.\n\n```yaml\n- name: \"BRTHDTC\"\n operator: \"invalid_date\"\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -342,7 +431,9 @@ "markdownDescription": "\nDate check\n\n> DM.RFSTDTC = complete date\n\n```yaml\n- name: \"RFSTDTC\"\n operator: \"is_complete_date\"\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -352,7 +443,10 @@ "markdownDescription": "\nValue in `name` compared against a list in `value`. The list can have literal values or be a reference to a `$variable`.\n\nThis operator behaves similarly to `contains`. The key distinction: `contains` checks if comparator \u2208 target, while `is_contained_by` checks if target \u2208 comparator.\n\n> ACTARM in ('Screen Failure', 'Not Assigned', 'Not Treated', 'Unplanned Treatment')\n\n```yaml\n- name: \"ACTARM\"\n operator: \"is_contained_by\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n - \"Not Treated\"\n - \"Unplanned Treatment\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -362,7 +456,10 @@ "markdownDescription": "\nValue in `name` case insensitive compared against a list in `value`. The list can have literal values or be a reference to a `$variable`.\n\n> ACTARM in ('Screen Failure', 'Not Assigned', 'Not Treated', 'Unplanned Treatment')\n\n```yaml\n- name: \"ACTARM\"\n operator: \"is_contained_by_case_insensitive\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n - \"Not Treated\"\n - \"Unplanned Treatment\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -372,7 +469,9 @@ "markdownDescription": "\nComplement of `is_complete_date`\n\nDate check\n\n> DM.RFSTDTC ^= complete date\n\n```yaml\n- name: \"RFSTDTC\"\n operator: \"is_incomplete_date\"\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -382,7 +481,10 @@ "markdownDescription": "\nComplement of `is_contained_by`\n\n> ARM not in ('Screen Failure', 'Not Assigned')\n\n```yaml\n- name: \"ARM\"\n operator: \"is_not_contained_by\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -392,7 +494,10 @@ "markdownDescription": "\nComplement of `is_contained_by_case_insensitive`\n\n> ARM not in ('Screen Failure', 'Not Assigned')\n\n```yaml\n- name: \"ARM\"\n operator: \"is_not_contained_by_case_insensitive\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -402,7 +507,10 @@ "markdownDescription": "\nComplement of `is_ordered_by`\n" } }, - "required": ["operator", "order"], + "required": [ + "operator", + "order" + ], "type": "object" }, { @@ -411,7 +519,10 @@ "const": "is_not_ordered_set" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -421,7 +532,10 @@ "markdownDescription": "\nComplement of `is_unique_relationship`\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -431,7 +545,9 @@ "markdownDescription": "\nComplement of `is_unique_set`.\n\n> --SEQ is not unique within DOMAIN, USUBJID, and --TESTCD\n\n```yaml\n- name: \"--SEQ\"\n operator: is_not_unique_set\n value:\n - \"DOMAIN\"\n - \"USUBJID\"\n - \"--TESTCD\"\n```\n\n> `name` can be a variable containing a list of columns and `value` does not need to be present\n\n```yaml\nRule Type: Dataset Contents Check against Define XML\nCheck:\n all:\n - name: define_dataset_key_sequence # contains list of dataset key columns\n operator: is_not_unique_set\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -441,7 +557,10 @@ "markdownDescription": "\nTrue if the dataset rows are ordered by the values within `name`, given the ordering specified by `order`\n\n```yaml\nCheck:\n all:\n - name: --SEQ\n operator: is_ordered_by\n order: asc\n```\n" } }, - "required": ["operator", "order"], + "required": [ + "operator", + "order" + ], "type": "object" }, { @@ -451,7 +570,36 @@ "markdownDescription": "\nTrue if the dataset rows are in ascending order of the values within `name`, grouped by the values within `value`. Value can either be a single column or multiple.\n\n```yaml\nCheck:\n all:\n - name: --SEQ\n operator: is_ordered_set\n value: USUBJID\n```\n\n```yaml\nCheck:\n all:\n - name: --SEQ\n operator: is_ordered_set\n value:\n - USUBJID\n - \"--TESTCD\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], + "type": "object" + }, + { + "properties": { + "operator": { + "const": "is_consecutive_ordered_set", + "markdownDescription": "\nTrue if the dataset rows are in ascending order of values within `name`, grouped by `value`, and there are no skips between successive sequence values.\n\nThis extends `is_ordered_set` with continuity checking.\n\nRules:\n- Duplicates are allowed (e.g., 1, 1, 2, 3).\n- The sequence may start at any integer.\n- Null/empty/non-numeric values in `name` evaluate to false (strict behavior).\n\n```yaml\nCheck:\n all:\n - name: --SEQ\n operator: is_consecutive_ordered_set\n value: USUBJID\n```\n```yaml\nCheck:\n all:\n - name: --SEQ\n operator: is_consecutive_ordered_set\n value:\n - USUBJID\n - \"--TESTCD\"\n```\n" + } + }, + "required": [ + "operator", + "value" + ], + "type": "object" + }, + { + "properties": { + "operator": { + "const": "is_not_consecutive_ordered_set", + "markdownDescription": "\nComplement of `is_consecutive_ordered_set`\n" + } + }, + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -461,7 +609,10 @@ "markdownDescription": "\nRelationship Integrity Check looking for a 1-1 relationship between name and value. Ensures uniqueness of both name and value.\n\n> AETERM and AEDECOD has a 1-to-1 relationship\n\n```yaml\n- name: AETERM\n operator: is_unique_relationship\n value: AEDECOD\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -471,7 +622,10 @@ "markdownDescription": "\nChecks if a variable maintains consistent values within groups defined by one or more grouping variables. Groups records by specified value(s) and validates that the target variable maintains the same value within each unique combination of grouping variables. When inconsistency is detected within a group, the operator attempts to identify a majority value. If one value appears more frequently than all others, only the minority records (those not matching the majority value) are flagged. If no single majority exists \u2014 i.e., two or more values are tied for the highest frequency \u2014 all records in that group are flagged.\n\nSingle grouping variable - true if the values of BGSTRESU differ within USUBJID:\n\nIf a regex parameter is provided, it is applied to the values of the target variable before the consistency check. The first capture group of the regex is used as the normalized value for comparison. This can be useful when only part of the value should be considered during comparison (for example, comparing only the date portion of a datetime value).\n\n- regex is optional.\n- The pattern must include at least one capture group(or whole regex will be wrapped to capture group).\n- Only the first capture group is used for comparison.\n- If the pattern does not match a value, the original value is used.\n\n```yaml\n- name: \"BGSTRESU\"\n operator: is_inconsistent_across_dataset\n value: \"USUBJID\"\n```\n\nMultiple grouping variables - true if the values of --STRESU differ within each combination of --TESTCD, --CAT, --SCAT, --SPEC, and --METHOD:\n\n```yaml\n- name: \"--STRESU\"\n operator: is_inconsistent_across_dataset\n value:\n - \"--TESTCD\"\n - \"--CAT\"\n - \"--SCAT\"\n - \"--SPEC\"\n - \"--METHOD\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -481,7 +635,9 @@ "markdownDescription": "\nRelationship Integrity Check\n\n> --SEQ is unique within DOMAIN, USUBJID, and --TESTCD\n\n```yaml\n- name: \"--SEQ\"\n operator: is_unique_set\n value:\n - \"DOMAIN\"\n - \"USUBJID\"\n - \"--TESTCD\"\n```\n\n> `name` can be a variable containing a list of columns and `value` does not need to be present\n\n> The `regex` parameter allows you to extract portions of values using a regex pattern before checking uniqueness.\n\n> Compare date only (YYYY-MM-DD) for uniqueness\n\n```yaml\n- name: \"--REPNUM\"\n operator: is_not_unique_set\n value:\n - \"USUBJID\"\n - \"--TESTCD\"\n - \"$TIMING_VARIABLES\"\n regex: '^\\d{4}-\\d{2}-\\d{2}'\n```\n\n> Compare by first N characters of a string\n\n```yaml\n- name: \"ITEM_ID\"\n operator: is_not_unique_set\n value:\n - \"USUBJID\"\n - \"CATEGORY\"\n regex: \"^.{2}\"\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -491,7 +647,10 @@ "markdownDescription": "\nValue comparison\n\n> TSVAL < 1\n\n```yaml\n- name: TSVAL\n operator: less_than\n value: 1\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -501,7 +660,10 @@ "markdownDescription": "\nValue comparison\n\n> TSVAL <= 1\n\n```yaml\n- name: TSVAL\n operator: less_than_or_equal_to\n value: 1\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -511,7 +673,10 @@ "markdownDescription": "\nLength comparison\n\n> SETCD value length > 8\n\n```yaml\n- name: \"SETCD\"\n operator: \"longer_than\"\n value: 8\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -521,7 +686,10 @@ "markdownDescription": "\nLength comparison\n\n> TSVAL value length >= 201\n\n```yaml\n- name: \"TSVAL\"\n operator: \"longer_than_or_equal_to\"\n value: 201\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -531,7 +699,10 @@ "markdownDescription": "\nRegular Expression value matching\n\n- Determine if each string starts with a match of a regular expression. Refer to this pandas documentation: https://pandas.pydata.org/docs/reference/api/pandas.Series.str.match.html\n- To \"search\" for a regex within the entire text, prefix the regex with `.*` and do not use anchors `^` , `$`\n- To do a \"fullmatch\" of a regex with the entire text, suffix the regex with an anchor `$` and do not prefix the regex with `.*`\n- For syntax guide, refer to this Python documentation: [Regular Expression HOWTO](https://docs.python.org/3/howto/regex.html).\n- Suggestion for an on-line regular expression logic. tester: https://regex101.com, choose the Python dialect.\n- For regex token visualization, try https://www.debuggex.com.\n\n> --DOSTXT value is non-numeric\n\n```yaml\n- name: --DOSTXT\n operator: matches_regex\n value: ^\\d*\\.?\\d*$\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -541,7 +712,9 @@ "markdownDescription": "\nComplement of `empty`\n\n> --OCCUR ^= null\n\n```yaml\n- name: --OCCUR\n operator: non_empty\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -551,7 +724,10 @@ "markdownDescription": "\nComplement of `empty_within_except_last_row`\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -561,7 +737,9 @@ "markdownDescription": "\nComplement of `contains_all`\n\n> All of ('Screen Failure', 'Not Assigned', 'Not Treated', 'Unplanned Treatment') not in ACTARM\n\n```yaml\n- name: \"ACTARM\"\n operator: \"not_contains_all\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n - \"Not Treated\"\n - \"Unplanned Treatment\"\n```\n\nThe operator also supports lists:\n\n```yaml\n- name: \"$spec_codelist\"\n operator: \"not_contains_all\"\n value: \"$ppspec_value\"\n```\n\nWhere:\n\n| $spec_codelist | $ppspec_value |\n| :-------------------------- | :----------------: |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE1\", \"CODE2\"] |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE2\", \"CODE3\"] |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE1\"] |\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -580,7 +758,10 @@ "type": "boolean" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -599,7 +780,10 @@ "type": "boolean" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -609,7 +793,9 @@ "markdownDescription": "\nComplement of `exists`\n\n> AEOCCUR not present in dataset\n\n```yaml\n- name: \"AEOCCUR\"\n operator: \"not_exists\"\n```\n\n> Domain SJ does not exist\n\n```yaml\nRule Type: Domain Presence Check\nCheck:\n all:\n - name: \"SJ\"\n operator: \"not_exists\"\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -619,7 +805,10 @@ "markdownDescription": "\nComplement of `matches_regex`\n\n> --TESTCD <= 8 chars and contains only letters, numbers, and underscores and can not start with a number\n\n```yaml\n- name: --TESTCD\n operator: not_matches_regex\n value: ^[A-Z_][A-Z0-9_]{0,7}$\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -629,7 +818,11 @@ "markdownDescription": "\nComplement of `prefix_matches_regex`\n" } }, - "required": ["operator", "prefix", "value"], + "required": [ + "operator", + "prefix", + "value" + ], "type": "object" }, { @@ -639,7 +832,10 @@ "markdownDescription": "\nComplement of `present_on_multiple_rows_within`\n\n```yaml\n- operator: \"not_present_on_multiple_rows_within\"\n name: \"RELID\"\n value: 4 (optional)\n within: \"USUBJID\"\n```\n" } }, - "required": ["operator", "within"], + "required": [ + "operator", + "within" + ], "type": "object" }, { @@ -649,7 +845,11 @@ "markdownDescription": "\nComplement of `suffix_matches_regex`\n\n> QNAM does not end with numbers\n\n```yaml\n- name: \"QNAM\"\n operator: \"not_suffix_matches_regex\"\n suffix: 2\n value: \"\\d\\d\"\n```\n" } }, - "required": ["operator", "suffix", "value"], + "required": [ + "operator", + "suffix", + "value" + ], "type": "object" }, { @@ -659,7 +859,11 @@ "markdownDescription": "\nTrue if the `prefix` number of characters beginning a string in `name` match one of the strings in the list in `value`\n\n> Check if a variable's domain identifier exists in the study\n\n```yaml\n- name: variable_name\n operator: prefix_is_contained_by\n prefix: 2\n value: $study_domains\n```\n" } }, - "required": ["operator", "prefix", "value"], + "required": [ + "operator", + "prefix", + "value" + ], "type": "object" }, { @@ -669,7 +873,11 @@ "markdownDescription": "\nTrue if the `prefix` number of characters beginning a string in `name` match the string in `value`\n\n```yaml\n- name: dataset_name\n operator: prefix_equal_to\n prefix: 2\n value: DOMAIN\n```\n" } }, - "required": ["operator", "prefix", "value"], + "required": [ + "operator", + "prefix", + "value" + ], "type": "object" }, { @@ -679,7 +887,11 @@ "markdownDescription": "\nComplement of `prefix_is_contained_by`\n" } }, - "required": ["operator", "prefix", "value"], + "required": [ + "operator", + "prefix", + "value" + ], "type": "object" }, { @@ -689,7 +901,11 @@ "markdownDescription": "\nTrue if the `prefix` number of characters beginning a string in `name` match a regular expression in `value`\n\n```yaml\n- name: DOMAIN\n operator: prefix_matches_regex\n prefix: 2\n value: (AP|ap)\n```\n" } }, - "required": ["operator", "prefix", "value"], + "required": [ + "operator", + "prefix", + "value" + ], "type": "object" }, { @@ -699,7 +915,11 @@ "markdownDescription": "\nComplement of `prefix_equal_to`\n" } }, - "required": ["operator", "prefix", "value"], + "required": [ + "operator", + "prefix", + "value" + ], "type": "object" }, { @@ -709,7 +929,10 @@ "markdownDescription": "\nTrue if the same value of `name` is present on multiple rows, grouped by `within`. A maximum allowed number of occurrences can be specified in the value attribute. In this instance the value: 4 means that an error will be flagged if the same value appears more than 4 times within a USUBJID. By default the operator will flag any time a value appears more than once.\n\n```yaml\n- operator: \"present_on_multiple_rows_within\"\n name: \"RELID\"\n value: 4 (optional)\n within: \"USUBJID\"\n```\n" } }, - "required": ["operator", "within"], + "required": [ + "operator", + "within" + ], "type": "object" }, { @@ -719,7 +942,10 @@ "markdownDescription": "\nWill raise an issue if at least one of the values in `name` is the same as one of the values in `value`. See [shares_no_elements_with](#shares_no_elements_with).\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -729,7 +955,10 @@ "markdownDescription": "\nWill raise an issue if exactly one of the values in `name` is the same as one of the values in `value`. See [shares_no_elements_with](#shares_no_elements_with).\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -739,7 +968,10 @@ "markdownDescription": "\nWill raise an issue if the values in `name` do not share any of the values in `value`\n\n> Check if $dataset_variables shares no elements with $timing_variables\n\n```yaml\nRule Type: Dataset Metadata Check # One record per dataset\nCheck:\n - all:\n name: $dataset_variables\n operator: shares_no_elements_with\n value: $timing_variables\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -749,7 +981,10 @@ "markdownDescription": "\nLength comparison\n\n> SETCD value length < 9\n\n```yaml\n- name: \"SETCD\"\n operator: \"shorter_than\"\n value: 9\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -759,7 +994,10 @@ "markdownDescription": "\nLength comparison\n\n> TSVAL value length <= 200\n\n```yaml\n- name: \"TSVAL\"\n operator: \"shorter_than_or_equal_to\"\n value: 201\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -769,7 +1007,9 @@ "markdownDescription": "\nSplits a string by a separator and checks if both parts have equal length. Generic operator for validating paired data formats where both parts must have the same level of detail or precision.\n\nParameters:\n\n- `separator`: The delimiter to split on (default: \"/\")\n\n> Check that string parts separated by a delimiter have equal length\n\n```yaml\n- name: --DTC\n operator: split_parts_have_equal_length\n separator: \"/\"\n```\n\nUse cases:\n\n- **Date/time intervals**: `2003-12-15T10:00/2003-12-15T10:30` \u2192 True (both 16 characters)\n- **Date ranges**: `2003-12-01/2003-12-10` \u2192 True (both 10 characters)\n- **Version ranges**: `1.2.3/2.0.0` \u2192 True (both 5 characters)\n- **Product codes**: `ABC-123/XYZ-789` \u2192 True (both 7 characters)\n\nInvalid example:\n\n- `2003-12-15T10:00/2003-12-15T10:30:15` \u2192 False (16 vs 19 characters - different precision)\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -779,7 +1019,9 @@ "markdownDescription": "\nComplement of `split_parts_have_equal_length`. Returns True when parts have unequal lengths (indicates a violation).\n\n```yaml\n- name: --DTC\n operator: split_parts_have_unequal_length\n separator: \"/\"\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -789,7 +1031,10 @@ "markdownDescription": "\nSubstring matching\n\n> DOMAIN beginning with 'AP'\n\n```yaml\n- name: \"DOMAIN\"\n operator: \"starts_with\"\n value: \"AP\"\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -799,7 +1044,11 @@ "markdownDescription": "\nTrue if the `suffix` number of characters ending a string in `name` match the string in `value`\n\n```yaml\n- name: dataset_name\n operator: suffix_equal_to\n prefix: 2\n value: DOMAIN\n```\n" } }, - "required": ["operator", "suffix", "value"], + "required": [ + "operator", + "suffix", + "value" + ], "type": "object" }, { @@ -809,7 +1058,11 @@ "markdownDescription": "\nTrue if the `suffix` number of characters ending a string in `name` match one of the strings in the list in `value`\n\n> Check if a supp's parent domain exists in the study\n\n```yaml\n- name: dataset_name\n operator: suffix_is_contained_by\n prefix: 2\n value: $study_domains\n```\n" } }, - "required": ["operator", "suffix", "value"], + "required": [ + "operator", + "suffix", + "value" + ], "type": "object" }, { @@ -819,7 +1072,11 @@ "markdownDescription": "\nComplement of `suffix_is_contained_by`\n" } }, - "required": ["operator", "suffix", "value"], + "required": [ + "operator", + "suffix", + "value" + ], "type": "object" }, { @@ -829,7 +1086,11 @@ "markdownDescription": "\nTrue if the `suffix` number of characters ending a string in `name` match a regular expression in `value`\n\n> QNAM ends with numbers\n\n```yaml\n- name: \"QNAM\"\n operator: \"suffix_matches_regex\"\n suffix: 2\n value: \"\\d\\d\"\n```\n" } }, - "required": ["operator", "suffix", "value"], + "required": [ + "operator", + "suffix", + "value" + ], "type": "object" }, { @@ -839,7 +1100,11 @@ "markdownDescription": "\nComplement of `suffix_equal_to`\n" } }, - "required": ["operator", "suffix", "value"], + "required": [ + "operator", + "suffix", + "value" + ], "type": "object" }, { @@ -849,7 +1114,11 @@ "markdownDescription": "\nComplement of `target_is_sorted_by`\n" } }, - "required": ["operator", "value", "within"], + "required": [ + "operator", + "value", + "within" + ], "type": "object" }, { @@ -859,7 +1128,11 @@ "markdownDescription": "\nTrue if the values in name are ordered according to the values specified by value\nin ascending/descending order, grouped by the values in within. Each value entry\nrequires a variable name, a sort_order of asc or desc, and an optional\nnull_position of first or last (defaults to last) which controls where null/empty\ncomparator values are placed in the expected ordering. Within accepts either a\nsingle column or an ordered list of columns. Columns can be either number or Char\nDates in ISO8601 YYYY-MM-DD format. Date value(s) with different precisions that\noverlap (e.g. 2005-10, 2005-10-3 and 2005-10-08) are all flagged as not sorted as\ntheir order cannot be inferred.\n\nOptionally supports a `regex` parameter that extracts a portion of the target\nvalue for sorting. The regex must contain at least one capturing group. The first\ncaptured group is extracted and converted to numeric if possible, allowing proper\nsorting of sequence numbers (e.g., \"MIDS1\", \"MIDS2\", ..., \"MIDS10\" with regex\n`.*?(\\\\d+)$`). This is particularly useful for variables that end with sequence\nnumbers that may or may not be zero-padded.\n\n```yaml\nCheck:\n all:\n - name: --SEQ\n within:\n - USUBJID\n - MIDSTYPE\n operator: target_is_sorted_by\n value:\n - name: --STDTC\n sort_order: asc\n null_position: last\n```\n\nExample with regex for extracting sequence numbers:\n\n```yaml\nCheck:\n all:\n - name: MIDS\n operator: target_is_sorted_by\n regex: \".*?(\\\\d+)$\" # Extract trailing digits, convert to numeric\n value:\n - name: SMSTDTC\n sort_order: asc\n within:\n - USUBJID\n - MIDSTYPE\n```\n" } }, - "required": ["operator", "value", "within"], + "required": [ + "operator", + "value", + "within" + ], "type": "object" }, { @@ -869,7 +1142,10 @@ "markdownDescription": "\nComplement of `value_has_multiple_references`\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -879,7 +1155,10 @@ "markdownDescription": "\nTrue if the value in `name` has more than one count in the dictionary defined in `value`\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -889,7 +1168,10 @@ "markdownDescription": "\nChecks for inconsistencies in enumerated columns of a DataFrame. Starting with the smallest/largest enumeration of the given variable, returns True if VARIABLE(N+1) is populated but VARIABLE(N) is not populated. Repeats for all variables belonging to the enumeration. Note that the initial variable will not have an index (VARIABLE) and the next enumerated variable has index 1 (VARIABLE1).\n\nex: Check if there are inconsistencies in the TSVAL columns (TSVAL, TSVAL1, TSVAL2, etc.)\n\n```yaml\nCheck:\n all:\n - name: \"TSVAL\"\n operator: \"inconsistent_enumerated_columns\"\n```\n" } }, - "required": ["operator", "name"], + "required": [ + "operator", + "name" + ], "type": "object" }, { @@ -899,7 +1181,10 @@ "markdownDescription": "\nChecks if elements in the target list appear in the same relative order in the comparator list.\n\n> Check if dataset column order is a correctly ordered subset of library column order\n\n```yaml\n- name: $column_order_from_dataset\n operator: is_ordered_subset_of\n value: $column_order_from_library\n```\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -909,7 +1194,10 @@ "markdownDescription": "\nComplement of `is_ordered_subset_of`\n" } }, - "required": ["operator", "value"], + "required": [ + "operator", + "value" + ], "type": "object" }, { @@ -919,7 +1207,9 @@ "markdownDescription": "\nValidates 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. \nNOTE: The titlecase library may produce false positives or false negatives in syntactic edge cases (e.g. hyphenated words, slash-separated terms, uncommon prepositions).\n\n> Check that AELABEL values are in proper title case\n\n```yaml\n- name: AELABEL\n operator: is_title_case\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" }, { @@ -929,13 +1219,18 @@ "markdownDescription": "\nComplement of `is_title_case`. Returns True when values are NOT in proper title case.\n\n> Flag AELABEL values that violate title case rules\n\n```yaml\n- name: AELABEL\n operator: is_not_title_case\n```\n" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" } ], "properties": { "comparator": { - "type": ["number", "string"] + "type": [ + "number", + "string" + ] }, "context": { "type": "string" @@ -973,18 +1268,27 @@ "type": "boolean" }, "codelistcheck": { - "enum": ["code", "value"], + "enum": [ + "code", + "value" + ], "type": "string" }, "codelistlevel": { - "enum": ["term", "codelist"], + "enum": [ + "term", + "codelist" + ], "type": "string" }, "operator": { "type": "string" }, "order": { - "enum": ["asc", "dsc"], + "enum": [ + "asc", + "dsc" + ], "type": "string" }, "ordering": { @@ -1002,17 +1306,25 @@ "value": { "oneOf": [ { - "type": ["boolean", "number", "string"] + "type": [ + "boolean", + "number", + "string" + ] }, { "items": { - "type": ["number"] + "type": [ + "number" + ] }, "type": "array" }, { "items": { - "type": ["string"] + "type": [ + "string" + ] }, "type": "array" }, @@ -1023,7 +1335,10 @@ "$ref": "Operator.json#/properties/name" }, "null_position": { - "enum": ["first", "last"], + "enum": [ + "first", + "last" + ], "type": "string" }, "order": { @@ -1066,6 +1381,8 @@ "type": "string" } }, - "required": ["operator"], + "required": [ + "operator" + ], "type": "object" } diff --git a/resources/schema/rule-merged/Organization_CDISC.json b/resources/schema/rule-merged/Organization_CDISC.json index db1041921..9aaef8a76 100644 --- a/resources/schema/rule-merged/Organization_CDISC.json +++ b/resources/schema/rule-merged/Organization_CDISC.json @@ -22,7 +22,9 @@ "const": "Failure" } }, - "required": ["Type"], + "required": [ + "Type" + ], "type": "object" }, "Origin": { @@ -38,7 +40,9 @@ "type": "object" }, "Version": { - "enum": ["5.0"] + "enum": [ + "5.0" + ] } }, "type": "object" @@ -47,7 +51,12 @@ "type": "array" }, "Version": { - "enum": ["1.0", "1.1", "1.2", "1.3"] + "enum": [ + "1.0", + "1.1", + "1.2", + "1.3" + ] } }, "type": "object" @@ -66,7 +75,9 @@ "const": "Success" } }, - "required": ["Type"], + "required": [ + "Type" + ], "type": "object" }, "Origin": { @@ -79,13 +90,19 @@ "type": "string" }, "Version": { - "enum": ["1", "2", "3"] + "enum": [ + "1", + "2", + "3" + ] } }, "type": "object" }, "Version": { - "enum": ["2.0"] + "enum": [ + "2.0" + ] } }, "type": "object" @@ -94,7 +111,11 @@ "type": "array" }, "Version": { - "enum": ["3.2", "3.3", "3.4"] + "enum": [ + "3.2", + "3.3", + "3.4" + ] } }, "type": "object" @@ -113,7 +134,9 @@ "const": "Success" } }, - "required": ["Type"], + "required": [ + "Type" + ], "type": "object" }, "Origin": { @@ -129,7 +152,9 @@ "type": "object" }, "Version": { - "enum": ["5.0"] + "enum": [ + "5.0" + ] } }, "type": "object" @@ -138,7 +163,11 @@ "type": "array" }, "Version": { - "enum": ["3.0", "3.1", "3.1.1"] + "enum": [ + "3.0", + "3.1", + "3.1.1" + ] } }, "type": "object" @@ -157,7 +186,9 @@ "const": "Success" } }, - "required": ["Type"], + "required": [ + "Type" + ], "type": "object" }, "Origin": { @@ -173,7 +204,9 @@ "type": "object" }, "Version": { - "enum": ["5.0"] + "enum": [ + "5.0" + ] } }, "type": "object" @@ -182,7 +215,10 @@ "type": "array" }, "Version": { - "enum": ["1.1", "1.2"] + "enum": [ + "1.1", + "1.2" + ] } }, "type": "object" @@ -201,7 +237,9 @@ "const": "Success" } }, - "required": ["Type"], + "required": [ + "Type" + ], "type": "object" }, "Origin": { @@ -217,7 +255,9 @@ "type": "object" }, "Version": { - "enum": ["5.0"] + "enum": [ + "5.0" + ] } }, "type": "object" @@ -226,7 +266,9 @@ "type": "array" }, "Version": { - "enum": ["1.0"] + "enum": [ + "1.0" + ] } }, "type": "object" @@ -245,7 +287,9 @@ "const": "Success" } }, - "required": ["Type"], + "required": [ + "Type" + ], "type": "object" }, "Origin": { @@ -261,7 +305,9 @@ "type": "object" }, "Version": { - "enum": ["1.0"] + "enum": [ + "1.0" + ] } }, "type": "object" @@ -270,13 +316,24 @@ "type": "array" }, "Version": { - "enum": ["1.0"] + "enum": [ + "1.0" + ] }, "Substandard": { - "enum": ["SDTM", "SEND", "ADaM", "CDASH"] + "enum": [ + "SDTM", + "SEND", + "ADaM", + "CDASH" + ] } }, - "required": ["Name", "Version", "Substandard"], + "required": [ + "Name", + "Version", + "Substandard" + ], "type": "object" }, { @@ -297,13 +354,17 @@ "type": "string" }, "Version": { - "enum": ["1"] + "enum": [ + "1" + ] } }, "type": "object" }, "Version": { - "enum": ["1.0"] + "enum": [ + "1.0" + ] } }, "type": "object" @@ -312,7 +373,10 @@ "type": "array" }, "Version": { - "enum": ["3.0", "4.0"] + "enum": [ + "3.0", + "4.0" + ] } }, "type": "object" diff --git a/resources/schema/rule-merged/Organization_Custom.json b/resources/schema/rule-merged/Organization_Custom.json index bf5bd7276..c5f591394 100644 --- a/resources/schema/rule-merged/Organization_Custom.json +++ b/resources/schema/rule-merged/Organization_Custom.json @@ -9,7 +9,10 @@ "type": "string", "description": "Name of your custom organization", "not": { - "enum": ["CDISC", "FDA"] + "enum": [ + "CDISC", + "FDA" + ] } }, "Standards": { @@ -45,7 +48,9 @@ "description": "Version of the rule" } }, - "required": ["Id"], + "required": [ + "Id" + ], "type": "object" }, "Version": { @@ -55,7 +60,10 @@ "Criteria": { "properties": { "Type": { - "enum": ["Failure", "Success"], + "enum": [ + "Failure", + "Success" + ], "type": "string" }, "Plain Language Expression": { @@ -70,30 +78,46 @@ "type": "string" } }, - "required": ["Rule"], + "required": [ + "Rule" + ], "type": "object" } }, - "required": ["Type"], + "required": [ + "Type" + ], "anyOf": [ { - "required": ["Logical Expression"] + "required": [ + "Logical Expression" + ] }, { - "required": ["Plain Language Expression"] + "required": [ + "Plain Language Expression" + ] } ], "type": "object" } }, - "required": ["Origin", "Rule Identifier", "Version"], + "required": [ + "Origin", + "Rule Identifier", + "Version" + ], "type": "object" }, "minItems": 1, "type": "array" } }, - "required": ["Name", "References", "Version"], + "required": [ + "Name", + "References", + "Version" + ], "type": "object" }, "minItems": 1, @@ -141,7 +165,10 @@ }, "OutputType": { "type": "string", - "enum": ["Check", "Listing"], + "enum": [ + "Check", + "Listing" + ], "description": "Output type of the rule validation result" }, "Keywords": { @@ -155,7 +182,11 @@ "additionalProperties": true } }, - "required": ["Organization", "Standards", "Category"], + "required": [ + "Organization", + "Standards", + "Category" + ], "type": "object", "$defs": { "metadata": { diff --git a/resources/schema/rule-merged/Organization_FDA.json b/resources/schema/rule-merged/Organization_FDA.json index b0f7de783..94af54bc4 100644 --- a/resources/schema/rule-merged/Organization_FDA.json +++ b/resources/schema/rule-merged/Organization_FDA.json @@ -41,7 +41,10 @@ } } ], - "required": ["Document", "Cited Guidance"], + "required": [ + "Document", + "Cited Guidance" + ], "type": "object" }, "type": "array" @@ -52,7 +55,9 @@ "const": "Success" } }, - "required": ["Type"], + "required": [ + "Type" + ], "type": "object" }, "Origin": { @@ -68,7 +73,9 @@ "type": "object" }, "Version": { - "enum": ["1.5"] + "enum": [ + "1.5" + ] } }, "type": "object" @@ -84,7 +91,11 @@ "const": "SDTMIG" }, "Version": { - "enum": ["3.2", "3.3", "3.4"] + "enum": [ + "3.2", + "3.3", + "3.4" + ] } }, "type": "object" @@ -95,7 +106,11 @@ "const": "SENDIG" }, "Version": { - "enum": ["3.0", "3.1", "3.1.1"] + "enum": [ + "3.0", + "3.1", + "3.1.1" + ] } }, "type": "object" @@ -106,7 +121,9 @@ "const": "SENDIG-AR" }, "Version": { - "enum": ["1.0"] + "enum": [ + "1.0" + ] } }, "type": "object" @@ -117,7 +134,10 @@ "const": "SENDIG-DART" }, "Version": { - "enum": ["1.1", "1.2"] + "enum": [ + "1.1", + "1.2" + ] } }, "type": "object" @@ -128,7 +148,9 @@ "const": "SENDIG-GENETOX" }, "Version": { - "enum": ["1.0"] + "enum": [ + "1.0" + ] } }, "type": "object" diff --git a/resources/schema/rule/Operator.json b/resources/schema/rule/Operator.json index 021e79f89..21a28c21b 100644 --- a/resources/schema/rule/Operator.json +++ b/resources/schema/rule/Operator.json @@ -246,6 +246,16 @@ "required": ["operator", "value"], "type": "object" }, + { + "properties": { "operator": { "const": "is_consecutive_ordered_set" } }, + "required": ["operator", "value"], + "type": "object" + }, + { + "properties": { "operator": { "const": "is_not_consecutive_ordered_set" } }, + "required": ["operator", "value"], + "type": "object" + }, { "properties": { "operator": { "const": "is_unique_relationship" } }, "required": ["operator", "value"], diff --git a/resources/schema/rule/Operator.md b/resources/schema/rule/Operator.md index b05e9a22b..e20c47b33 100644 --- a/resources/schema/rule/Operator.md +++ b/resources/schema/rule/Operator.md @@ -1113,6 +1113,38 @@ Check: - "--TESTCD" ``` +### is_consecutive_ordered_set + +True if the dataset rows are in ascending order of values within `name`, grouped by `value`, and there are no skips between successive sequence values. + +This extends `is_ordered_set` with continuity checking. + +Rules: +- Duplicates are allowed (e.g., 1, 1, 2, 3). +- The sequence may start at any integer. +- Null/empty/non-numeric values in `name` evaluate to false (strict behavior). + +```yaml +Check: + all: + - name: --SEQ + operator: is_consecutive_ordered_set + value: USUBJID +``` +```yaml +Check: + all: + - name: --SEQ + operator: is_consecutive_ordered_set + value: + - USUBJID + - "--TESTCD" +``` + +### is_not_consecutive_ordered_set + +Complement of `is_consecutive_ordered_set` + ### is_ordered_by True if the dataset rows are ordered by the values within `name`, given the ordering specified by `order` diff --git a/tests/unit/test_check_operators/test_value_set_checks.py b/tests/unit/test_check_operators/test_value_set_checks.py index 726c71528..029f7c634 100644 --- a/tests/unit/test_check_operators/test_value_set_checks.py +++ b/tests/unit/test_check_operators/test_value_set_checks.py @@ -218,6 +218,97 @@ def test_is_ordered_set_multiple_comparators(): ) +def test_is_consecutive_ordered_set(): + data = { + "GROUP": ["A", "A", "A", "B", "B", "B", "C", "C"], + "VALUE": [1, 2, 3, 1, 2, 4, 1, 1], + } + df = PandasDataset.from_dict(data) + result = DataframeType({"value": df}).is_consecutive_ordered_set( + {"target": "VALUE", "comparator": "GROUP"} + ) + pd.testing.assert_series_equal( + result, + pd.Series([True, True, True, True, True, False, True, True]), + check_names=False, + ) + + +@pytest.mark.parametrize("dataset_type", [PandasDataset, DaskDataset]) +def test_is_consecutive_ordered_set_strict_invalid_values(dataset_type): + data = { + "GROUP": ["A", "A", "A", "A", "A"], + "VALUE": [1, None, 2, "X", 3], + } + df = dataset_type.from_dict(data) + result = DataframeType({"value": df}).is_consecutive_ordered_set( + {"target": "VALUE", "comparator": "GROUP"} + ) + pd.testing.assert_series_equal( + result, pd.Series([True, False, True, False, True]), check_names=False + ) + + +@pytest.mark.parametrize("dataset_type", [PandasDataset, DaskDataset]) +def test_is_not_consecutive_ordered_set(dataset_type): + data = { + "GROUP": ["A", "A", "A"], + "VALUE": [1, 2, 4], + } + df = dataset_type.from_dict(data) + result = DataframeType({"value": df}).is_not_consecutive_ordered_set( + {"target": "VALUE", "comparator": "GROUP"} + ) + pd.testing.assert_series_equal( + result, pd.Series([False, False, True]), check_names=False + ) + + +@pytest.mark.parametrize("dataset_type", [PandasDataset, DaskDataset]) +def test_is_consecutive_ordered_set_unordered_fails(dataset_type): + data = { + "GROUP": ["A", "A", "A"], + "VALUE": [1, 3, 2], + } + df = dataset_type.from_dict(data) + result = DataframeType({"value": df}).is_consecutive_ordered_set( + {"target": "VALUE", "comparator": "GROUP"} + ) + pd.testing.assert_series_equal( + result, pd.Series([True, False, False]), check_names=False + ) + + +@pytest.mark.parametrize("dataset_type", [PandasDataset, DaskDataset]) +def test_is_consecutive_ordered_set_accepts_numeric_strings(dataset_type): + data = { + "GROUP": ["A", "A", "A"], + "VALUE": [1, "2", 3], + } + df = dataset_type.from_dict(data) + result = DataframeType({"value": df}).is_consecutive_ordered_set( + {"target": "VALUE", "comparator": "GROUP"} + ) + pd.testing.assert_series_equal( + result, pd.Series([True, True, True]), check_names=False + ) + + +@pytest.mark.parametrize("dataset_type", [PandasDataset, DaskDataset]) +def test_is_not_consecutive_ordered_set_accepts_numeric_strings(dataset_type): + data = { + "GROUP": ["A", "A", "A"], + "VALUE": [1, "2", 3], + } + df = dataset_type.from_dict(data) + result = DataframeType({"value": df}).is_not_consecutive_ordered_set( + {"target": "VALUE", "comparator": "GROUP"} + ) + pd.testing.assert_series_equal( + result, pd.Series([False, False, False]), check_names=False + ) + + @pytest.mark.parametrize( "target, comparator, dataset_type, expected_result", [ From 93693f172662dd7489d1e2b378f0e1ef35732576 Mon Sep 17 00:00:00 2001 From: "Niemes, Adam" Date: Wed, 5 Aug 2026 17:33:14 -0400 Subject: [PATCH 2/4] prettier update --- resources/schema/rule-merged/CORE-base.json | 150 ++---- resources/schema/rule-merged/Operations.json | 301 ++--------- resources/schema/rule-merged/Operator.json | 497 ++++-------------- .../rule-merged/Organization_CDISC.json | 112 +--- .../rule-merged/Organization_Custom.json | 53 +- .../schema/rule-merged/Organization_FDA.json | 38 +- 6 files changed, 230 insertions(+), 921 deletions(-) diff --git a/resources/schema/rule-merged/CORE-base.json b/resources/schema/rule-merged/CORE-base.json index adb800f8e..c7ce4f5e8 100644 --- a/resources/schema/rule-merged/CORE-base.json +++ b/resources/schema/rule-merged/CORE-base.json @@ -9,9 +9,7 @@ "$ref": "#/$defs/CheckItems" } }, - "required": [ - "all" - ], + "required": ["all"], "type": "object" }, { @@ -21,9 +19,7 @@ "$ref": "#/$defs/CheckItems" } }, - "required": [ - "any" - ], + "required": ["any"], "type": "object" }, { @@ -33,9 +29,7 @@ "$ref": "#/$defs/CheckItem" } }, - "required": [ - "not" - ], + "required": ["not"], "type": "object" } ] @@ -134,18 +128,13 @@ "$ref": "#/$defs/Domains" }, "include_split_datasets": { - "enum": [ - true - ] + "enum": [true] } }, "type": "object" }, "JoinType": { - "enum": [ - "inner", - "left" - ], + "enum": ["inner", "left"], "type": "string" }, "LeftRightKeys": { @@ -172,10 +161,7 @@ ] } }, - "required": [ - "Left", - "Right" - ], + "required": ["Left", "Right"], "type": "object" }, "EntityName": { @@ -216,13 +202,7 @@ "type": "string" }, "USDMPrimitiveType": { - "enum": [ - "string", - "integer", - "boolean", - "decimal", - "date" - ] + "enum": ["string", "integer", "boolean", "decimal", "date"] }, "VariableReference": { "anyOf": [ @@ -279,10 +259,7 @@ "type": "string" } }, - "required": [ - "Document", - "Cited Guidance" - ], + "required": ["Document", "Cited Guidance"], "type": "object" }, "type": "array" @@ -291,14 +268,10 @@ "additionalProperties": false, "anyOf": [ { - "required": [ - "Logical Expression" - ] + "required": ["Logical Expression"] }, { - "required": [ - "Plain Language Expression" - ] + "required": ["Plain Language Expression"] } ], "properties": { @@ -312,25 +285,18 @@ "type": "string" } }, - "required": [ - "Rule" - ], + "required": ["Rule"], "type": "object" }, "Plain Language Expression": { "type": "string" }, "Type": { - "enum": [ - "Failure", - "Success" - ], + "enum": ["Failure", "Success"], "type": "string" } }, - "required": [ - "Type" - ], + "required": ["Type"], "type": "object" }, "Origin": { @@ -344,18 +310,11 @@ "type": "string" }, "Relationship": { - "enum": [ - "Predecessor", - "Related", - "Successor" - ], + "enum": ["Predecessor", "Related", "Successor"], "type": "string" } }, - "required": [ - "Id", - "Relationship" - ], + "required": ["Id", "Relationship"], "type": "object" }, "type": "array" @@ -373,9 +332,7 @@ "type": "string" } }, - "required": [ - "Id" - ], + "required": ["Id"], "type": "object" }, "Validator Rule Message": { @@ -385,11 +342,7 @@ "type": "string" } }, - "required": [ - "Origin", - "Rule Identifier", - "Version" - ], + "required": ["Origin", "Rule Identifier", "Version"], "type": "object" }, "minItems": 1, @@ -402,11 +355,7 @@ "type": "string" } }, - "required": [ - "Name", - "References", - "Version" - ], + "required": ["Name", "References", "Version"], "type": "object" }, "minItems": 1, @@ -429,10 +378,7 @@ "$ref": "Organization_Custom.json" } ], - "required": [ - "Organization", - "Standards" - ], + "required": ["Organization", "Standards"], "type": "object" }, "minItems": 1, @@ -472,15 +418,10 @@ "const": "Published" } }, - "required": [ - "Id" - ] + "required": ["Id"] } ], - "required": [ - "Status", - "Version" - ], + "required": ["Status", "Version"], "type": "object" }, "Description": { @@ -535,9 +476,7 @@ "type": "string" } }, - "required": [ - "Name" - ], + "required": ["Name"], "type": "object" }, "minItems": 1, @@ -563,9 +502,7 @@ "type": "array" } }, - "required": [ - "Message" - ], + "required": ["Message"], "type": "object" }, "Rule Type": { @@ -583,9 +520,7 @@ "$ref": "#/$defs/Classes" } }, - "required": [ - "Include" - ], + "required": ["Include"], "type": "object" }, { @@ -595,9 +530,7 @@ "$ref": "#/$defs/Classes" } }, - "required": [ - "Exclude" - ], + "required": ["Exclude"], "type": "object" } ] @@ -611,9 +544,7 @@ "$ref": "#/$defs/DataStructures" } }, - "required": [ - "Include" - ], + "required": ["Include"], "type": "object" }, { @@ -623,9 +554,7 @@ "$ref": "#/$defs/DataStructures" } }, - "required": [ - "Exclude" - ], + "required": ["Exclude"], "type": "object" } ] @@ -687,14 +616,10 @@ }, "anyOf": [ { - "required": [ - "Exclude" - ] + "required": ["Exclude"] }, { - "required": [ - "Include" - ] + "required": ["Include"] } ], "type": "object" @@ -716,20 +641,13 @@ }, "oneOf": [ { - "required": [ - "Classes", - "Domains" - ] + "required": ["Classes", "Domains"] }, { - "required": [ - "Data Structures" - ] + "required": ["Data Structures"] }, { - "required": [ - "Entities" - ] + "required": ["Entities"] } ], "type": "object" @@ -764,9 +682,7 @@ } }, "then": { - "required": [ - "Grouping_Variables" - ] + "required": ["Grouping_Variables"] }, "type": "object" } diff --git a/resources/schema/rule-merged/Operations.json b/resources/schema/rule-merged/Operations.json index 0eef6a6b3..8c03f1c5a 100644 --- a/resources/schema/rule-merged/Operations.json +++ b/resources/schema/rule-merged/Operations.json @@ -10,10 +10,7 @@ "markdownDescription": "\nReturns a Series indicating whether a specified codelist is extensible. Used in conjunction with codelist_terms to determine if values outside the codelist are acceptable. From the above example, $extensible will contain a bool if the codelist PKUDUG is extensible in all rows of the column.\n\nIf ct_package_type, version, and codelist_code parameters are provided, it will instead attach a new column containing the extensible value for each combination provided in the source dataset.\n\nFor example, given the current dataset:\n\n```\nid \tcodeSystemVersion \t$codelist_code\n1 \t2024-09-27 \tC201264\n2 \t2024-09-27 \tC201265\n3 \t2023-03-29 \tC127262\n```\n\nand the following operation:\n\n```yaml\n- id: $codelist_extensible\n operator: codelist_extensible\n ct_package_type: DDF\n version: codeSystemVersion\n codelist_code: $codelist_code\n```\n\nThis will result in the following dataset:\n\n```\nid \tcodeSystemVersion \t$codelist_code \t$codelist_extensible\n1 \t2024-09-27 \tC201264 \tfalse\n2 \t2024-09-27 \tC201265 \tfalse\n3 \t2023-03-29 \tC127262 \ttrue\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -23,10 +20,7 @@ "markdownDescription": "\nReturns a list of valid codelist/term values. Used for evaluating whether NCI codes, submission values or NCI preferred terms are valid based on controlled terminology. Expects three parameters: `codelists` which is a list of the codelist submission value(s) to retrieve, `level` which is the level of data (either \"codelist\" or \"term\") at which to return data from, and `returntype` which is the type of values to return: \"code\" for NCI Code(s), \"value\" for submission value(s), or \"pref_term\" for NCI preferred term(s).\n\n```yaml\n- Check:\n - all:\n - name: PPSTRESU\n operator: is_not_contained_by\n value: $terms\n - name: $extensible\n operator: equal_to\n value: true\n- Operations:\n - id: $terms\n operator: codelist_terms\n codelists:\n - PKUDUG\n level: term\n returntype: value\n - id: $extensible\n codelist: PKUDUG\n operator: codelist_extensible\n```\n\nIf `ct_package_type`, `version`, and `codelist_code` parameters are provided, it will instead attach a new column containing the term for each combination provided in the source dataset. If a column name is provided as:\n\n- `term_code`, it will find term information using the term codes in the specified column.\n- `term_value`, it will find term information using the term submission values in the specified column.\n- `term_pref_term`, it will find term information using the term preferred terms in the specified column.\n\nOnly one of `term_code`, `term_value` or `term_pref_term` can be provided. The term information returned will depend on the value of the `returntype` parameter, as described above. If `returntype` is not specified, specifying `term_code` will return the term submission value and specifying either `term_value` or `term_pref_term` will return the term code.\n\nFor example, given the current dataset:\n\n```\nid \tcodeSystemVersion $codelist_code code decode\n1 \t2024-09-27 C201264 C201356 After\n2 \t2024-09-27 C201265 C201352 End to End\n3 \t2023-03-29 C127262 C51282 CLINIC\n```\n\nand the following operations:\n\n```yaml\n- id: $found_term_value\n operator: codelist_terms\n ct_package_type: DDF\n version: codeSystemVersion\n codelist_code: $codelist_code\n term_code: code\n- id: $found_term_pref_term\n operator: codelist_terms\n ct_package_type: DDF\n version: codeSystemVersion\n codelist_code: $codelist_code\n term_code: code\n returntype: pref_term\n```\n\nThis will result in the following dataset:\n\n```\n id codeSystemVersion $codelist_code code decode $found_term_value $found_term_pref_term\n 1 2024-09-27 C201264 C201356 After After After Timing Type\n 2 2024-09-27 C201265 C201352 End to End End to End End to End\n 3 2023-03-31 C127262 C51282 CLINIC CLINIC Clinic\n```\n\nConversely, if given the same dataset, and the following operations:\n\n```yaml\n- id: $found_term_code1\n operator: codelist_terms\n ct_package_type: DDF\n version: codeSystemVersion\n codelist_code: $codelist_code\n term_value: decode\n- id: $found_term_code2\n operator: codelist_terms\n ct_package_type: DDF\n version: codeSystemVersion\n codelist_code: $codelist_code\n term_pref_term: decode\n```\n\nThis will result in the following dataset:\n\n```\n id codeSystemVersion $codelist_code code decode $found_term_code1 $found_term_code2\n 1 2024-09-27 C201264 C201356 After C201356\n 2 2024-09-27 C201265 C201352 End to End C201352 C201352\n 3 2023-03-31 C127262 C51282 CLINIC C51282 C51282\n```\n\nNote that `$found_term_code2` is:\n\n- `null` for the first record because \"After\" does not match any NCI preferred term in the C201264 codelist.\n- populated for the third record because matching is case-insensitive (i.e., \"CLINIC\" matches \"Clinic\").\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -36,11 +30,7 @@ "markdownDescription": "\nReturns a list of valid extensible codelist term's submission values. Used for evaluating whether submission values are valid based on controlled terminology. Expects the parameter codelists which is a list of the codelist submission value(s) to retrieve. If the codelist argument is [\"All\"] will return all extensible terms for the CT in a list.\n\n```yaml\n{\n \"id\": \"$ext_value\",\n \"codelist\": [\"ALL\"],\n \"operator\": \"define_extensible_codelists\",\n}\n```\n" } }, - "required": [ - "id", - "operator", - "codelists" - ], + "required": ["id", "operator", "codelists"], "type": "object" }, { @@ -50,11 +40,7 @@ "markdownDescription": "\nIf a target variable name is specified, returns the specified metadata in the define for the specified target variable.\n\nInput\n\n```yaml\n- operator: define_variable_metadata\n attribute_name: define_variable_label\n name: LBTESTCD\n id: $LBTESTCD_VARIABLE_LABEL\n```\n\nOutput\n\n```\nLaboratory Test Code\n```\n\nIf no target variable name specified, returns a dictionary containing the specified metadata in the define for all variables.\n\nInput\n\n```yaml\n- operator: define_variable_metadata\n attribute_name: define_variable_label\n id: $VARIABLE_LABEL\n```\n\nOutput\n\n```\n{\n \"STUDYID\": \"Study Identifier\",\n \"USUBJID\": \"Unique Subject Identifier\",\n \"LBTESTCD\": \"Laboratory Test Code\",\n \"...\": \"...\"\n}\n```\n" } }, - "required": [ - "id", - "operator", - "attribute_name" - ], + "required": ["id", "operator", "attribute_name"], "type": "object" }, { @@ -64,11 +50,7 @@ "markdownDescription": "\nGet a distinct list of values for the given name.\n\nIf a group list is specified, the distinct value list will be grouped by the variables within group.\nIf a filter object is provided, only values for records that match the filter criteria are included in the distinct values.\nIf `value_is_reference` is set to true, the target column contains the names of other columns, and the operation will check the referenced columns to ensure they exist in the associated dataset before adding them to the distinct list.\n\nIf group is provided, group_aliases may also be provided to assign new grouping variable names so that results grouped by the values in one set of grouping variables can be merged onto a dataset according to the same grouping value(s) stored in different set of grouping variables. When both group and group_aliases are provided, columns are renamed according to corresponding list position (i.e., the 1st column in group is renamed to the 1st column in group_aliases, etc.). If there are more columns listed in group than in group_aliases, only the group columns with corresponding group_aliases columns will be renamed. If there are more columns listed in group_aliases than in group, the extra column names in group_aliases will be ignored. See record_count for an example of the use of group_aliases.\n\n```yaml\nCheck:\n all:\n - name: SSSTRESC\n operator: equal_to\n value: DEAD\n value_is_literal: true\n - name: $ds_dsdecod\n operator: does_not_contain\n value: DEATH\n value_is_literal: true\nOperations:\n - operator: distinct\n domain: DS\n name: DSDECOD\n id: $ds_dsdecod\n group:\n - USUBJID\n filter:\n CAT: \"CATEGORY 1\"\n SCAT: \"SUBCATEGORY A\"\n```\n\n> below, `IDVAR` contains column names, the operation retrieves the value from each column for that row, checks the dataset associated with that column using the CO RDOMAIN. Columns that exist are added to the returns the distinct set.\n\n```yaml\nOperations:\n - domain: CO\n id: $rdomain_variables\n name: IDVAR\n operator: distinct\n value_is_reference: true\n```\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -78,10 +60,7 @@ "markdownDescription": "\nChecks whether the domain is in the set of domains within the provided standard.\n\nInput\n\nTarget Domain: XY\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\nOperations:\n - operator: domain_is_custom\n id: $domain_is_custom\n```\n\nOutput\n\n```\ntrue\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -91,10 +70,7 @@ "markdownDescription": "\nChecks whether the related domain (for example, the parent domain of a SUPP or RELREC dataset) is not present in the set of standard domains for the provided standard and version. This is useful for determining whether relationships point to non-standard or custom domains.\n\nInput\n\nTarget Domain: SUPPEX\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\nOperations:\n - operator: related_domain_is_custom\n id: $related_domain_is_custom\n```\n\nOutput\n\n```\ntrue\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -104,10 +80,7 @@ "markdownDescription": "\nReturns the label for the domain the operation is executing on within the provided standard.\n\nInput.\n\nTarget Domain: LB\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\nOperations:\n - operator: domain_label\n id: $domain_label\n```\n\nOutput\n\n```\nLaboratory Test Results\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -117,11 +90,7 @@ "markdownDescription": "\nCalculates the number of days between the DTC and RFSTDTC. The Study Day value is incremented by 1 for each date following RFSTDTC. Dates prior to RFSTDTC are decreased by 1, with the date preceding RFSTDTC designated as Study Day -1 (there is no Study Day 0). All Study Day values are integers. Thus, to calculate Study Day:\n\n- --DY = (date portion of --DTC) - (date portion of RFSTDTC) + 1 if --DTC is on or after RFSTDTC\n- --DY = (date portion of --DTC) - (date portion of RFSTDTC) if --DTC precedes RFSTDTC\n\nThis algorithm should be used across all domains.\n\n```yaml\nCheck:\n all:\n - name: --DY\n operator: non_empty\n - name: --DTC\n operator: is_complete_date\n - name: RFSTDTC\n operator: is_complete_date\n - name: --DY\n operator: not_equal_to\n value: $dy\nOperations:\n - name: --DTC\n operator: dy\n id: $dy\nMatch Datasets:\n - Name: DM\n Keys:\n - USUBJID\n```\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -131,11 +100,7 @@ "markdownDescription": "\nReturns the requested dataset level metadata value for the current dataset. Possible name values are:\n\n- dataset_size\n- dataset_location\n- dataset_name\n- dataset_label\n- domain\n- is_ap\n- ap_suffix\n\nExample\n\nInput:\n\nTarget domain: LB\n\n```yaml\n- name: dataset_label\n operator: extract_metadata\n id: $dataset_label\n```\n\nOutput:\n\n```\nLaboratory Test Results\n```\n\nExample: ap_suffix\n\nExtracts the domain suffix (characters 3-4) from AP-related domains. For example, \"FA\" from \"APFA\" DOMAIN value.\n\nInput:\n\nTarget domain: APFA\n\n```yaml\n- name: ap_suffix\n operator: extract_metadata\n id: $ap_suffix\n```\n\nOutput:\n\n```\nFA\n```\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -145,10 +110,7 @@ "markdownDescription": "\nReturns the expected (\"Core\" = Exp ) variables for the domain in the current standard Variable Metadata for custom domains will pull from the model while non-custom domains will be from the IG and Model.\n\nInput:\n\nTarget Domain: LB\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\n- operator: expected_variables\n id: $expected_variables\n```\n\nOutput:\n\n```\n[\"LBCAT\", \"LBORRES\", \"LBORRESU\", \"...\"]\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -158,13 +120,7 @@ "markdownDescription": "\nFetches controlled terminology attribute values from CT packages based on row-specific CT package and version references. The operation constructs CT package names based on the standard being validated and the values in the `name` and `version` columns (e.g., SDTMIG \u2192 \"sdtmct-{version}\"). When the `name` column contains \"CDISC\" or \"CDISC CT\", it uses the validation run's standard to determine the package prefix and the version found in the cell of the specified column. The operation extracts all codes matching the specified ct_attribute from the package.\n\n**Required Parameters:**\n\n- `ct_attribute`: Attribute to extract - `\"Term CCODE\"`, `\"Codelist CCODE\"`, `\"Term Value\"`, `\"Codelist Value\"`, or `\"Term Preferred Term\"`\n- `name`: Column containing CT reference (e.g., \"TSVCDREF\") - identifies which terminology system is referenced\n- `version`: Column containing CT version (e.g., \"TSVCDVER\")\n\n```yaml\n- id: $VALID_TERM_CODES\n name: TSVCDREF\n operator: get_codelist_attributes\n ct_attribute: Term CCODE\n version: TSVCDVER\n```\n\n**Note:** if using this operator with excel data, you must put the ctpackage versions contained within your data in the library tab for it work properly.\n" } }, - "required": [ - "id", - "operator", - "name", - "ct_attribute", - "version" - ], + "required": ["id", "operator", "name", "ct_attribute", "version"], "type": "object" }, { @@ -174,10 +130,7 @@ "markdownDescription": "\nReturns list of dataset columns in order\n\n```yaml\nCheck:\n all:\n - name: $column_order_from_dataset\n operator: is_not_ordered_by\n value: $column_order_from_library\nOperations:\n - id: $column_order_from_library\n operator: get_column_order_from_library\n - id: $column_order_from_dataset\n operator: get_column_order_from_dataset\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -187,10 +140,7 @@ "markdownDescription": "\nFetches column order for a given domain from the CDISC library. The lists with column names are sorted in accordance to \"ordinal\" key of library metadata.\nOptionally Filters variables based on specified metadata criteria.\n\nRule Type: Variable Metadata Check\n\n```yaml\nCheck:\n all:\n - name: variable_name\n operator: is_not_contained_by\n value: $ig_variables\nOperations:\n - id: $ig_variables\n operator: get_column_order_from_library\n key_name: \"role\" # role, core, etc\n key_value: \"Exp\" # Timing, Req, Exp, Perm, etc\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -200,10 +150,7 @@ "markdownDescription": "\nReturns the list of domains for a given class from the CDISC Library Implementation Guide. This operation retrieves all domains that belong to a specified class (e.g., \"TRIAL DESIGN\", \"FINDINGS\", \"EVENTS\") based on the current standard and version. The operation uses the standard and version from the validation context as well as the optional `domain_class` parameter which is the name of the class to filter by (e.g., \"TRIAL DESIGN\", \"FINDINGS\", \"EVENTS\", \"INTERVENTIONS). NOTE: Class names are case-sensitive and should match the Library metadata format. If no `domain_class` parameter is provided, the operation returns all domains across all classes in the Implementation Guide:\n\n```yaml\n- operator: get_library_class_domains\n id: $trial_design_domains\n domain_class: \"TRIAL DESIGN\"\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -213,10 +160,7 @@ "markdownDescription": "\nFetches column order for a given model class from the CDISC library. The lists with column names are sorted in accordance to \"ordinal\" key of library metadata.\n\nRule Type: Variable Metadata Check\n\n```yaml\nCheck:\n all:\n - name: variable_name\n operator: is_not_contained_by\n value: $model_variables\nOperations:\n - id: $model_variables\n operator: get_model_column_order\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -226,12 +170,7 @@ "markdownDescription": "\nFetches variable level library model properties filtered by the provided key_name and key_value\n\nExample\n\nInput\n\nTarget Domain: LB\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\n- operator: get_model_filtered_variables\n id: $model_filtered_variables\n key_name: \"role\"\n key_value: \"Timing\"\n```\n\nOutput\n\n```\n[\"VISITNUM\", \"VISIT\", \"VISITDY\", \"TAETORD\", \"...\"]\n```\n" } }, - "required": [ - "id", - "operator", - "key_name", - "key_value" - ], + "required": ["id", "operator", "key_name", "key_value"], "type": "object" }, { @@ -241,10 +180,7 @@ "markdownDescription": "\nFetches column order for a given SUPP's parent model class from the CDISC library. The lists with column names are sorted in accordance to \"ordinal\" key of library metadata.\n\n```yaml\nCheck:\n all:\n - operator: is_not_contained_by\n value: $parent_model_variables\nOperations:\n - id: $parent_model_variables\n operator: get_parent_model_column_order\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -254,12 +190,7 @@ "markdownDescription": "\nFilters variables from the dataset based on specified metadata criteria. Returns a list of variable names that exist in the dataset and match the filter criteria.\n\n```yaml\n- operator: get_dataset_filtered_variables\n id: $timing_variables\n key_name: \"role\"\n key_value: \"Timing\"\n```\n" } }, - "required": [ - "id", - "operator", - "key_name", - "key_value" - ], + "required": ["id", "operator", "key_name", "key_value"], "type": "object" }, { @@ -269,11 +200,7 @@ "markdownDescription": "\nGenerates a dataframe where each record in the dataframe is the library ig variable metadata corresponding with the variable label found in the column provided in name. The metadata column names are prefixed with the string provided in `id`.\n\nInput\n\nTarget Dataset: SUPPLB\n\nProduct: sdtmig\n\nVersion: 3-4\n\nDataset:\n\n```\n{\n \"STUDYID\": [\"STUDY1\", \"STUDY1\", \"STUDY1\"],\n \"USUBJID\": [\"SUBJ1\", \"SUBJ1\", \"SUBJ1\"],\n \"QLABEL\": [\"Toxicity\", \"Viscosity\", \"Analysis Method\"]\n}\n```\n\nRule:\n\n```yaml\n- operator: label_referenced_variable_metadata\n id: $qlabel_referenced_variable_metadata\n name: \"QLABEL\"\n```\n\nOutput\n\n```\n{\n \"STUDYID\": [\"STUDY1\", \"STUDY1\", \"STUDY1\"],\n \"USUBJID\": [\"SUBJ1\", \"SUBJ1\", \"SUBJ1\"],\n \"QLABEL\": [\"Toxicity\", \"Viscosity\", \"Analysis Method\"],\n \"$qlabel_referenced_variable_metadata_name\": [\"LBTOX\", null, \"LBANMETH\"],\n \"$qlabel_referenced_variable_metadata_role\": [\n \"Variable Qualifier\",\n null,\n \"Record Qualifier\"\n ],\n \"$qlabel_referenced_variable_metadata_ordinal\": [44, null, 38],\n \"$qlabel_referenced_variable_metadata_core\": [\"Req\", \"Req\", \"Req\"],\n \"$qlabel_referenced_variable_metadata_label\": [\"Toxicity\", null, \"Analysis Method\"]\n}\n```\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -283,11 +210,7 @@ "markdownDescription": "\nAllows the creation of a lookup table to take the values from multiple input columns and map them to values in an output column. The map parameter contains a list of objects. Each dictionary contains column names as properties that match the column names in the source dataset and an output property that will be returned as a result.\n\nIf map has a single object and output is the only property specified on that object, this will function as a direct assignment.\n\nFor example, given the following current dataset:\n\n```\nid \tparent_entity \tparent_rel\n1 \tTiming \trelativeToFrom\n2 \tSomething \trelativeToFrom\n3 \tTiming \ttype\n```\n\nand the following operation:\n\n```yaml\nOperations:\n - id: $codelist_code\n operator: map\n map:\n - parent_entity: Timing\n parent_rel: type\n output: C201264\n - parent_entity: Timing\n parent_rel: relativeToFrom\n output: C201265\n```\n\nThis will result in the following dataset:\n\n```\nid \tparent_entity \tparent_rel \t$codelist_code\n1 \tTiming \trelativeToFrom \tC201265\n2 \tSomething \trelativeToFrom \tNone\n3 \tTiming \ttype \tC201264\n```\n\nThe following operation:\n\n```yaml\nOperations:\n - id: $codelist_code\n operator: map\n map:\n - output: C201264\n```\n\nWill result in the following dataset:\n\n```\nid \tparent_entity \tparent_rel \t$codelist_code\n1 \tTiming \trelativeToFrom \tC201264\n2 \tSomething \trelativeToFrom \tC201264\n3 \tTiming \ttype \tC201264\n```\n" } }, - "required": [ - "id", - "operator", - "map" - ], + "required": ["id", "operator", "map"], "type": "object" }, { @@ -297,11 +220,7 @@ "markdownDescription": "\nIf no group is provided, returns the max value in name. If group is provided, returns the max value in name, within each unique set of the grouping variables.\n\n```yaml\nCheck:\n all:\n - name: \"$max_age\"\n operator: \"greater_than\"\n value: \"MAXAGE\"\nOperations:\n - operator: \"max\"\n domain: \"DM\"\n name: \"AGE\"\n id: \"$max_age\"\n```\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -311,11 +230,7 @@ "markdownDescription": "\nIf no group is provided, returns the max date value in name. If group is provided, returns the max date value in name, within each unique set of the grouping variables.\n\n```yaml\nCheck:\n all:\n - name: USUBJID\n operator: is_contained_by\n value: $ex_usubjid\n - name: RFXENDTC\n operator: not_equal_to\n value: $max_ex_exstdtc\n - name: RFXENDTC\n operator: not_equal_to\n value: $max_ex_exendtc\nOperations:\n - operator: distinct\n domain: EX\n name: USUBJID\n id: $ex_usubjid\n - operator: max_date\n domain: EX\n name: EXSTDTC\n id: $max_ex_exstdtc\n group:\n - USUBJID\n - operator: max_date\n domain: EX\n name: EXENDTC\n id: $max_ex_exendtc\n group:\n - USUBJID\n```\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -325,11 +240,7 @@ "markdownDescription": "\nExample: AAGE > mean(DM.AGE), where AAGE is a fictitious NSV\n\n```yaml\nCheck:\n all:\n - name: \"AAGE\"\n operator: \"greater_than\"\n value: \"$average_age\"\nOperations:\n - operator: \"mean\"\n domain: \"DM\"\n name: \"AGE\"\n id: \"$average_age\"\n```\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -339,11 +250,7 @@ "markdownDescription": "\nIf no group is provided, returns the min value in name. If group is provided, returns the min value in name, within each unique set of the grouping variables.\n\n```yaml\nCheck:\n all:\n - name: \"$min_age\"\n operator: \"less_than\"\n value: \"MINAGE\"\nOperations:\n - operator: \"min\"\n domain: \"DM\"\n name: \"AGE\"\n id: \"$min_age\"\n```\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -353,11 +260,7 @@ "markdownDescription": "\nIf no group is provided, returns the min date value in name. If group is provided, returns the min date value in name, within each unique set of the grouping variables.\n\nExample: RFSTDTC is greater than min AE.AESTDTC for the current USUBJID\n\n```yaml\nCheck:\n all:\n - name: \"RFSTDTC\"\n operator: \"date_greater_than\"\n value: \"$ae_aestdtc\"\nOperations:\n - operator: \"min_date\"\n domain: \"AE\"\n name: \"AESTDTC\"\n id: \"$ae_aestdtc\"\n group:\n - USUBJID\n```\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -367,12 +270,7 @@ "markdownDescription": "\nComputes set difference: elements in `name` that are not in `subtract`. By default a standard [set difference]() semantics (A \u2216 B) is applied. Optional `order_insensitive` property allows to have the element order to be taken into consideration and only those `name` elements are removed which follow the same order as in `subtract` . Preserves order from the first list. Both `name` and `subtract` must reference other operation results (e.g., `$expected_variables`, `$dataset_variables`). When `subtract` is empty or missing, returns all elements from `name`. Can be computed and added to output variables to display missing elements in error results.\n\n```yaml\nOperations:\n - id: $expected_variables\n operator: expected_variables\n - id: $dataset_variables\n operator: get_column_order_from_dataset\n - id: $expected_minus_dataset\n name: $expected_variables\n operator: minus\n subtract: $dataset_variables\n order_insensitive: false\n```\n" } }, - "required": [ - "id", - "operator", - "name", - "subtract" - ], + "required": ["id", "operator", "name", "subtract"], "type": "object" }, { @@ -382,11 +280,7 @@ "markdownDescription": "\nGenerates a dataframe where each record in the dataframe is the library ig variable metadata corresponding with the variable name found in the column provided in name. The metadata column names are prefixed with the string provided in `id`.\n\nInput\n\nTarget Dataset: SUPPLB\n\nProduct: sdtmig\n\nVersion: 3-4\n\nDataset:\n\n```\n{\n \"STUDYID\": [\"STUDY1\", \"STUDY1\", \"STUDY1\"],\n \"USUBJID\": [\"SUBJ1\", \"SUBJ1\", \"SUBJ1\"],\n \"QNAM\": [\"Toxicity\", \"LBVISCOS\", \"Analysis Method\"]\n}\n```\n\nRule:\n\n```yaml\n- operator: name_referenced_variable_metadata\n id: $qnam_referenced_variable_metadata\n name: \"QNAM\"\n```\n\nOutput\n\n```\n{\n \"STUDYID\": [\"STUDY1\", \"STUDY1\", \"STUDY1\"],\n \"USUBJID\": [\"SUBJ1\", \"SUBJ1\", \"SUBJ1\"],\n \"QNAM\": [\"LBTOX\", \"LBVISCOS\", \"LBANMETH\"],\n \"$qnam_referenced_variable_metadata_name\": [\"LBTOX\", null, \"LBANMETH\"],\n \"$qnam_referenced_variable_metadata_role\": [\n \"Variable Qualifier\",\n null,\n \"Record Qualifier\"\n ],\n \"$qnam_referenced_variable_metadata_ordinal\": [44, null, 38],\n \"$qnam_referenced_variable_metadata_core\": [\"Req\", \"Req\", \"Req\"],\n \"$qnam_referenced_variable_metadata_label\": [\"Toxicity\", null, \"Analysis Method\"]\n}\n```\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -396,10 +290,7 @@ "markdownDescription": "\nReturns the permissible variables (\"Core\" = Perm ) for a given domain and standard Variable Metadata for custom domains will pull from the model while non-custom domains will be from the IG and Model.\n\nInput:\n\nTarget Domain: LB\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\n- operator: permissible_variables\n id: $permissible_variables\n```\n\nOutput:\n\n```\n[\"LBGRPID\", \"LBREFID\", \"LBSPID\", \"...\"]\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -409,10 +300,7 @@ "markdownDescription": "\nIf no filter or group is provided, returns the number of records in the dataset. If filter is provided, returns the number of records in the dataset that contain the value(s) in the corresponding column(s) provided in the filter. Filter can have a wildcard `&` that when added to the end of the filter value will look for all instances of that prefix (see 4th example below). If group is provided, returns the number of rows matching each unique set of the grouping variables. These can be static column name(s) or can be derived from other operations like get_dataset_filtered_variables.\n\nIf both filter and group are provided, returns the number of records in the dataset that contain the value(s) in the corresponding column(s) provided in the filter that also match each unique set of the grouping variables.\n\n**Wildcard Filtering:** Filter values ending with % will match any records where the column value starts with the specified prefix. For example, RACE% will match RACE1, RACE2, RACE3, etc. This is useful for matching related variables with numeric or alphabetic suffixes.\n\n**Regex Transformation:** If regex is provided along with group, the regex pattern will be applied to transform grouping column values before grouping. The regex is only applied to columns where the pattern matches the data type. For example, using regex `^\\d{4}-\\d{2}-\\d{2}` on a column containing `2022-01-14T08:00` will extract `2022-01-14` for grouping purposes.\n\nIf group is provided, group_aliases may also be provided to assign new grouping variable names so that results grouped by the values in one set of grouping variables can be merged onto a dataset according to the same grouping value(s) stored in different set of grouping variables. When both group and group_aliases are provided, columns are renamed according to corresponding list position (i.e., the 1st column in group is renamed to the 1st column in group_aliases, etc.). If there are more columns listed in group than in group_aliases, only the group columns with corresponding group_aliases columns will be renamed. If there are more columns listed in group_aliases than in group, the extra column names in group_aliases will be ignored.\n\nExample: return the number of records in a dataset.\n\n```yaml\n- operator: record_count\n id: $records_in_dataset\n```\n\nExample: return the number of records where STUDYID = \"CDISC01\" and FLAGVAR = \"Y\".\n\n```yaml\n- operator: record_count\n id: $flagged_cdisc01_records_in_dataset\n filter:\n STUDYID: \"CDISC01\"\n FLAGVAR: \"Y\"\n```\n\nExample: return the number of records grouped by USUBJID and timing variables, extracting only the date portion from datetime values.\n\n```yaml\n- operator: record_count\n id: $records_per_usubjid_date\n group:\n - USUBJID\n - --TESTCD\n - $TIMING_VARIABLES\n regex: \"^\\d{4}-\\d{2}-\\d{2}\"\n```\n\nExample: return the number of records where QNAM starts with \"RACE\" (matches RACE1, RACE2, RACE3, etc.) per USUBJID.\n\n```yaml\n- operator: record_count\n id: $race_records_in_dataset\n filter:\n QNAM: \"RACE&\"\n group:\n - \"USUBJID\"\n```\n\nExample: return the number of records grouped by USUBJID.\n\n```yaml\n- operator: record_count\n id: $records_per_usubjid\n group:\n - USUBJID\n```\n\nExample: return the number of records grouped by USUBJID where FLAGVAR = \"Y\".\n\n```yaml\n- operator: record_count\n id: $flagged_records_per_usubjid\n group:\n - USUBJID\n filter:\n FLAGVAR: \"Y\"\n```\n\nExample: return the number of records grouped by USUBJID and IDVARVAL where QNAM = \"TEST1\" and IDVAR = \"GROUPID\", renaming the IDVARVAL column to GROUPID for subsequent merging.\n\n```yaml\n- operator: record_count\n id: $test1_records_per_usubjid_groupid\n group:\n - USUBJID\n - IDVARVAL\n filter:\n QNAM: \"TEST1\"\n IDVAR: \"GROUPID\"\n group_aliases:\n - USUBJID\n - GROUPID\n```\n\nExample: Group the StudyIdentifier dataset by parent_id and merge the result back to the context dataset StudyVersion using StudyVersion.id == StudyIdentifier.parent_id\n\n```yaml\nScope:\n Entities:\n Include:\n - StudyVersion\nOperations:\n - domain: StudyIdentifier\n filter:\n parent_entity: \"StudyVersion\"\n parent_rel: \"studyIdentifiers\"\n rel_type: \"definition\"\n studyIdentifierScope.organizationType.code: \"C70793\"\n studyIdentifierScope.organizationType.codeSystem: \"http://www.cdisc.org\"\n group:\n - parent_id\n group_aliases:\n - id\n id: $num_sponsor_ids\n operator: record_count\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -422,10 +310,7 @@ "markdownDescription": "\nReturns the required variables ( \"Core\" = Req ) for a given domain and standard Variable Metadata for custom domains will pull from the model while non-custom domains will be from the IG and Model.\n\nInput:\n\nTarget Domain: LB\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\n- operator: required_variables\n id: $required_variables\n```\n\nOutput:\n\n```\n[\"STUDYID\", \"DOMAIN\", \"USUBJID\", \"LBSEQ\", \"LBTESTCD\", \"LBTEST\"]\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -435,12 +320,7 @@ "markdownDescription": "\nSplits a dataset column by a given delimiter\n\n```yaml\nOperations:\n - name: PPSPEC\n delimiter: ;\n id: $ppspec_value\n operator: split_by\n```\n" } }, - "required": [ - "id", - "operator", - "delimiter", - "name" - ], + "required": ["id", "operator", "delimiter", "name"], "type": "object" }, { @@ -450,10 +330,7 @@ "markdownDescription": "\nReturns a list of the domains in the study\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -463,10 +340,7 @@ "markdownDescription": "\nReturns a list of the submitted dataset filenames in all uppercase\n\nex. if TS.xpt, AE.xpt, EC.xpt, and SUPPEC.xpt are submitted -> [TS, AE, EC, SUPPEC] will be returned\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -476,10 +350,7 @@ "markdownDescription": "\nReturns a list of valid SDTM domain names from the standard metadata. This can be used to compare extracted suffixes from DOMAIN values or dataset names.\n\nInput\n\nProduct: sdtmig\n\nVersion: 3-4\n\n```yaml\nOperations:\n - operator: standard_domains\n id: $valid_domain_names\n```\n\nOutput\n\n```\n[\"AE\", \"CM\", \"DM\", \"FA\", \"LB\", \"QS\", ...]\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -489,10 +360,7 @@ "markdownDescription": "\nReturns the valid terminology package dates for a given standard.\n\nGiven a list of terminology packages:\n\n```\n[\n \"sdtmct-2023-10-26\",\n \"sdtmct-2023-12-13\",\n \"adamct-2023-12-13\",\n \"cdashct-2023-05-19\"\n]\n```\n\nand standard: sdtmig\n\nthe operation will return:\n\n```\n[\"2023-10-26\", \"2023-12-13\"]\n```\n\nBy default, the standard is as specified when running validation - as the validation runtime parameter and/or as specified in the rule header - and the list of terminology packages is obtained from the current cache. If required, the default standard may be overridden using the optional ct_package_types parameter. For example, given the same list of terminology packages, the following operation:\n\n```yaml\nOperations:\n - operator: valid_codelist_dates\n id: $valid_dates\n ct_package_types:\n - SDTM\n - CDASH\n```\n\nwill return:\n\n```\n[\"2023-05-19\", \"2023-10-26\", \"2023-12-13\"]\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -502,11 +370,7 @@ "markdownDescription": "\nReturns true if the version of an external dictionary provided in the define.xml file matches the version parsed from the dictionary files.\n\nInput:\n\n```yaml\nOperations:\n - operator: valid_define_external_dictionary_version\n id: $is_valid_loinc_version\n external_dictionary_type: loinc\n```\n\nOutput:\n\n```\n[true, true, true, true]\n```\n" } }, - "required": [ - "id", - "operator", - "external_dictionary_type" - ], + "required": ["id", "operator", "external_dictionary_type"], "type": "object" }, { @@ -564,10 +428,7 @@ "markdownDescription": "\nDetermines whether the values are valid in the following variables:\n\n- --SOCCD (System Organ Class Code)\n- --HLGTCD (High Level Group Term Code)\n- --HLTCD (High Level Term Code)\n- --PTCD (Preferred Term Code)\n- --LLTCD (Lowest Level Term Code)\n\nInput:\n\n```yaml\nOperations:\n - id: $is_valid_meddra_codes\n operator: valid_meddra_code_references\n```\n\nOutput:\n\n```\n[true, false, true, true]\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -577,10 +438,7 @@ "markdownDescription": "\nDetermines whether the values are valid in the following variable pairs:\n\n- --SOCCD, --SOC (System Organ Class Code and Term)\n- --HLGTCD, --HLGT (High Level Group Term Code and Term)\n- --HLTCD, --HLT (High Level Term Code and Term)\n- --PTCD, --DECOD (Preferred Term Code and Dictionary-Derived Term)\n- --LLTCD, --LLT (Lowest Level Term Code and Term)\n\nInput:\n\n```yaml\nOperations:\n - id: $is_valid_meddra_pairs\n operator: valid_meddra_code_term_pairs\n```\n\nOutput:\n\n```\n[true, true, false, true, true]\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -590,10 +448,7 @@ "markdownDescription": "\nDetermines whether the values are valid in the following variables:\n\n- --SOC (System Organ Class)\n- --HLGT (High Level Group Term)\n- --HLT (High Level Term)\n- --DECOD (Dictionary-Derived Term)\n- --LLT (Lowest Level Term)\n\nInput:\n\n```yaml\nOperations:\n - id: $is_valid_meddra_terms\n operator: valid_meddra_term_references\n```\n\nOutput:\n\n```\n[true, true, false, true, true]\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -603,11 +458,7 @@ "markdownDescription": "\nChecks if a reference to whodrug term in name points to the existing code in Atc Text (INA) file.\n\nInput:\n\n```yaml\nOperations:\n - id: $whodrug_refs_valid\n operator: valid_whodrug_references\n```\n\nOutput:\n\n```\n[true, false, true, true]\n```\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -617,10 +468,7 @@ "markdownDescription": "\nReturns a mapping of variable names to the number of times that variable appears in a domain within the study.\n\nInput\n\n```\n{\n \"AE\": [\"STUDYID\", \"DOMAIN\", \"USUBJID\", \"AETERM\", \"AEENDTC\"],\n \"LB\": [\"STUDYID\", \"DOMAIN\", \"USUBJID\", \"LBTESTCD\", \"LBENDTC\"]\n}\n```\n\nOutput\n\n```\n{\n \"STUDYID\": 2,\n \"DOMAIN\": 2,\n \"USUBJID\": 2,\n \"--TERM\": 1,\n \"--TESTCD\": 1,\n \"--ENDTC\": 2\n}\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -630,10 +478,7 @@ "markdownDescription": "\nOperation operates only on original submission datasets regardless of rule type. Flags an error if a column exists is in the submission dataset currently being evaluated.\n\nRule Type: Domain Presence Check\n\n```yaml\nCheck:\n all:\n - name: $MIDS_EXISTS\n operator: equal_to\n value: true\n - name: TM\n operator: not_exists\nOperations:\n - id: $MIDS_EXISTS\n name: MIDS\n operator: variable_exists\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -643,10 +488,7 @@ "markdownDescription": "\nReturns true if a variable is missing from the dataset or if all values within the variable are null or empty string. This operation first checks if the target variable exists in the dataset, and if it does exist, evaluates whether all its values are null or empty.\nThe operation supports two sources via the `source` parameter:\n\n- **`submission`** : checks against the raw submission dataset\n- **`evaluation`** (default): checks against the evaluation dataset built based on the rule type\n\n```yaml" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -656,10 +498,7 @@ "markdownDescription": "\nReturns the set of variable names from the library for the given standard. This operation extracts all variable names across all domains in the specified standard's library metadata.\n\nInput:\n\nValidation Standard: sdtmig\nValidation Version: 3-4\n\n```yaml\n- operator: variable_names\n id: $all_variables\n```\n\nOutput:\n\n```\n[\"STUDYID\", \"DOMAIN\", \"USUBJID\", \"SUBJID\", \"RFSTDTC\", \"RFENDTC\", \"SITEID\", \"AGE\", \"AGEU\", \"SEX\", \"RACE\", \"ETHNIC\", \"ARMCD\", \"ARM\", \"ACTARMCD\", \"ACTARM\", \"COUNTRY\", \"DMDTC\", \"DMDY\", \"AETERM\", \"AEDECOD\", \"AECAT\", \"AESCAT\", \"AEPRESP\", \"AEBODSYS\", \"AEBDSYCD\", \"AESOC\", \"AESOCCD\", \"AELLT\", \"AELLTCD\", \"AEHLT\", \"AEHLTCD\", \"AEHLGT\", \"AEHLGTCD\", \"AEPTCD\", \"AESTDTC\", \"AEENDTC\", \"AESTDY\", \"AEENDY\", \"AEDUR\", \"AESER\", \"AESEV\", \"AEACN\", \"AEREL\", \"AEOUT\", \"AESCAN\", \"AESCONG\", \"AESDISAB\", \"AESDTH\", \"AESHOSP\", \"AESLIFE\", \"AESOD\", \"AECONTRT\", \"AETOXGR\", \"LBTESTCD\", \"LBTEST\", \"LBCAT\", \"LBSCAT\", \"LBSPEC\", \"LBMETHOD\", \"LBORRES\", \"LBORRESU\", \"LBORNRLO\", \"LBORNRHI\", \"LBSTRESC\", \"LBSTRESN\", \"LBSTRESU\", \"LBSTNRLO\", \"LBSTNRHI\", \"LBNRIND\", \"LBNAM\", \"LBSPEC\", \"LBANTREG\", \"LBFAST\", \"LBDRVFL\", \"LBTOX\", \"LBTOXGR\", \"LBSTDTC\", \"LBENDTC\", \"LBSTDY\", \"LBENDY\", \"LBTPT\", \"LBTPTNUM\", \"LBELTM\", \"LBTPTREF\", \"LBRFTDTC\", \"...\"]\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -669,11 +508,7 @@ "markdownDescription": "\nGiven a variable name, returns a mapping of variable values to the number of times that value appears in the variable within all datasets in the study.\n" } }, - "required": [ - "id", - "operator", - "name" - ], + "required": ["id", "operator", "name"], "type": "object" }, { @@ -683,10 +518,7 @@ "markdownDescription": "\nDetermines whether the values are valid and in the correct hierarchical structure in the following variables:\n\n- --DECOD\n- --CLAS\n- --CLASCD\n\nInput:\n\n```yaml\nOperations:\n - id: $valid_whodrug_codes\n operator: whodrug_code_hierarchy\n```\n" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" }, { @@ -696,12 +528,7 @@ "markdownDescription": "\nValidates XHTML fragments in the target column against the specified namespace.\n\n```yaml\nOperations:\n - id: $xhtml_errors\n name: text\n operator: get_xhtml_errors\n namespace: http://www.cdisc.org/ns/usdm/xhtml/v1.0\n```\n\nNote that a local XSD file is required for validation. The file must be stored in the folder indicated by the value of the `LOCAL_XSD_FILE_DIR` default file path and the mapping between the namespace and the local XSD file's `sub-folder/name` must be included in the value of the `LOCAL_XSD_FILE_MAP` default file path.\n" } }, - "required": [ - "id", - "operator", - "name", - "namespace" - ], + "required": ["id", "operator", "name", "namespace"], "type": "object" } ], @@ -764,13 +591,7 @@ "type": "string" }, "dictionary_term_type": { - "enum": [ - "LLT", - "PT", - "HLT", - "HLGT", - "SOC" - ] + "enum": ["LLT", "PT", "HLT", "HLGT", "SOC"] }, "domain": { "anyOf": [ @@ -786,9 +607,7 @@ ] }, "external_dictionary_type": { - "enum": [ - "meddra" - ] + "enum": ["meddra"] }, "filter": { "type": "object" @@ -833,10 +652,7 @@ }, "level": { "type": "string", - "enum": [ - "codelist", - "term" - ] + "enum": ["codelist", "term"] }, "map": { "type": "array", @@ -847,9 +663,7 @@ "type": "string" } }, - "required": [ - "output" - ] + "required": ["output"] } }, "name": { @@ -866,11 +680,7 @@ }, "returntype": { "type": "string", - "enum": [ - "code", - "value", - "pref_term" - ] + "enum": ["code", "value", "pref_term"] }, "source": { "type": "string" @@ -897,9 +707,6 @@ "type": "string" } }, - "required": [ - "id", - "operator" - ], + "required": ["id", "operator"], "type": "object" } diff --git a/resources/schema/rule-merged/Operator.json b/resources/schema/rule-merged/Operator.json index b1dfc4773..36af07b4a 100644 --- a/resources/schema/rule-merged/Operator.json +++ b/resources/schema/rule-merged/Operator.json @@ -9,9 +9,7 @@ "const": "additional_columns_empty" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -20,9 +18,7 @@ "const": "additional_columns_not_empty" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -32,10 +28,7 @@ "markdownDescription": "\nWill return True if the value in `value` is contained within the collection/iterable in the target column, or if there's an exact match for non-iterable data.\n\nThe operator checks if every value in a column is a list or set. If yes, it compares row-by-row. If any value is blank or a different type (like a string or number), it compares each value against the entire column instead.\n\nExample:\n\n```yaml\n- name: \"--TOXGR\" # Column containing lists like ['GRADE', 'SEVERITY', 'ONSET']\n operator: \"contains\"\n value: \"GRADE\" # True if 'GRADE' is an element in the list\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -45,9 +38,7 @@ "markdownDescription": "\nTrue if all values in `value` are contained within the variable `name`.\n\n> All of ('Screen Failure', 'Not Assigned', 'Not Treated', 'Unplanned Treatment') in ACTARM\n\n```yaml\n- name: \"ACTARM\"\n operator: \"contains_all\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n - \"Not Treated\"\n - \"Unplanned Treatment\"\n```\n\nThe operator also supports lists:\n\n```yaml\n- name: \"$spec_codelist\"\n operator: \"contains_all\"\n value: \"$ppspec_value\"\n```\n\nWhere:\n\n| $spec_codelist | $ppspec_value |\n| :-------------------------- | :----------------: |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE1\", \"CODE2\"] |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE2\", \"CODE3\"] |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE1\"] |\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -57,10 +48,7 @@ "markdownDescription": "\nTrue if the value in `value` is contained within the collection/iterable in the target column, performing case-insensitive comparison.\n\nExample:\n\n```yaml\n- name: \"--TOXGR\" # Column containing lists like ['Grade', 'Severity', 'Onset']\n operator: \"contains_case_insensitive\"\n value: \"grade\" # True if 'Grade'/'GRADE'/'grade' exists in the list\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -70,10 +58,7 @@ "markdownDescription": "\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified.\n\nThe `date_component` parameter accepts: `\"year\"`, `\"month\"`, `\"day\"`, `\"hour\"`, `\"minute\"`, `\"second\"`, `\"microsecond\"`, or `\"auto\"`.\n\nWhen `date_component: \"auto\"` is used, the operator automatically detects the precision of both dates and compares at the common (less precise) level.\n\n```yaml\n- name: \"AESTDTC\"\n operator: \"date_equal_to\"\n value: \"RFSTDTC\"\n date_component: \"auto\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -83,10 +68,7 @@ "markdownDescription": "\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified. Supports `date_component: \"auto\"`.\n\n> Year part of BRTHDTC > 2021\n\n```yaml\n- name: \"BRTHDTC\"\n operator: \"date_greater_than\"\n date_component: \"year\"\n value: \"2021\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -96,10 +78,7 @@ "markdownDescription": "\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified. Supports `date_component: \"auto\"`.\n\n> Year part of BRTHDTC >= 2021\n\n```yaml\n- name: \"BRTHDTC\"\n operator: \"date_greater_than_or_equal_to\"\n date_component: \"year\"\n value: \"2021\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -109,10 +88,7 @@ "markdownDescription": "\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified. Supports `date_component: \"auto\"`.\n\n> AEENDTC < AESTDTC\n\n```yaml\n- name: \"AEENDTC\"\n operator: \"date_less_than\"\n value: \"AESTDTC\"\n```\n\n> SSDTC < all DS.DSSTDTC when SSSTRESC = \"DEAD\"\n\n```yaml\nCheck:\n all:\n - name: \"SSSTRESC\"\n operator: \"equal_to\"\n value: \"DEAD\"\n - name: \"SSDTC\"\n operator: \"date_less_than\"\n value: \"$max_ds_dsstdtc\"\nOperations:\n - operator: \"max_date\"\n domain: \"DS\"\n name: \"DSSTDTC\"\n id: \"$max_ds_dsstdtc\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -122,10 +98,7 @@ "markdownDescription": "\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified. Supports `date_component: \"auto\"`.\n\n> AEENDTC <= AESTDTC\n\n```yaml\n- name: \"AEENDTC\"\n operator: \"date_less_than_or_equal_to\"\n value: \"AESTDTC\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -135,10 +108,7 @@ "markdownDescription": "\nComplement of `date_equal_to`\n\nDate comparison. Compare `name` to `value`. Compares partial dates if `date_component` is specified. Supports `date_component: \"auto\"`.\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -148,10 +118,7 @@ "markdownDescription": "\nComplement of `contains`. Returns True when the value is NOT contained within the target collection.\n\n```yaml\n- name: \"--TOXGR\"\n operator: \"does_not_contain\"\n value: \"GRADE\" # True if 'GRADE' is NOT an element in the list\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -161,10 +128,7 @@ "markdownDescription": "\nComplement of `contains_case_insensitive`. Returns True when the value is NOT contained within the target collection (case-insensitive).\n\nExample:\n\n```yaml\n- name: \"--TOXGR\"\n operator: \"does_not_contain_case_insensitive\"\n value: \"grade\" # True if no case variation of 'grade' exists in the list\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -177,11 +141,7 @@ "type": "boolean" } }, - "required": [ - "operator", - "value", - "regex" - ], + "required": ["operator", "value", "regex"], "type": "object" }, { @@ -191,12 +151,7 @@ "markdownDescription": "\nComplement of `has_next_corresponding_record`\n" } }, - "required": [ - "operator", - "ordering", - "value", - "within" - ], + "required": ["operator", "ordering", "value", "within"], "type": "object" }, { @@ -206,9 +161,7 @@ "markdownDescription": "\nValue presence\n\n> --OCCUR = null\n\n```yaml\n- name: --OCCUR\n operator: empty\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -218,10 +171,7 @@ "markdownDescription": "\n> SEENDTC is not empty when it is not the last record, grouped by USUBJID, sorted by SESTDTC\n\n```yaml\n- name: SEENDTC\n operator: empty_within_except_last_row\n ordering: SESTDTC\n value: USUBJID\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -231,10 +181,7 @@ "markdownDescription": "\nSubstring matching\n\n> DOMAIN ending with 'FOOBAR'\n\n```yaml\n- name: \"DOMAIN\"\n operator: \"ends_with\"\n value: \"FOOBAR\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -253,10 +200,7 @@ "type": "boolean" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -275,10 +219,7 @@ "type": "boolean" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -291,11 +232,7 @@ "type": "boolean" } }, - "required": [ - "operator", - "value", - "regex" - ], + "required": ["operator", "value", "regex"], "type": "object" }, { @@ -305,9 +242,7 @@ "markdownDescription": "\nTrue if the column exists in the current dataframe. (Works for datasets and variables)\n\n> --OCCUR is present in dataset\n\n```yaml\n- name: \"--OCCUR\"\n operator: \"exists\"\n```\n\n> Domain SJ exists\n\n```yaml\nRule Type: Domain Presence Check\nCheck:\n all:\n - name: \"SJ\"\n operator: \"exists\"\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -317,10 +252,7 @@ "markdownDescription": "\nValue comparison\n\n> TSVAL > 0\n\n```yaml\n- name: TSVAL\n operator: greater_than\n value: 0\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -330,10 +262,7 @@ "markdownDescription": "\nValue comparison\n\n> TSVAL >= 0\n\n```yaml\n- name: TSVAL\n operator: greater_than_or_equal_to\n value: 1\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -343,9 +272,7 @@ "markdownDescription": "\nComplement of `has_same_values`\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -355,9 +282,7 @@ "markdownDescription": "\nLength comparison\n\n> Check whether variable values has equal length of another variable.\n\n```yaml\n- name: SEENDTC\n operator: has_equal_length\n value: SESTDTC\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -367,12 +292,7 @@ "markdownDescription": "\nEnsures that a value of a variable `name` in one record is equal to the value of another variable `value` in the next corresponding record. The rows are grouped by `within` and ordered by `ordering`.\n\n> SEENDTC is equal to the SESTDTC of the next record within a USUBJID. Ordered by SESEQ\n\n```yaml\n- name: SEENDTC\n operator: has_next_corresponding_record\n value: SESTDTC\n within: USUBJID\n ordering: SESEQ\n```\n" } }, - "required": [ - "operator", - "ordering", - "value", - "within" - ], + "required": ["operator", "ordering", "value", "within"], "type": "object" }, { @@ -382,9 +302,7 @@ "markdownDescription": "\nComplement of `has_equal_length`\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -394,9 +312,7 @@ "markdownDescription": "\nTrue if all values in `name` are the same\n\n> Condition: MHCAT ^= null\n> Rule: MHCAT ^= the same value for all records\n\n```yaml\nCheck:\n all:\n - name: MHCAT\n operator: non_empty\n - name: MHCAT\n operator: has_same_values\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -406,10 +322,7 @@ "markdownDescription": "\nDuration ISO-8601 check, returns True if a duration is not in ISO-8601 format. The negative parameter must be specified to indicate if negative durations are either allowed (True) or disallowed (False)\n\n> DURVAR is invalid (negative durations disallowed)\n\n```yaml\n- name: \"DURVAR\"\n operator: \"invalid_duration\"\n negative: False\n```\n" } }, - "required": [ - "operator", - "negative" - ], + "required": ["operator", "negative"], "type": "object" }, { @@ -419,9 +332,7 @@ "markdownDescription": "\nThe operator performs date validation against complete and partial dates with uncertainty in the following order:\n\n1. Attempts to parse using [dateutil.parser.isoparse()](https://dateutil.readthedocs.io/en/stable/parser.html)\n2. If parsing fails and the string contains uncertainty indicators (`/`, `--`, `-:`), validates against an extended ISO 8601 dates regex pattern\n3. If parsing succeeds, dates are still validated against the regex pattern.\n\n```yaml\n- name: \"BRTHDTC\"\n operator: \"invalid_date\"\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -431,9 +342,7 @@ "markdownDescription": "\nDate check\n\n> DM.RFSTDTC = complete date\n\n```yaml\n- name: \"RFSTDTC\"\n operator: \"is_complete_date\"\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -443,10 +352,7 @@ "markdownDescription": "\nValue in `name` compared against a list in `value`. The list can have literal values or be a reference to a `$variable`.\n\nThis operator behaves similarly to `contains`. The key distinction: `contains` checks if comparator \u2208 target, while `is_contained_by` checks if target \u2208 comparator.\n\n> ACTARM in ('Screen Failure', 'Not Assigned', 'Not Treated', 'Unplanned Treatment')\n\n```yaml\n- name: \"ACTARM\"\n operator: \"is_contained_by\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n - \"Not Treated\"\n - \"Unplanned Treatment\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -456,10 +362,7 @@ "markdownDescription": "\nValue in `name` case insensitive compared against a list in `value`. The list can have literal values or be a reference to a `$variable`.\n\n> ACTARM in ('Screen Failure', 'Not Assigned', 'Not Treated', 'Unplanned Treatment')\n\n```yaml\n- name: \"ACTARM\"\n operator: \"is_contained_by_case_insensitive\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n - \"Not Treated\"\n - \"Unplanned Treatment\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -469,9 +372,7 @@ "markdownDescription": "\nComplement of `is_complete_date`\n\nDate check\n\n> DM.RFSTDTC ^= complete date\n\n```yaml\n- name: \"RFSTDTC\"\n operator: \"is_incomplete_date\"\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -481,10 +382,7 @@ "markdownDescription": "\nComplement of `is_contained_by`\n\n> ARM not in ('Screen Failure', 'Not Assigned')\n\n```yaml\n- name: \"ARM\"\n operator: \"is_not_contained_by\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -494,10 +392,7 @@ "markdownDescription": "\nComplement of `is_contained_by_case_insensitive`\n\n> ARM not in ('Screen Failure', 'Not Assigned')\n\n```yaml\n- name: \"ARM\"\n operator: \"is_not_contained_by_case_insensitive\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -507,10 +402,7 @@ "markdownDescription": "\nComplement of `is_ordered_by`\n" } }, - "required": [ - "operator", - "order" - ], + "required": ["operator", "order"], "type": "object" }, { @@ -519,10 +411,7 @@ "const": "is_not_ordered_set" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -532,10 +421,7 @@ "markdownDescription": "\nComplement of `is_unique_relationship`\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -545,9 +431,7 @@ "markdownDescription": "\nComplement of `is_unique_set`.\n\n> --SEQ is not unique within DOMAIN, USUBJID, and --TESTCD\n\n```yaml\n- name: \"--SEQ\"\n operator: is_not_unique_set\n value:\n - \"DOMAIN\"\n - \"USUBJID\"\n - \"--TESTCD\"\n```\n\n> `name` can be a variable containing a list of columns and `value` does not need to be present\n\n```yaml\nRule Type: Dataset Contents Check against Define XML\nCheck:\n all:\n - name: define_dataset_key_sequence # contains list of dataset key columns\n operator: is_not_unique_set\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -557,10 +441,7 @@ "markdownDescription": "\nTrue if the dataset rows are ordered by the values within `name`, given the ordering specified by `order`\n\n```yaml\nCheck:\n all:\n - name: --SEQ\n operator: is_ordered_by\n order: asc\n```\n" } }, - "required": [ - "operator", - "order" - ], + "required": ["operator", "order"], "type": "object" }, { @@ -570,10 +451,7 @@ "markdownDescription": "\nTrue if the dataset rows are in ascending order of the values within `name`, grouped by the values within `value`. Value can either be a single column or multiple.\n\n```yaml\nCheck:\n all:\n - name: --SEQ\n operator: is_ordered_set\n value: USUBJID\n```\n\n```yaml\nCheck:\n all:\n - name: --SEQ\n operator: is_ordered_set\n value:\n - USUBJID\n - \"--TESTCD\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -583,10 +461,7 @@ "markdownDescription": "\nTrue if the dataset rows are in ascending order of values within `name`, grouped by `value`, and there are no skips between successive sequence values.\n\nThis extends `is_ordered_set` with continuity checking.\n\nRules:\n- Duplicates are allowed (e.g., 1, 1, 2, 3).\n- The sequence may start at any integer.\n- Null/empty/non-numeric values in `name` evaluate to false (strict behavior).\n\n```yaml\nCheck:\n all:\n - name: --SEQ\n operator: is_consecutive_ordered_set\n value: USUBJID\n```\n```yaml\nCheck:\n all:\n - name: --SEQ\n operator: is_consecutive_ordered_set\n value:\n - USUBJID\n - \"--TESTCD\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -596,10 +471,7 @@ "markdownDescription": "\nComplement of `is_consecutive_ordered_set`\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -609,10 +481,7 @@ "markdownDescription": "\nRelationship Integrity Check looking for a 1-1 relationship between name and value. Ensures uniqueness of both name and value.\n\n> AETERM and AEDECOD has a 1-to-1 relationship\n\n```yaml\n- name: AETERM\n operator: is_unique_relationship\n value: AEDECOD\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -622,10 +491,7 @@ "markdownDescription": "\nChecks if a variable maintains consistent values within groups defined by one or more grouping variables. Groups records by specified value(s) and validates that the target variable maintains the same value within each unique combination of grouping variables. When inconsistency is detected within a group, the operator attempts to identify a majority value. If one value appears more frequently than all others, only the minority records (those not matching the majority value) are flagged. If no single majority exists \u2014 i.e., two or more values are tied for the highest frequency \u2014 all records in that group are flagged.\n\nSingle grouping variable - true if the values of BGSTRESU differ within USUBJID:\n\nIf a regex parameter is provided, it is applied to the values of the target variable before the consistency check. The first capture group of the regex is used as the normalized value for comparison. This can be useful when only part of the value should be considered during comparison (for example, comparing only the date portion of a datetime value).\n\n- regex is optional.\n- The pattern must include at least one capture group(or whole regex will be wrapped to capture group).\n- Only the first capture group is used for comparison.\n- If the pattern does not match a value, the original value is used.\n\n```yaml\n- name: \"BGSTRESU\"\n operator: is_inconsistent_across_dataset\n value: \"USUBJID\"\n```\n\nMultiple grouping variables - true if the values of --STRESU differ within each combination of --TESTCD, --CAT, --SCAT, --SPEC, and --METHOD:\n\n```yaml\n- name: \"--STRESU\"\n operator: is_inconsistent_across_dataset\n value:\n - \"--TESTCD\"\n - \"--CAT\"\n - \"--SCAT\"\n - \"--SPEC\"\n - \"--METHOD\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -635,9 +501,7 @@ "markdownDescription": "\nRelationship Integrity Check\n\n> --SEQ is unique within DOMAIN, USUBJID, and --TESTCD\n\n```yaml\n- name: \"--SEQ\"\n operator: is_unique_set\n value:\n - \"DOMAIN\"\n - \"USUBJID\"\n - \"--TESTCD\"\n```\n\n> `name` can be a variable containing a list of columns and `value` does not need to be present\n\n> The `regex` parameter allows you to extract portions of values using a regex pattern before checking uniqueness.\n\n> Compare date only (YYYY-MM-DD) for uniqueness\n\n```yaml\n- name: \"--REPNUM\"\n operator: is_not_unique_set\n value:\n - \"USUBJID\"\n - \"--TESTCD\"\n - \"$TIMING_VARIABLES\"\n regex: '^\\d{4}-\\d{2}-\\d{2}'\n```\n\n> Compare by first N characters of a string\n\n```yaml\n- name: \"ITEM_ID\"\n operator: is_not_unique_set\n value:\n - \"USUBJID\"\n - \"CATEGORY\"\n regex: \"^.{2}\"\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -647,10 +511,7 @@ "markdownDescription": "\nValue comparison\n\n> TSVAL < 1\n\n```yaml\n- name: TSVAL\n operator: less_than\n value: 1\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -660,10 +521,7 @@ "markdownDescription": "\nValue comparison\n\n> TSVAL <= 1\n\n```yaml\n- name: TSVAL\n operator: less_than_or_equal_to\n value: 1\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -673,10 +531,7 @@ "markdownDescription": "\nLength comparison\n\n> SETCD value length > 8\n\n```yaml\n- name: \"SETCD\"\n operator: \"longer_than\"\n value: 8\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -686,10 +541,7 @@ "markdownDescription": "\nLength comparison\n\n> TSVAL value length >= 201\n\n```yaml\n- name: \"TSVAL\"\n operator: \"longer_than_or_equal_to\"\n value: 201\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -699,10 +551,7 @@ "markdownDescription": "\nRegular Expression value matching\n\n- Determine if each string starts with a match of a regular expression. Refer to this pandas documentation: https://pandas.pydata.org/docs/reference/api/pandas.Series.str.match.html\n- To \"search\" for a regex within the entire text, prefix the regex with `.*` and do not use anchors `^` , `$`\n- To do a \"fullmatch\" of a regex with the entire text, suffix the regex with an anchor `$` and do not prefix the regex with `.*`\n- For syntax guide, refer to this Python documentation: [Regular Expression HOWTO](https://docs.python.org/3/howto/regex.html).\n- Suggestion for an on-line regular expression logic. tester: https://regex101.com, choose the Python dialect.\n- For regex token visualization, try https://www.debuggex.com.\n\n> --DOSTXT value is non-numeric\n\n```yaml\n- name: --DOSTXT\n operator: matches_regex\n value: ^\\d*\\.?\\d*$\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -712,9 +561,7 @@ "markdownDescription": "\nComplement of `empty`\n\n> --OCCUR ^= null\n\n```yaml\n- name: --OCCUR\n operator: non_empty\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -724,10 +571,7 @@ "markdownDescription": "\nComplement of `empty_within_except_last_row`\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -737,9 +581,7 @@ "markdownDescription": "\nComplement of `contains_all`\n\n> All of ('Screen Failure', 'Not Assigned', 'Not Treated', 'Unplanned Treatment') not in ACTARM\n\n```yaml\n- name: \"ACTARM\"\n operator: \"not_contains_all\"\n value:\n - \"Screen Failure\"\n - \"Not Assigned\"\n - \"Not Treated\"\n - \"Unplanned Treatment\"\n```\n\nThe operator also supports lists:\n\n```yaml\n- name: \"$spec_codelist\"\n operator: \"not_contains_all\"\n value: \"$ppspec_value\"\n```\n\nWhere:\n\n| $spec_codelist | $ppspec_value |\n| :-------------------------- | :----------------: |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE1\", \"CODE2\"] |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE2\", \"CODE3\"] |\n| [\"CODE1\", \"CODE2\", \"CODE3\"] | [\"CODE1\"] |\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -758,10 +600,7 @@ "type": "boolean" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -780,10 +619,7 @@ "type": "boolean" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -793,9 +629,7 @@ "markdownDescription": "\nComplement of `exists`\n\n> AEOCCUR not present in dataset\n\n```yaml\n- name: \"AEOCCUR\"\n operator: \"not_exists\"\n```\n\n> Domain SJ does not exist\n\n```yaml\nRule Type: Domain Presence Check\nCheck:\n all:\n - name: \"SJ\"\n operator: \"not_exists\"\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -805,10 +639,7 @@ "markdownDescription": "\nComplement of `matches_regex`\n\n> --TESTCD <= 8 chars and contains only letters, numbers, and underscores and can not start with a number\n\n```yaml\n- name: --TESTCD\n operator: not_matches_regex\n value: ^[A-Z_][A-Z0-9_]{0,7}$\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -818,11 +649,7 @@ "markdownDescription": "\nComplement of `prefix_matches_regex`\n" } }, - "required": [ - "operator", - "prefix", - "value" - ], + "required": ["operator", "prefix", "value"], "type": "object" }, { @@ -832,10 +659,7 @@ "markdownDescription": "\nComplement of `present_on_multiple_rows_within`\n\n```yaml\n- operator: \"not_present_on_multiple_rows_within\"\n name: \"RELID\"\n value: 4 (optional)\n within: \"USUBJID\"\n```\n" } }, - "required": [ - "operator", - "within" - ], + "required": ["operator", "within"], "type": "object" }, { @@ -845,11 +669,7 @@ "markdownDescription": "\nComplement of `suffix_matches_regex`\n\n> QNAM does not end with numbers\n\n```yaml\n- name: \"QNAM\"\n operator: \"not_suffix_matches_regex\"\n suffix: 2\n value: \"\\d\\d\"\n```\n" } }, - "required": [ - "operator", - "suffix", - "value" - ], + "required": ["operator", "suffix", "value"], "type": "object" }, { @@ -859,11 +679,7 @@ "markdownDescription": "\nTrue if the `prefix` number of characters beginning a string in `name` match one of the strings in the list in `value`\n\n> Check if a variable's domain identifier exists in the study\n\n```yaml\n- name: variable_name\n operator: prefix_is_contained_by\n prefix: 2\n value: $study_domains\n```\n" } }, - "required": [ - "operator", - "prefix", - "value" - ], + "required": ["operator", "prefix", "value"], "type": "object" }, { @@ -873,11 +689,7 @@ "markdownDescription": "\nTrue if the `prefix` number of characters beginning a string in `name` match the string in `value`\n\n```yaml\n- name: dataset_name\n operator: prefix_equal_to\n prefix: 2\n value: DOMAIN\n```\n" } }, - "required": [ - "operator", - "prefix", - "value" - ], + "required": ["operator", "prefix", "value"], "type": "object" }, { @@ -887,11 +699,7 @@ "markdownDescription": "\nComplement of `prefix_is_contained_by`\n" } }, - "required": [ - "operator", - "prefix", - "value" - ], + "required": ["operator", "prefix", "value"], "type": "object" }, { @@ -901,11 +709,7 @@ "markdownDescription": "\nTrue if the `prefix` number of characters beginning a string in `name` match a regular expression in `value`\n\n```yaml\n- name: DOMAIN\n operator: prefix_matches_regex\n prefix: 2\n value: (AP|ap)\n```\n" } }, - "required": [ - "operator", - "prefix", - "value" - ], + "required": ["operator", "prefix", "value"], "type": "object" }, { @@ -915,11 +719,7 @@ "markdownDescription": "\nComplement of `prefix_equal_to`\n" } }, - "required": [ - "operator", - "prefix", - "value" - ], + "required": ["operator", "prefix", "value"], "type": "object" }, { @@ -929,10 +729,7 @@ "markdownDescription": "\nTrue if the same value of `name` is present on multiple rows, grouped by `within`. A maximum allowed number of occurrences can be specified in the value attribute. In this instance the value: 4 means that an error will be flagged if the same value appears more than 4 times within a USUBJID. By default the operator will flag any time a value appears more than once.\n\n```yaml\n- operator: \"present_on_multiple_rows_within\"\n name: \"RELID\"\n value: 4 (optional)\n within: \"USUBJID\"\n```\n" } }, - "required": [ - "operator", - "within" - ], + "required": ["operator", "within"], "type": "object" }, { @@ -942,10 +739,7 @@ "markdownDescription": "\nWill raise an issue if at least one of the values in `name` is the same as one of the values in `value`. See [shares_no_elements_with](#shares_no_elements_with).\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -955,10 +749,7 @@ "markdownDescription": "\nWill raise an issue if exactly one of the values in `name` is the same as one of the values in `value`. See [shares_no_elements_with](#shares_no_elements_with).\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -968,10 +759,7 @@ "markdownDescription": "\nWill raise an issue if the values in `name` do not share any of the values in `value`\n\n> Check if $dataset_variables shares no elements with $timing_variables\n\n```yaml\nRule Type: Dataset Metadata Check # One record per dataset\nCheck:\n - all:\n name: $dataset_variables\n operator: shares_no_elements_with\n value: $timing_variables\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -981,10 +769,7 @@ "markdownDescription": "\nLength comparison\n\n> SETCD value length < 9\n\n```yaml\n- name: \"SETCD\"\n operator: \"shorter_than\"\n value: 9\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -994,10 +779,7 @@ "markdownDescription": "\nLength comparison\n\n> TSVAL value length <= 200\n\n```yaml\n- name: \"TSVAL\"\n operator: \"shorter_than_or_equal_to\"\n value: 201\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -1007,9 +789,7 @@ "markdownDescription": "\nSplits a string by a separator and checks if both parts have equal length. Generic operator for validating paired data formats where both parts must have the same level of detail or precision.\n\nParameters:\n\n- `separator`: The delimiter to split on (default: \"/\")\n\n> Check that string parts separated by a delimiter have equal length\n\n```yaml\n- name: --DTC\n operator: split_parts_have_equal_length\n separator: \"/\"\n```\n\nUse cases:\n\n- **Date/time intervals**: `2003-12-15T10:00/2003-12-15T10:30` \u2192 True (both 16 characters)\n- **Date ranges**: `2003-12-01/2003-12-10` \u2192 True (both 10 characters)\n- **Version ranges**: `1.2.3/2.0.0` \u2192 True (both 5 characters)\n- **Product codes**: `ABC-123/XYZ-789` \u2192 True (both 7 characters)\n\nInvalid example:\n\n- `2003-12-15T10:00/2003-12-15T10:30:15` \u2192 False (16 vs 19 characters - different precision)\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -1019,9 +799,7 @@ "markdownDescription": "\nComplement of `split_parts_have_equal_length`. Returns True when parts have unequal lengths (indicates a violation).\n\n```yaml\n- name: --DTC\n operator: split_parts_have_unequal_length\n separator: \"/\"\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -1031,10 +809,7 @@ "markdownDescription": "\nSubstring matching\n\n> DOMAIN beginning with 'AP'\n\n```yaml\n- name: \"DOMAIN\"\n operator: \"starts_with\"\n value: \"AP\"\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -1044,11 +819,7 @@ "markdownDescription": "\nTrue if the `suffix` number of characters ending a string in `name` match the string in `value`\n\n```yaml\n- name: dataset_name\n operator: suffix_equal_to\n prefix: 2\n value: DOMAIN\n```\n" } }, - "required": [ - "operator", - "suffix", - "value" - ], + "required": ["operator", "suffix", "value"], "type": "object" }, { @@ -1058,11 +829,7 @@ "markdownDescription": "\nTrue if the `suffix` number of characters ending a string in `name` match one of the strings in the list in `value`\n\n> Check if a supp's parent domain exists in the study\n\n```yaml\n- name: dataset_name\n operator: suffix_is_contained_by\n prefix: 2\n value: $study_domains\n```\n" } }, - "required": [ - "operator", - "suffix", - "value" - ], + "required": ["operator", "suffix", "value"], "type": "object" }, { @@ -1072,11 +839,7 @@ "markdownDescription": "\nComplement of `suffix_is_contained_by`\n" } }, - "required": [ - "operator", - "suffix", - "value" - ], + "required": ["operator", "suffix", "value"], "type": "object" }, { @@ -1086,11 +849,7 @@ "markdownDescription": "\nTrue if the `suffix` number of characters ending a string in `name` match a regular expression in `value`\n\n> QNAM ends with numbers\n\n```yaml\n- name: \"QNAM\"\n operator: \"suffix_matches_regex\"\n suffix: 2\n value: \"\\d\\d\"\n```\n" } }, - "required": [ - "operator", - "suffix", - "value" - ], + "required": ["operator", "suffix", "value"], "type": "object" }, { @@ -1100,11 +859,7 @@ "markdownDescription": "\nComplement of `suffix_equal_to`\n" } }, - "required": [ - "operator", - "suffix", - "value" - ], + "required": ["operator", "suffix", "value"], "type": "object" }, { @@ -1114,11 +869,7 @@ "markdownDescription": "\nComplement of `target_is_sorted_by`\n" } }, - "required": [ - "operator", - "value", - "within" - ], + "required": ["operator", "value", "within"], "type": "object" }, { @@ -1128,11 +879,7 @@ "markdownDescription": "\nTrue if the values in name are ordered according to the values specified by value\nin ascending/descending order, grouped by the values in within. Each value entry\nrequires a variable name, a sort_order of asc or desc, and an optional\nnull_position of first or last (defaults to last) which controls where null/empty\ncomparator values are placed in the expected ordering. Within accepts either a\nsingle column or an ordered list of columns. Columns can be either number or Char\nDates in ISO8601 YYYY-MM-DD format. Date value(s) with different precisions that\noverlap (e.g. 2005-10, 2005-10-3 and 2005-10-08) are all flagged as not sorted as\ntheir order cannot be inferred.\n\nOptionally supports a `regex` parameter that extracts a portion of the target\nvalue for sorting. The regex must contain at least one capturing group. The first\ncaptured group is extracted and converted to numeric if possible, allowing proper\nsorting of sequence numbers (e.g., \"MIDS1\", \"MIDS2\", ..., \"MIDS10\" with regex\n`.*?(\\\\d+)$`). This is particularly useful for variables that end with sequence\nnumbers that may or may not be zero-padded.\n\n```yaml\nCheck:\n all:\n - name: --SEQ\n within:\n - USUBJID\n - MIDSTYPE\n operator: target_is_sorted_by\n value:\n - name: --STDTC\n sort_order: asc\n null_position: last\n```\n\nExample with regex for extracting sequence numbers:\n\n```yaml\nCheck:\n all:\n - name: MIDS\n operator: target_is_sorted_by\n regex: \".*?(\\\\d+)$\" # Extract trailing digits, convert to numeric\n value:\n - name: SMSTDTC\n sort_order: asc\n within:\n - USUBJID\n - MIDSTYPE\n```\n" } }, - "required": [ - "operator", - "value", - "within" - ], + "required": ["operator", "value", "within"], "type": "object" }, { @@ -1142,10 +889,7 @@ "markdownDescription": "\nComplement of `value_has_multiple_references`\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -1155,10 +899,7 @@ "markdownDescription": "\nTrue if the value in `name` has more than one count in the dictionary defined in `value`\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -1168,10 +909,7 @@ "markdownDescription": "\nChecks for inconsistencies in enumerated columns of a DataFrame. Starting with the smallest/largest enumeration of the given variable, returns True if VARIABLE(N+1) is populated but VARIABLE(N) is not populated. Repeats for all variables belonging to the enumeration. Note that the initial variable will not have an index (VARIABLE) and the next enumerated variable has index 1 (VARIABLE1).\n\nex: Check if there are inconsistencies in the TSVAL columns (TSVAL, TSVAL1, TSVAL2, etc.)\n\n```yaml\nCheck:\n all:\n - name: \"TSVAL\"\n operator: \"inconsistent_enumerated_columns\"\n```\n" } }, - "required": [ - "operator", - "name" - ], + "required": ["operator", "name"], "type": "object" }, { @@ -1181,10 +919,7 @@ "markdownDescription": "\nChecks if elements in the target list appear in the same relative order in the comparator list.\n\n> Check if dataset column order is a correctly ordered subset of library column order\n\n```yaml\n- name: $column_order_from_dataset\n operator: is_ordered_subset_of\n value: $column_order_from_library\n```\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -1194,10 +929,7 @@ "markdownDescription": "\nComplement of `is_ordered_subset_of`\n" } }, - "required": [ - "operator", - "value" - ], + "required": ["operator", "value"], "type": "object" }, { @@ -1207,9 +939,7 @@ "markdownDescription": "\nValidates 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. \nNOTE: The titlecase library may produce false positives or false negatives in syntactic edge cases (e.g. hyphenated words, slash-separated terms, uncommon prepositions).\n\n> Check that AELABEL values are in proper title case\n\n```yaml\n- name: AELABEL\n operator: is_title_case\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" }, { @@ -1219,18 +949,13 @@ "markdownDescription": "\nComplement of `is_title_case`. Returns True when values are NOT in proper title case.\n\n> Flag AELABEL values that violate title case rules\n\n```yaml\n- name: AELABEL\n operator: is_not_title_case\n```\n" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" } ], "properties": { "comparator": { - "type": [ - "number", - "string" - ] + "type": ["number", "string"] }, "context": { "type": "string" @@ -1268,27 +993,18 @@ "type": "boolean" }, "codelistcheck": { - "enum": [ - "code", - "value" - ], + "enum": ["code", "value"], "type": "string" }, "codelistlevel": { - "enum": [ - "term", - "codelist" - ], + "enum": ["term", "codelist"], "type": "string" }, "operator": { "type": "string" }, "order": { - "enum": [ - "asc", - "dsc" - ], + "enum": ["asc", "dsc"], "type": "string" }, "ordering": { @@ -1306,25 +1022,17 @@ "value": { "oneOf": [ { - "type": [ - "boolean", - "number", - "string" - ] + "type": ["boolean", "number", "string"] }, { "items": { - "type": [ - "number" - ] + "type": ["number"] }, "type": "array" }, { "items": { - "type": [ - "string" - ] + "type": ["string"] }, "type": "array" }, @@ -1335,10 +1043,7 @@ "$ref": "Operator.json#/properties/name" }, "null_position": { - "enum": [ - "first", - "last" - ], + "enum": ["first", "last"], "type": "string" }, "order": { @@ -1381,8 +1086,6 @@ "type": "string" } }, - "required": [ - "operator" - ], + "required": ["operator"], "type": "object" } diff --git a/resources/schema/rule-merged/Organization_CDISC.json b/resources/schema/rule-merged/Organization_CDISC.json index 9aaef8a76..db1041921 100644 --- a/resources/schema/rule-merged/Organization_CDISC.json +++ b/resources/schema/rule-merged/Organization_CDISC.json @@ -22,9 +22,7 @@ "const": "Failure" } }, - "required": [ - "Type" - ], + "required": ["Type"], "type": "object" }, "Origin": { @@ -40,9 +38,7 @@ "type": "object" }, "Version": { - "enum": [ - "5.0" - ] + "enum": ["5.0"] } }, "type": "object" @@ -51,12 +47,7 @@ "type": "array" }, "Version": { - "enum": [ - "1.0", - "1.1", - "1.2", - "1.3" - ] + "enum": ["1.0", "1.1", "1.2", "1.3"] } }, "type": "object" @@ -75,9 +66,7 @@ "const": "Success" } }, - "required": [ - "Type" - ], + "required": ["Type"], "type": "object" }, "Origin": { @@ -90,19 +79,13 @@ "type": "string" }, "Version": { - "enum": [ - "1", - "2", - "3" - ] + "enum": ["1", "2", "3"] } }, "type": "object" }, "Version": { - "enum": [ - "2.0" - ] + "enum": ["2.0"] } }, "type": "object" @@ -111,11 +94,7 @@ "type": "array" }, "Version": { - "enum": [ - "3.2", - "3.3", - "3.4" - ] + "enum": ["3.2", "3.3", "3.4"] } }, "type": "object" @@ -134,9 +113,7 @@ "const": "Success" } }, - "required": [ - "Type" - ], + "required": ["Type"], "type": "object" }, "Origin": { @@ -152,9 +129,7 @@ "type": "object" }, "Version": { - "enum": [ - "5.0" - ] + "enum": ["5.0"] } }, "type": "object" @@ -163,11 +138,7 @@ "type": "array" }, "Version": { - "enum": [ - "3.0", - "3.1", - "3.1.1" - ] + "enum": ["3.0", "3.1", "3.1.1"] } }, "type": "object" @@ -186,9 +157,7 @@ "const": "Success" } }, - "required": [ - "Type" - ], + "required": ["Type"], "type": "object" }, "Origin": { @@ -204,9 +173,7 @@ "type": "object" }, "Version": { - "enum": [ - "5.0" - ] + "enum": ["5.0"] } }, "type": "object" @@ -215,10 +182,7 @@ "type": "array" }, "Version": { - "enum": [ - "1.1", - "1.2" - ] + "enum": ["1.1", "1.2"] } }, "type": "object" @@ -237,9 +201,7 @@ "const": "Success" } }, - "required": [ - "Type" - ], + "required": ["Type"], "type": "object" }, "Origin": { @@ -255,9 +217,7 @@ "type": "object" }, "Version": { - "enum": [ - "5.0" - ] + "enum": ["5.0"] } }, "type": "object" @@ -266,9 +226,7 @@ "type": "array" }, "Version": { - "enum": [ - "1.0" - ] + "enum": ["1.0"] } }, "type": "object" @@ -287,9 +245,7 @@ "const": "Success" } }, - "required": [ - "Type" - ], + "required": ["Type"], "type": "object" }, "Origin": { @@ -305,9 +261,7 @@ "type": "object" }, "Version": { - "enum": [ - "1.0" - ] + "enum": ["1.0"] } }, "type": "object" @@ -316,24 +270,13 @@ "type": "array" }, "Version": { - "enum": [ - "1.0" - ] + "enum": ["1.0"] }, "Substandard": { - "enum": [ - "SDTM", - "SEND", - "ADaM", - "CDASH" - ] + "enum": ["SDTM", "SEND", "ADaM", "CDASH"] } }, - "required": [ - "Name", - "Version", - "Substandard" - ], + "required": ["Name", "Version", "Substandard"], "type": "object" }, { @@ -354,17 +297,13 @@ "type": "string" }, "Version": { - "enum": [ - "1" - ] + "enum": ["1"] } }, "type": "object" }, "Version": { - "enum": [ - "1.0" - ] + "enum": ["1.0"] } }, "type": "object" @@ -373,10 +312,7 @@ "type": "array" }, "Version": { - "enum": [ - "3.0", - "4.0" - ] + "enum": ["3.0", "4.0"] } }, "type": "object" diff --git a/resources/schema/rule-merged/Organization_Custom.json b/resources/schema/rule-merged/Organization_Custom.json index c5f591394..bf5bd7276 100644 --- a/resources/schema/rule-merged/Organization_Custom.json +++ b/resources/schema/rule-merged/Organization_Custom.json @@ -9,10 +9,7 @@ "type": "string", "description": "Name of your custom organization", "not": { - "enum": [ - "CDISC", - "FDA" - ] + "enum": ["CDISC", "FDA"] } }, "Standards": { @@ -48,9 +45,7 @@ "description": "Version of the rule" } }, - "required": [ - "Id" - ], + "required": ["Id"], "type": "object" }, "Version": { @@ -60,10 +55,7 @@ "Criteria": { "properties": { "Type": { - "enum": [ - "Failure", - "Success" - ], + "enum": ["Failure", "Success"], "type": "string" }, "Plain Language Expression": { @@ -78,46 +70,30 @@ "type": "string" } }, - "required": [ - "Rule" - ], + "required": ["Rule"], "type": "object" } }, - "required": [ - "Type" - ], + "required": ["Type"], "anyOf": [ { - "required": [ - "Logical Expression" - ] + "required": ["Logical Expression"] }, { - "required": [ - "Plain Language Expression" - ] + "required": ["Plain Language Expression"] } ], "type": "object" } }, - "required": [ - "Origin", - "Rule Identifier", - "Version" - ], + "required": ["Origin", "Rule Identifier", "Version"], "type": "object" }, "minItems": 1, "type": "array" } }, - "required": [ - "Name", - "References", - "Version" - ], + "required": ["Name", "References", "Version"], "type": "object" }, "minItems": 1, @@ -165,10 +141,7 @@ }, "OutputType": { "type": "string", - "enum": [ - "Check", - "Listing" - ], + "enum": ["Check", "Listing"], "description": "Output type of the rule validation result" }, "Keywords": { @@ -182,11 +155,7 @@ "additionalProperties": true } }, - "required": [ - "Organization", - "Standards", - "Category" - ], + "required": ["Organization", "Standards", "Category"], "type": "object", "$defs": { "metadata": { diff --git a/resources/schema/rule-merged/Organization_FDA.json b/resources/schema/rule-merged/Organization_FDA.json index 94af54bc4..b0f7de783 100644 --- a/resources/schema/rule-merged/Organization_FDA.json +++ b/resources/schema/rule-merged/Organization_FDA.json @@ -41,10 +41,7 @@ } } ], - "required": [ - "Document", - "Cited Guidance" - ], + "required": ["Document", "Cited Guidance"], "type": "object" }, "type": "array" @@ -55,9 +52,7 @@ "const": "Success" } }, - "required": [ - "Type" - ], + "required": ["Type"], "type": "object" }, "Origin": { @@ -73,9 +68,7 @@ "type": "object" }, "Version": { - "enum": [ - "1.5" - ] + "enum": ["1.5"] } }, "type": "object" @@ -91,11 +84,7 @@ "const": "SDTMIG" }, "Version": { - "enum": [ - "3.2", - "3.3", - "3.4" - ] + "enum": ["3.2", "3.3", "3.4"] } }, "type": "object" @@ -106,11 +95,7 @@ "const": "SENDIG" }, "Version": { - "enum": [ - "3.0", - "3.1", - "3.1.1" - ] + "enum": ["3.0", "3.1", "3.1.1"] } }, "type": "object" @@ -121,9 +106,7 @@ "const": "SENDIG-AR" }, "Version": { - "enum": [ - "1.0" - ] + "enum": ["1.0"] } }, "type": "object" @@ -134,10 +117,7 @@ "const": "SENDIG-DART" }, "Version": { - "enum": [ - "1.1", - "1.2" - ] + "enum": ["1.1", "1.2"] } }, "type": "object" @@ -148,9 +128,7 @@ "const": "SENDIG-GENETOX" }, "Version": { - "enum": [ - "1.0" - ] + "enum": ["1.0"] } }, "type": "object" From afe442a72eaadec980d5e921801647ee43043590 Mon Sep 17 00:00:00 2001 From: "Niemes, Adam" Date: Thu, 6 Aug 2026 08:42:29 -0400 Subject: [PATCH 3/4] fixed whitespace --- cdisc_rules_engine/check_operators/dataframe_operators.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cdisc_rules_engine/check_operators/dataframe_operators.py b/cdisc_rules_engine/check_operators/dataframe_operators.py index 70404f39e..b014af5e7 100644 --- a/cdisc_rules_engine/check_operators/dataframe_operators.py +++ b/cdisc_rules_engine/check_operators/dataframe_operators.py @@ -1419,7 +1419,7 @@ def _coerce_sequence_value_to_int(self, value): Returns: - (True, int_value) when conversion is valid - (False, None) when value cannot be used in consecutive sequence logic - """ + """ if value is None or value == "" or pd.isna(value): return False, None if isinstance(value, (int, np.integer)): @@ -1509,8 +1509,8 @@ def _check_ordered_partition_strict(self, partition: pd.Series) -> pd.Series: def is_consecutive_ordered_set(self, other_value): """ Checks whether the values in the target column are consecutive and ordered - within each group defined by the comparator. - + within each group defined by the comparator. + This operator extends is_ordered_set by adding continuity validation: after grouping rows by comparator, target values must be in ascending order and each next value must be either: @@ -1565,7 +1565,7 @@ def is_consecutive_ordered_set(self, other_value): def is_not_consecutive_ordered_set(self, other_value): """ Complement of is_consecutive_ordered_set. - """ + """ return ~self.is_consecutive_ordered_set(other_value) @log_operator_execution From a6077ed20a44e55f492b697ffe12050a4ee7e50f Mon Sep 17 00:00:00 2001 From: "Niemes, Adam" Date: Thu, 6 Aug 2026 08:46:00 -0400 Subject: [PATCH 4/4] reran black on dataframe_operators --- cdisc_rules_engine/check_operators/dataframe_operators.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cdisc_rules_engine/check_operators/dataframe_operators.py b/cdisc_rules_engine/check_operators/dataframe_operators.py index b014af5e7..9e1a9e8b8 100644 --- a/cdisc_rules_engine/check_operators/dataframe_operators.py +++ b/cdisc_rules_engine/check_operators/dataframe_operators.py @@ -1556,7 +1556,9 @@ def is_consecutive_ordered_set(self, other_value): ) ordered_result = self.value.convert_to_series(ordered_result).sort_index() - consecutive_result = self.value.convert_to_series(consecutive_result).astype("bool") + consecutive_result = self.value.convert_to_series(consecutive_result).astype( + "bool" + ) return ordered_result & consecutive_result