Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions cdisc_rules_engine/check_operators/dataframe_operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,11 @@ def starts_with(self, other_value):
results = self.value[target].str.startswith(comparison_data)
return results

@log_operator_execution
@type_operator(FIELD_DATAFRAME)
def does_not_start_with(self, other_value):
return ~self.starts_with(other_value)

@log_operator_execution
@type_operator(FIELD_DATAFRAME)
def ends_with(self, other_value):
Expand All @@ -862,6 +867,11 @@ def ends_with(self, other_value):
results = self.value[target].str.endswith(comparison_data)
return results

@log_operator_execution
@type_operator(FIELD_DATAFRAME)
def does_not_end_with(self, other_value):
return ~self.ends_with(other_value)

@log_operator_execution
@type_operator(FIELD_DATAFRAME)
def has_equal_length(self, other_value: dict):
Expand Down
20 changes: 20 additions & 0 deletions resources/schema/rule-merged/Operator.json
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,16 @@
"required": ["operator", "value"],
"type": "object"
},
{
"properties": {
"operator": {
"const": "does_not_end_with",
"markdownDescription": "\nSubstring matching\n\n> DOMAIN not ending with 'FOOBAR'\n\n```yaml\n- name: \"DOMAIN\"\n operator: \"does_not_end_with\"\n value: \"FOOBAR\"\n```\n"
}
},
"required": ["operator", "value"],
"type": "object"
},
{
"properties": {
"operator": {
Expand Down Expand Up @@ -792,6 +802,16 @@
"required": ["operator", "value"],
"type": "object"
},
{
"properties": {
"operator": {
"const": "does_not_start_with",
"markdownDescription": "\nSubstring matching\n\n> DOMAIN not beginning with 'AP'\n\n```yaml\n- name: \"DOMAIN\"\n operator: \"does_not_start_with\"\n value: \"AP\"\n```\n"
}
},
"required": ["operator", "value"],
"type": "object"
},
{
"properties": {
"operator": {
Expand Down
10 changes: 10 additions & 0 deletions resources/schema/rule/Operator.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@
"required": ["operator", "value"],
"type": "object"
},
{
"properties": { "operator": { "const": "does_not_end_with" } },
"required": ["operator", "value"],
"type": "object"
},
{
"properties": {
"operator": { "const": "equal_to" },
Expand Down Expand Up @@ -432,6 +437,11 @@
"required": ["operator", "value"],
"type": "object"
},
{
"properties": { "operator": { "const": "does_not_start_with" } },
"required": ["operator", "value"],
"type": "object"
},
{
"properties": { "operator": { "const": "suffix_equal_to" } },
"required": ["operator", "suffix", "value"],
Expand Down
24 changes: 24 additions & 0 deletions resources/schema/rule/Operator.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,18 @@ Substring matching
value: "AP"
```

### does_not_start_with

Substring matching

> DOMAIN not beginning with 'AP'

```yaml
- name: "DOMAIN"
operator: "does_not_start_with"
value: "AP"
```

### ends_with

Substring matching
Expand All @@ -271,6 +283,18 @@ Substring matching
value: "FOOBAR"
```

### does_not_end_with

Substring matching

> DOMAIN not ending with 'FOOBAR'

```yaml
- name: "DOMAIN"
operator: "does_not_end_with"
value: "FOOBAR"
```

### prefix_equal_to

True if the `prefix` number of characters beginning a string in `name` match the string in `value`
Expand Down
58 changes: 58 additions & 0 deletions tests/unit/test_check_operators/test_string_comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,32 @@ def test_starts_with(data, comparator, dataset_type, expected_result):
assert result.equals(df.convert_to_series(expected_result))


@pytest.mark.parametrize(
"data,comparator,dataset_type,expected_result",
[
(
{"target": ["Att", "Btt", "Ctt"], "VAR2": ["A", "B", "D"]},
"VAR2",
PandasDataset,
[False, False, True],
),
(
{"target": ["Att", "Btt", "Ctt"], "VAR2": ["A", "B", "D"]},
"VAR2",
DaskDataset,
[False, False, True],
),
],
)
def test_does_not_start_with(data, comparator, dataset_type, expected_result):
df = dataset_type.from_dict(data)
dataframe_type = DataframeType({"value": df})
result = dataframe_type.does_not_start_with(
{"target": "target", "comparator": comparator}
)
assert result.equals(df.convert_to_series(expected_result))


@pytest.mark.parametrize(
"data,comparator,dataset_type,expected_result",
[
Expand All @@ -333,6 +359,38 @@ def test_ends_with(data, comparator, dataset_type, expected_result):
assert result.equals(df.convert_to_series(expected_result))


@pytest.mark.parametrize(
"data,comparator,dataset_type,expected_result",
[
(
{"target": ["Att", "Btt", "Ctt"], "VAR2": ["A", "Bd", "lll"]},
"VAR2",
DaskDataset,
[True, True, True],
),
(
{"target": ["Att", "Btt", "Ctt"], "VAR2": ["A", "Bd", "lll"]},
"Att",
PandasDataset,
[False, True, True],
),
(
{"target": ["A2", "Btt", "Ctt2"], "VAR2": ["2", "3", "2"]},
"VAR2",
PandasDataset,
[False, True, False],
),
],
)
def test_does_not_end_with(data, comparator, dataset_type, expected_result):
df = dataset_type.from_dict(data)
dataframe_type = DataframeType({"value": df})
result = dataframe_type.does_not_end_with(
{"target": "target", "comparator": comparator}
)
assert result.equals(df.convert_to_series(expected_result))


@pytest.mark.parametrize(
"data,comparator,dataset_type,expected_result",
[
Expand Down
Loading