diff --git a/retrack/engine/executor.py b/retrack/engine/executor.py index 0202670..7e3b8ab 100644 --- a/retrack/engine/executor.py +++ b/retrack/engine/executor.py @@ -126,11 +126,6 @@ def __run_node(self, node_id: str, execution: Execution): node.model_dump(by_alias=True), current_node_filter, execution=execution ) - if input_params is None or ( - hasattr(input_params, "empty") and input_params.empty - ): - return - # TODO: Remove this condition after adding support for kwargs in the run method for all nodes if node.kind() == NodeKind.CONNECTOR: input_params["context"] = execution.context diff --git a/retrack/engine/rule.py b/retrack/engine/rule.py index ced326d..4d299b4 100644 --- a/retrack/engine/rule.py +++ b/retrack/engine/rule.py @@ -38,7 +38,7 @@ def create( dynamic_nodes_registry: Registry, validator_registry: Registry = validators.registry(), raise_if_null_version: bool = False, - validate_version: bool = True, + validate_version: bool = False, name: str = None, ): components_registry = Rule.create_component_registry( diff --git a/retrack/nodes/dynamic/conditional_connector.py b/retrack/nodes/dynamic/conditional_connector.py index da041bd..924964e 100644 --- a/retrack/nodes/dynamic/conditional_connector.py +++ b/retrack/nodes/dynamic/conditional_connector.py @@ -29,7 +29,7 @@ def conditional_connector_factory( input_fields[name] = BaseDynamicNode.create_sub_field(InputConnectionModel) inputs_model = BaseDynamicIOModel.with_fields( - "CSVTableV0InputsModel", **input_fields + "ConditionalConnectorInputsModel", **input_fields ) models = { diff --git a/tests/resources/conditional-connector.json b/tests/resources/conditional-connector.json new file mode 100644 index 0000000..d99a22b --- /dev/null +++ b/tests/resources/conditional-connector.json @@ -0,0 +1,417 @@ +{ + "id": "demo@0.1.0", + "nodes": { + "79c91efb5fc8e23e": { + "id": "79c91efb5fc8e23e", + "name": "Start", + "data": {}, + "outputs": { + "output_up_void": { + "connections": [ + { + "node": "6690e619d640e884", + "input": "input_void" + } + ] + }, + "output_down_void": { + "connections": [ + { + "node": "e81e103583abac29", + "input": "input_void" + } + ] + } + }, + "inputs": {} + }, + "5dc2fa95808ff994": { + "id": "5dc2fa95808ff994", + "name": "Input", + "data": { + "name": "cpf", + "default": "" + }, + "outputs": { + "output_value": { + "connections": [ + { + "node": "4a18c47f529ad26b", + "input": "input_value_0" + } + ] + } + }, + "inputs": { + "input_void": { + "connections": [ + { + "node": "92fe051396102987", + "output": "output_then_filter" + } + ] + } + } + }, + "7be7fcca32dbbf02": { + "id": "7be7fcca32dbbf02", + "name": "Input", + "data": { + "name": "license_plate", + "default": "" + }, + "outputs": { + "output_value": { + "connections": [ + { + "node": "288c0a42cefd7dd4", + "input": "input_value_0" + } + ] + } + }, + "inputs": { + "input_void": { + "connections": [ + { + "node": "92fe051396102987", + "output": "output_then_filter" + } + ] + } + } + }, + "288c0a42cefd7dd4": { + "id": "288c0a42cefd7dd4", + "name": "BureauConnector", + "data": { + "name": "rcf", + "source": "vehicle_claims:rns", + "resource": "qtd_sinistros_rcf", + "headers": [ + "license_plate" + ], + "headers_map": [ + "input_value_0" + ] + }, + "outputs": { + "output_value": { + "connections": [ + { + "node": "8e17dcc63de89878", + "input": "input_value_1" + } + ] + } + }, + "inputs": { + "input_value_0": { + "connections": [ + { + "node": "7be7fcca32dbbf02", + "output": "output_value" + } + ] + } + } + }, + "4a18c47f529ad26b": { + "id": "4a18c47f529ad26b", + "name": "BureauConnector", + "data": { + "name": "indefinido", + "source": "person_claims:rns", + "resource": "qtd_sinistros_indefinido", + "headers": [ + "cpf" + ], + "headers_map": [ + "input_value_0" + ] + }, + "outputs": { + "output_value": { + "connections": [ + { + "node": "8e17dcc63de89878", + "input": "input_value_0" + } + ] + } + }, + "inputs": { + "input_value_0": { + "connections": [ + { + "node": "5dc2fa95808ff994", + "output": "output_value" + } + ] + } + } + }, + "8e17dcc63de89878": { + "id": "8e17dcc63de89878", + "name": "Math", + "data": { + "operator": "+" + }, + "outputs": { + "output_value": { + "connections": [ + { + "node": "a4a18ab637c3946b", + "input": "input_value" + } + ] + } + }, + "inputs": { + "input_value_0": { + "connections": [ + { + "node": "4a18c47f529ad26b", + "output": "output_value" + } + ] + }, + "input_value_1": { + "connections": [ + { + "node": "288c0a42cefd7dd4", + "output": "output_value" + } + ] + } + } + }, + "a4a18ab637c3946b": { + "id": "a4a18ab637c3946b", + "name": "Output", + "data": { + "message": "rns" + }, + "outputs": {}, + "inputs": { + "input_value": { + "connections": [ + { + "node": "8e17dcc63de89878", + "output": "output_value" + } + ] + } + } + }, + "6690e619d640e884": { + "id": "6690e619d640e884", + "name": "Input", + "data": { + "name": "age", + "default": "" + }, + "outputs": { + "output_value": { + "connections": [ + { + "node": "07f7c745c5f436da", + "input": "input_value_0" + } + ] + } + }, + "inputs": { + "input_void": { + "connections": [ + { + "node": "79c91efb5fc8e23e", + "output": "output_up_void" + } + ] + } + } + }, + "07f7c745c5f436da": { + "id": "07f7c745c5f436da", + "name": "Check", + "data": { + "operator": ">" + }, + "outputs": { + "output_bool": { + "connections": [ + { + "node": "92fe051396102987", + "input": "input_bool" + } + ] + } + }, + "inputs": { + "input_value_0": { + "connections": [ + { + "node": "6690e619d640e884", + "output": "output_value" + } + ] + }, + "input_value_1": { + "connections": [ + { + "node": "e81e103583abac29", + "output": "output_value" + } + ] + } + } + }, + "e81e103583abac29": { + "id": "e81e103583abac29", + "name": "Constant", + "data": { + "value": "30" + }, + "outputs": { + "output_value": { + "connections": [ + { + "node": "07f7c745c5f436da", + "input": "input_value_1" + } + ] + } + }, + "inputs": { + "input_void": { + "connections": [ + { + "node": "79c91efb5fc8e23e", + "output": "output_down_void" + } + ] + } + } + }, + "92fe051396102987": { + "id": "92fe051396102987", + "name": "If", + "data": {}, + "outputs": { + "output_then_filter": { + "connections": [ + { + "node": "5dc2fa95808ff994", + "input": "input_void" + }, + { + "node": "7be7fcca32dbbf02", + "input": "input_void" + } + ] + }, + "output_else_filter": { + "connections": [ + { + "node": "79d8f8754d754c3d", + "input": "input_void" + } + ] + } + }, + "inputs": { + "input_bool": { + "connections": [ + { + "node": "07f7c745c5f436da", + "output": "output_bool" + } + ] + } + } + }, + "36396444b9fc8dfa": { + "id": "36396444b9fc8dfa", + "name": "BureauConnector", + "data": { + "name": "is_defandant", + "source": "lawsuits:bigdatacorp", + "resource": "is_defendant_and_court_type", + "headers": [ + "cpf" + ], + "headers_map": [ + "input_value_0" + ] + }, + "outputs": { + "output_value": { + "connections": [ + { + "node": "b29f49c2d84b2688", + "input": "input_value" + } + ] + } + }, + "inputs": { + "input_value_0": { + "connections": [ + { + "node": "79d8f8754d754c3d", + "output": "output_value" + } + ] + } + } + }, + "79d8f8754d754c3d": { + "id": "79d8f8754d754c3d", + "name": "Input", + "data": { + "name": "cpf", + "default": "" + }, + "outputs": { + "output_value": { + "connections": [ + { + "node": "36396444b9fc8dfa", + "input": "input_value_0" + } + ] + } + }, + "inputs": { + "input_void": { + "connections": [ + { + "node": "92fe051396102987", + "output": "output_else_filter" + } + ] + } + } + }, + "b29f49c2d84b2688": { + "id": "b29f49c2d84b2688", + "name": "Output", + "data": { + "message": "bdc" + }, + "outputs": {}, + "inputs": { + "input_value": { + "connections": [ + { + "node": "36396444b9fc8dfa", + "output": "output_value" + } + ] + } + } + } + }, + "version": "981088f3d1.2024-09-10" +} \ No newline at end of file diff --git a/tests/test_engine/test_conditional_connector_rule.py b/tests/test_engine/test_conditional_connector_rule.py new file mode 100644 index 0000000..7981854 --- /dev/null +++ b/tests/test_engine/test_conditional_connector_rule.py @@ -0,0 +1,71 @@ +import typing +import retrack +from retrack.nodes.dynamic import BaseDynamicNode, Registry +from retrack.nodes.dynamic import registry as dynamic_nodes_registry_factory +from retrack.nodes.dynamic.conditional_connector import conditional_connector_factory +import pandas as pd +import pytest + +VALUES = {"qtd_sinistros_rcf": 1, "qtd_sinistros_indefinido": 10, "is_defendant_and_court_type": False} + + +def bureau_connector_factory( + inputs: typing.Dict[str, typing.Any], **kwargs +) -> typing.Type[BaseDynamicNode]: + node_class = conditional_connector_factory(inputs=inputs, **kwargs) + + class ExampleConditionalConnectorNode(node_class): + def run( + self, context: typing.Optional[Registry] = None, **node_inputs + ) -> typing.Dict[str, typing.Any]: + parsed_inputs = {} + for i, in_node_key in enumerate(self.data.headers_map): + if in_node_key not in node_inputs.keys(): + raise ValueError( + f"Missing input {in_node_key} in BureauConnector node" + ) + + if node_inputs[in_node_key].empty: + return {"output_value": None} + + parsed_inputs[self.data.headers[i]] = node_inputs[in_node_key].to_list()[0] + + return {"output_value": VALUES[self.data.resource]} + + return ExampleConditionalConnectorNode + + +@pytest.mark.parametrize( + "age, cpf, license_plate, expected_result, expected_message", + [ + (40, "12345678900", "ABC1234", 11, "rns"), + (17, "12345678900", "ABC1234", False, "bdc"), + ], +) +def test_bureau_connector_factory( + age, cpf, license_plate, expected_result, expected_message +): + dynamic_nodes_registry = dynamic_nodes_registry_factory() + dynamic_nodes_registry.register( + "BureauConnector", bureau_connector_factory, overwrite=True + ) + rule_executor = retrack.from_json( + "tests/resources/conditional-connector.json", + dynamic_nodes_registry=dynamic_nodes_registry, + return_executor=True, + raise_if_null_version=False, + validate_version=False, + ) + + input_nodes = rule_executor.components_registry.get_by_kind( + retrack.nodes.base.NodeKind.INPUT + ) + input_nodes.extend(rule_executor.components_registry.get_by_name("connectorv0")) + + rule_executor.reset_request_manager(input_nodes) + + df = pd.DataFrame({"age": [age], "cpf": [cpf], "license_plate": [license_plate]}) + + result = rule_executor.execute(df, raise_raw_exception=True) + assert result["output"].to_list()[0] == expected_result + assert result["message"].to_list()[0] == expected_message \ No newline at end of file