ripple-down-rules 0.5.62__tar.gz → 0.5.64__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/PKG-INFO +1 -1
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/examples/part_containment_rdr/robot_rdr.py +3 -2
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/__init__.py +1 -1
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/datastructures/case.py +10 -4
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/datastructures/dataclasses.py +1 -1
- ripple_down_rules-0.5.64/src/ripple_down_rules/helpers.py +93 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/rdr.py +55 -59
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/rdr_decorators.py +48 -18
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/rules.py +9 -4
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/user_interface/gui.py +9 -7
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/user_interface/ipython_custom_shell.py +1 -1
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/user_interface/object_diagram.py +9 -1
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/user_interface/template_file_creator.py +24 -24
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/utils.py +174 -59
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules.egg-info/PKG-INFO +1 -1
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules.egg-info/SOURCES.txt +2 -0
- ripple_down_rules-0.5.64/test/conftest.py +23 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_helpers/helpers.py +9 -9
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_rdr.py +6 -6
- ripple_down_rules-0.5.64/test/test_rdr_helpers_rdrs.py +65 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_rdr_world.py +2 -2
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_output__scrdr.py +1 -2
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_rdr.py +2 -2
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_results/datasets_physical_object_is_a_robot/rdr_metadata/datasets_physical_object_is_a_robot.json +4 -4
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/physical_object_select_objects_that_are_parts_of_robot_output__mcrdr.py +1 -2
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/physical_object_select_objects_that_are_parts_of_robot_rdr.py +2 -2
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/rdr_metadata/datasets_physical_object_select_objects_that_are_parts_of_robot.json +6 -6
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_template_file_creator.py +3 -4
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_user_interface/test_qt_gui_inline.py +1 -1
- ripple_down_rules-0.5.64/test/test_utils.py +38 -0
- ripple_down_rules-0.5.62/src/ripple_down_rules/helpers.py +0 -51
- ripple_down_rules-0.5.62/test/test_utils.py +0 -23
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/.github/workflows/build_and_deploy_doc.yml +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/.github/workflows/ci.yml +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/.github/workflows/publish-to-test-pypi.yml +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/.idea/shelf/Uncommitted_changes_before_Checkout_at_2_4_25,_6_32_PM_[Changes]/shelved.patch +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/.idea/shelf/Uncommitted_changes_before_Checkout_at_2_4_25,_6_32_PM_[Changes]1/shelved.patch +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/LICENSE +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/README.md +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/doc/_config.yml +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/doc/_toc.yml +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/doc/bibliography.md +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/doc/intro.md +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/doc/references.bib +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/doc/requirements.txt +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/examples/__init__.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/examples/animal_species.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/examples/part_containment_rdr/__init__.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/examples/part_containment_rdr/rdr_metadata/part_containment_rdr.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/examples/part_containment_rdr/robot_contained_objects_mcrdr.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/examples/part_containment_rdr/robot_contained_objects_mcrdr_defs.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/examples/relational_example.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/images/scrdr.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/images/scrdr.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/images/thinking_pr2.jpg +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/pyproject.toml +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/pytest.ini +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/requirements-dev-ci.txt +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/requirements-dev.txt +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/requirements-gui.txt +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/requirements-viz.txt +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/requirements.txt +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_mcrdr_extra.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_mcrdr_extra.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_mcrdr_stop_only.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_mcrdr_stop_only.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_mcrdr_stop_plus_rule.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_mcrdr_stop_plus_rule.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_scrdr.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_scrdr.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_scrdr_2.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_scrdr_2.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_scrdr_3.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/complete_scrdr_3.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/grdr_Habitat.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/grdr_Habitat.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/grdr_Species.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/grdr_Species.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/mcrdr_extra.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/mcrdr_extra.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/mcrdr_extra_classify.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/mcrdr_extra_classify.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/mcrdr_stop_plus_rule_combined.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/mcrdr_stop_plus_rule_combined.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/partial_mcrdr_extra.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/partial_mcrdr_extra.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/relational_scrdr_classify.dot +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/results/relational_scrdr_classify.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/setup.cfg +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/datastructures/__init__.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/datastructures/callable_expression.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/datastructures/enums.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/experts.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/start-code-server.sh +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/user_interface/__init__.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/user_interface/prompt.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules.egg-info/dependency_links.txt +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules.egg-info/requires.txt +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules.egg-info/top_level.txt +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/datasets.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/correct_drawer_rdr_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/grdr_expert_answers_classify.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/grdr_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/grdr_expert_answers_fit_extra.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/grdr_expert_answers_fit_no_targets.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/mcrdr_expert_answers_classify.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/mcrdr_expert_answers_fit_no_targets.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/mcrdr_expert_answers_stop_only_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/mcrdr_extra_expert_answers_classify.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/mcrdr_extra_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/mcrdr_multi_line_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/mcrdr_stop_only_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/mcrdr_stop_plus_rule_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/mcrdr_stop_plus_rule_combined_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/mcrdr_stop_plus_rule_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/mutagenic_expert_answers.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/relational_scrdr_expert_answers_classify.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/scrdr_expert_answers_classify.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/scrdr_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/scrdr_expert_answers_fit_no_targets.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/scrdr_multi_line_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/scrdr_world_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_expert_answers/view_rdr_expert_answers_fit.json +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_generated_rdrs/__init__.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_helpers/__init__.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_helpers/object_diagram_case_query.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_helpers/object_diagram_person.png +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_json_serialization.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_object_diagram.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_on_mutagenic.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_rdr_alchemy.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_rdr_decorators.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_relational_rdr.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_relational_rdr_alchemy.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_results/datasets_physical_object_is_a_robot/__init__.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_output__scrdr_defs.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/__init__.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/physical_object_select_objects_that_are_parts_of_robot_output__mcrdr_defs.py +2 -2
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_sql_model.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_user_interface/__init__.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_user_interface/test_ipython.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_user_interface/test_ipython_copilot.py +0 -0
- {ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/test/test_user_interface/test_prompt.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ripple_down_rules
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.64
|
4
4
|
Summary: Implements the various versions of Ripple Down Rules (RDR) for knowledge representation and reasoning.
|
5
5
|
Author-email: Abdelrhman Bassiouny <abassiou@uni-bremen.de>
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
{ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/examples/part_containment_rdr/robot_rdr.py
RENAMED
@@ -1,5 +1,6 @@
|
|
1
1
|
from typing_extensions import Dict, Any
|
2
|
-
|
2
|
+
|
3
|
+
from ripple_down_rules.helpers import general_rdr_classify
|
3
4
|
from ripple_down_rules.datastructures.case import Case, create_case
|
4
5
|
from __main__ import Robot
|
5
6
|
from . import robot_contained_objects_mcrdr as contained_objects_classifier
|
@@ -12,4 +13,4 @@ classifiers_dict['contained_objects'] = contained_objects_classifier
|
|
12
13
|
def classify(case: Robot) -> Dict[str, Any]:
|
13
14
|
if not isinstance(case, Case):
|
14
15
|
case = create_case(case, max_recursion_idx=3)
|
15
|
-
return
|
16
|
+
return general_rdr_classify(classifiers_dict, case)
|
{ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/datastructures/case.py
RENAMED
@@ -84,7 +84,7 @@ class Case(UserDict, SubclassJSONSerializer):
|
|
84
84
|
def _to_json(self) -> Dict[str, Any]:
|
85
85
|
serializable = {k: v for k, v in self.items() if not k.startswith("_")}
|
86
86
|
serializable["_id"] = self._id
|
87
|
-
serializable["_obj_type"] = get_full_class_name(self._obj_type)
|
87
|
+
serializable["_obj_type"] = get_full_class_name(self._obj_type) if self._obj_type is not None else None
|
88
88
|
serializable["_name"] = self._name
|
89
89
|
for k, v in serializable.items():
|
90
90
|
if isinstance(v, set):
|
@@ -96,7 +96,7 @@ class Case(UserDict, SubclassJSONSerializer):
|
|
96
96
|
@classmethod
|
97
97
|
def _from_json(cls, data: Dict[str, Any]) -> Case:
|
98
98
|
id_ = data.pop("_id")
|
99
|
-
obj_type = get_type_from_string(data.pop("_obj_type"))
|
99
|
+
obj_type = get_type_from_string(data.pop("_obj_type")) if data["_obj_type"] is not None else None
|
100
100
|
name = data.pop("_name")
|
101
101
|
for k, v in data.items():
|
102
102
|
data[k] = SubclassJSONSerializer.from_json(v)
|
@@ -308,7 +308,10 @@ def create_case_attribute_from_iterable_attribute(attr_value: Any, name: str, ob
|
|
308
308
|
:return: A case attribute that represents the original iterable attribute.
|
309
309
|
"""
|
310
310
|
values = list(attr_value.values()) if isinstance(attr_value, (dict, UserDict)) else attr_value
|
311
|
-
|
311
|
+
try:
|
312
|
+
_type = type(list(values)[0]) if len(values) > 0 else get_value_type_from_type_hint(name, obj)
|
313
|
+
except ValueError:
|
314
|
+
_type = None
|
312
315
|
attr_case = Case(_type, _id=id(attr_value), _name=name, original_object=attr_value)
|
313
316
|
case_attr = CaseAttribute(values)
|
314
317
|
for idx, val in enumerate(values):
|
@@ -317,7 +320,10 @@ def create_case_attribute_from_iterable_attribute(attr_value: Any, name: str, ob
|
|
317
320
|
obj_name=name, parent_is_iterable=True)
|
318
321
|
attr_case.update(sub_attr_case)
|
319
322
|
for sub_attr, val in attr_case.items():
|
320
|
-
|
323
|
+
try:
|
324
|
+
setattr(case_attr, sub_attr, val)
|
325
|
+
except AttributeError:
|
326
|
+
pass
|
321
327
|
return case_attr
|
322
328
|
|
323
329
|
|
@@ -95,7 +95,7 @@ class CaseQuery:
|
|
95
95
|
"""
|
96
96
|
if self._case is not None:
|
97
97
|
return self._case
|
98
|
-
elif not isinstance(self.original_case,
|
98
|
+
elif not isinstance(self.original_case, Case):
|
99
99
|
self._case = create_case(self.original_case, max_recursion_idx=3)
|
100
100
|
else:
|
101
101
|
self._case = self.original_case
|
@@ -0,0 +1,93 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import os
|
4
|
+
from types import ModuleType
|
5
|
+
|
6
|
+
from .datastructures.case import create_case
|
7
|
+
from .datastructures.dataclasses import CaseQuery
|
8
|
+
from typing_extensions import Type, Optional, Callable, Any, Dict, TYPE_CHECKING, Union
|
9
|
+
|
10
|
+
from .utils import get_func_rdr_model_name, copy_case, make_set, update_case
|
11
|
+
from .utils import calculate_precision_and_recall
|
12
|
+
|
13
|
+
if TYPE_CHECKING:
|
14
|
+
from .rdr import RippleDownRules
|
15
|
+
|
16
|
+
|
17
|
+
def general_rdr_classify(classifiers_dict: Dict[str, Union[ModuleType, RippleDownRules]],
|
18
|
+
case: Any, modify_original_case: bool = False) -> Dict[str, Any]:
|
19
|
+
"""
|
20
|
+
Classify a case by going through all classifiers and adding the categories that are classified,
|
21
|
+
and then restarting the classification until no more categories can be added.
|
22
|
+
|
23
|
+
:param classifiers_dict: A dictionary mapping conclusion types to the classifiers that produce them.
|
24
|
+
:param case: The case to classify.
|
25
|
+
:param modify_original_case: Whether to modify the original case or create a copy and modify it.
|
26
|
+
:return: The categories that the case belongs to.
|
27
|
+
"""
|
28
|
+
conclusions = {}
|
29
|
+
case = create_case(case)
|
30
|
+
case_cp = copy_case(case) if not modify_original_case else case
|
31
|
+
while True:
|
32
|
+
new_conclusions = {}
|
33
|
+
for attribute_name, rdr in classifiers_dict.items():
|
34
|
+
pred_atts = rdr.classify(case_cp)
|
35
|
+
if pred_atts is None:
|
36
|
+
continue
|
37
|
+
if rdr.mutually_exclusive:
|
38
|
+
if attribute_name not in conclusions or \
|
39
|
+
(attribute_name in conclusions and conclusions[attribute_name] != pred_atts):
|
40
|
+
conclusions[attribute_name] = pred_atts
|
41
|
+
new_conclusions[attribute_name] = pred_atts
|
42
|
+
else:
|
43
|
+
pred_atts = make_set(pred_atts)
|
44
|
+
if attribute_name in conclusions:
|
45
|
+
pred_atts = {p for p in pred_atts if p not in conclusions[attribute_name]}
|
46
|
+
if len(pred_atts) > 0:
|
47
|
+
new_conclusions[attribute_name] = pred_atts
|
48
|
+
if attribute_name not in conclusions:
|
49
|
+
conclusions[attribute_name] = set()
|
50
|
+
conclusions[attribute_name].update(pred_atts)
|
51
|
+
if attribute_name in new_conclusions:
|
52
|
+
case_query = CaseQuery(case_cp, attribute_name, rdr.conclusion_type, rdr.mutually_exclusive)
|
53
|
+
update_case(case_query, new_conclusions)
|
54
|
+
if len(new_conclusions) == 0:
|
55
|
+
break
|
56
|
+
return conclusions
|
57
|
+
|
58
|
+
|
59
|
+
def is_matching(classifier: Callable[[Any], Any], case_query: CaseQuery, pred_cat: Optional[Dict[str, Any]] = None) -> bool:
|
60
|
+
"""
|
61
|
+
:param classifier: The RDR classifier to check the prediction of.
|
62
|
+
:param case_query: The case query to check.
|
63
|
+
:param pred_cat: The predicted category.
|
64
|
+
:return: Whether the classifier prediction is matching case_query target or not.
|
65
|
+
"""
|
66
|
+
if case_query.target is None:
|
67
|
+
return False
|
68
|
+
if pred_cat is None:
|
69
|
+
pred_cat = classifier(case_query.case)
|
70
|
+
if not isinstance(pred_cat, dict):
|
71
|
+
pred_cat = {case_query.attribute_name: pred_cat}
|
72
|
+
target = {case_query.attribute_name: case_query.target_value}
|
73
|
+
precision, recall = calculate_precision_and_recall(pred_cat, target)
|
74
|
+
return all(recall) and all(precision)
|
75
|
+
|
76
|
+
|
77
|
+
def load_or_create_func_rdr_model(func, model_dir: str, rdr_type: Type[RippleDownRules],
|
78
|
+
**rdr_kwargs) -> RippleDownRules:
|
79
|
+
"""
|
80
|
+
Load the RDR model of the function if it exists, otherwise create a new one.
|
81
|
+
|
82
|
+
:param func: The function to load the model for.
|
83
|
+
:param model_dir: The directory where the model is stored.
|
84
|
+
:param rdr_type: The type of the RDR model to load.
|
85
|
+
:param rdr_kwargs: Additional arguments to pass to the RDR constructor in the case of a new model.
|
86
|
+
"""
|
87
|
+
model_name = get_func_rdr_model_name(func)
|
88
|
+
model_path = os.path.join(model_dir, model_name, "rdr_metadata", f"{model_name}.json")
|
89
|
+
if os.path.exists(model_path):
|
90
|
+
rdr = rdr_type.load(load_dir=model_dir, model_name=model_name)
|
91
|
+
else:
|
92
|
+
rdr = rdr_type(**rdr_kwargs)
|
93
|
+
return rdr
|
@@ -28,7 +28,7 @@ from .datastructures.case import Case, CaseAttribute, create_case
|
|
28
28
|
from .datastructures.dataclasses import CaseQuery
|
29
29
|
from .datastructures.enums import MCRDRMode
|
30
30
|
from .experts import Expert, Human
|
31
|
-
from .helpers import is_matching
|
31
|
+
from .helpers import is_matching, general_rdr_classify
|
32
32
|
from .rules import Rule, SingleClassRule, MultiClassTopRule, MultiClassStopRule
|
33
33
|
try:
|
34
34
|
from .user_interface.gui import RDRCaseViewer
|
@@ -36,7 +36,7 @@ except ImportError as e:
|
|
36
36
|
RDRCaseViewer = None
|
37
37
|
from .utils import draw_tree, make_set, copy_case, \
|
38
38
|
SubclassJSONSerializer, make_list, get_type_from_string, \
|
39
|
-
is_conflicting,
|
39
|
+
is_conflicting, get_imports_from_scope, extract_function_source, extract_imports, get_full_class_name, \
|
40
40
|
is_iterable, str_to_snake_case
|
41
41
|
|
42
42
|
|
@@ -76,16 +76,18 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
|
|
76
76
|
"""
|
77
77
|
The name of the model. If None, the model name will be the generated python file name.
|
78
78
|
"""
|
79
|
+
mutually_exclusive: Optional[bool] = None
|
80
|
+
"""
|
81
|
+
Whether the output of the classification of this rdr allows only one possible conclusion or not.
|
82
|
+
"""
|
79
83
|
|
80
84
|
def __init__(self, start_rule: Optional[Rule] = None, viewer: Optional[RDRCaseViewer] = None,
|
81
|
-
save_dir: Optional[str] = None,
|
85
|
+
save_dir: Optional[str] = None, model_name: Optional[str] = None):
|
82
86
|
"""
|
83
87
|
:param start_rule: The starting rule for the classifier.
|
84
88
|
:param viewer: The viewer gui to use for the classifier. If None, no viewer is used.
|
85
89
|
:param save_dir: The directory to save the classifier to.
|
86
|
-
:param ask_always: Whether to always ask the expert (True) or only ask when classification fails (False).
|
87
90
|
"""
|
88
|
-
self.ask_always: bool = ask_always
|
89
91
|
self.model_name: Optional[str] = model_name
|
90
92
|
self.save_dir = save_dir
|
91
93
|
self.start_rule = start_rule
|
@@ -224,7 +226,10 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
|
|
224
226
|
"""
|
225
227
|
pass
|
226
228
|
|
227
|
-
def fit_case(self, case_query: CaseQuery,
|
229
|
+
def fit_case(self, case_query: CaseQuery,
|
230
|
+
expert: Optional[Expert] = None,
|
231
|
+
update_existing_rules: bool = True,
|
232
|
+
**kwargs) \
|
228
233
|
-> Union[CallableExpression, Dict[str, CallableExpression]]:
|
229
234
|
"""
|
230
235
|
Fit the classifier to a case and ask the expert for refinements or alternatives if the classification is
|
@@ -232,6 +237,8 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
|
|
232
237
|
|
233
238
|
:param case_query: The query containing the case to classify and the target category to compare the case with.
|
234
239
|
:param expert: The expert to ask for differentiating features as new rule conditions.
|
240
|
+
:param update_existing_rules: Whether to update the existing same conclusion type rules that already gave
|
241
|
+
some conclusions with the type required by the case query.
|
235
242
|
:return: The category that the case belongs to.
|
236
243
|
"""
|
237
244
|
if case_query is None:
|
@@ -248,11 +255,7 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
|
|
248
255
|
if case_query.target is None:
|
249
256
|
case_query_cp = copy(case_query)
|
250
257
|
conclusions = self.classify(case_query_cp.case, modify_case=True)
|
251
|
-
if
|
252
|
-
or is_iterable(conclusions) and len(conclusions) == 0
|
253
|
-
or (isinstance(conclusions, dict) and (case_query_cp.attribute_name not in conclusions
|
254
|
-
or not any(type(c) in case_query_cp.core_attribute_type
|
255
|
-
for c in make_list(conclusions[case_query_cp.attribute_name]))))):
|
258
|
+
if self.should_i_ask_the_expert_for_a_target(conclusions, case_query_cp, update_existing_rules):
|
256
259
|
expert.ask_for_conclusion(case_query_cp)
|
257
260
|
case_query.target = case_query_cp.target
|
258
261
|
if case_query.target is None:
|
@@ -268,6 +271,34 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
|
|
268
271
|
|
269
272
|
return fit_case_result
|
270
273
|
|
274
|
+
@staticmethod
|
275
|
+
def should_i_ask_the_expert_for_a_target(conclusions: Union[Any, Dict[str, Any]],
|
276
|
+
case_query: CaseQuery,
|
277
|
+
update_existing: bool) -> bool:
|
278
|
+
"""
|
279
|
+
Determine if the rdr should ask the expert for the target of a given case query.
|
280
|
+
|
281
|
+
:param conclusions: The conclusions of the case.
|
282
|
+
:param case_query: The query containing the case to classify.
|
283
|
+
:param update_existing: Whether to update rules that gave the required type of conclusions.
|
284
|
+
:return: True if the rdr should ask the expert, False otherwise.
|
285
|
+
"""
|
286
|
+
if conclusions is None:
|
287
|
+
return True
|
288
|
+
elif is_iterable(conclusions) and len(conclusions) == 0:
|
289
|
+
return True
|
290
|
+
elif isinstance(conclusions, dict):
|
291
|
+
if case_query.attribute_name not in conclusions:
|
292
|
+
return True
|
293
|
+
conclusions = conclusions[case_query.attribute_name]
|
294
|
+
conclusion_types = map(type, make_list(conclusions))
|
295
|
+
if not any(ct in case_query.core_attribute_type for ct in conclusion_types):
|
296
|
+
return True
|
297
|
+
elif update_existing:
|
298
|
+
return True
|
299
|
+
else:
|
300
|
+
return False
|
301
|
+
|
271
302
|
@abstractmethod
|
272
303
|
def _fit_case(self, case_query: CaseQuery, expert: Optional[Expert] = None, **kwargs) \
|
273
304
|
-> Union[CallableExpression, Dict[str, CallableExpression]]:
|
@@ -423,11 +454,10 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
|
|
423
454
|
f.write(defs_imports + "\n\n")
|
424
455
|
with open(file_name, "w") as f:
|
425
456
|
imports += f"from .{self.generated_python_defs_file_name} import *\n"
|
426
|
-
imports += f"from ripple_down_rules.rdr import {self.__class__.__name__}\n"
|
427
457
|
f.write(imports + "\n\n")
|
428
458
|
f.write(f"attribute_name = '{self.attribute_name}'\n")
|
429
459
|
f.write(f"conclusion_type = ({', '.join([ct.__name__ for ct in self.conclusion_type])},)\n")
|
430
|
-
f.write(f"
|
460
|
+
f.write(f"mutually_exclusive = {self.mutually_exclusive}\n")
|
431
461
|
f.write(f"\n\n{func_def}")
|
432
462
|
f.write(f"{' ' * 4}if not isinstance(case, Case):\n"
|
433
463
|
f"{' ' * 4} case = create_case(case, max_recursion_idx=3)\n""")
|
@@ -533,6 +563,11 @@ class RDRWithCodeWriter(RippleDownRules, ABC):
|
|
533
563
|
|
534
564
|
class SingleClassRDR(RDRWithCodeWriter):
|
535
565
|
|
566
|
+
mutually_exclusive: bool = True
|
567
|
+
"""
|
568
|
+
The output of the classification of this rdr negates all other possible outputs, there can only be one true value.
|
569
|
+
"""
|
570
|
+
|
536
571
|
def __init__(self, default_conclusion: Optional[Any] = None, **kwargs):
|
537
572
|
"""
|
538
573
|
:param start_rule: The starting rule for the classifier.
|
@@ -650,6 +685,10 @@ class MultiClassRDR(RDRWithCodeWriter):
|
|
650
685
|
"""
|
651
686
|
The conditions of the stopping rule if needed.
|
652
687
|
"""
|
688
|
+
mutually_exclusive: bool = False
|
689
|
+
"""
|
690
|
+
The output of the classification of this rdr allows for more than one true value as conclusion.
|
691
|
+
"""
|
653
692
|
|
654
693
|
def __init__(self, start_rule: Optional[MultiClassTopRule] = None,
|
655
694
|
mode: MCRDRMode = MCRDRMode.StopOnly, **kwargs):
|
@@ -903,50 +942,7 @@ class GeneralRDR(RippleDownRules):
|
|
903
942
|
:param modify_case: Whether to modify the original case or create a copy and modify it.
|
904
943
|
:return: The categories that the case belongs to.
|
905
944
|
"""
|
906
|
-
return
|
907
|
-
|
908
|
-
@staticmethod
|
909
|
-
def _classify(classifiers_dict: Dict[str, Union[ModuleType, RippleDownRules]],
|
910
|
-
case: Any, modify_original_case: bool = False) -> Dict[str, Any]:
|
911
|
-
"""
|
912
|
-
Classify a case by going through all classifiers and adding the categories that are classified,
|
913
|
-
and then restarting the classification until no more categories can be added.
|
914
|
-
|
915
|
-
:param classifiers_dict: A dictionary mapping conclusion types to the classifiers that produce them.
|
916
|
-
:param case: The case to classify.
|
917
|
-
:param modify_original_case: Whether to modify the original case or create a copy and modify it.
|
918
|
-
:return: The categories that the case belongs to.
|
919
|
-
"""
|
920
|
-
conclusions = {}
|
921
|
-
case = case if isinstance(case, (Case, SQLTable)) else create_case(case)
|
922
|
-
case_cp = copy_case(case) if not modify_original_case else case
|
923
|
-
while True:
|
924
|
-
new_conclusions = {}
|
925
|
-
for attribute_name, rdr in classifiers_dict.items():
|
926
|
-
pred_atts = rdr.classify(case_cp)
|
927
|
-
if pred_atts is None:
|
928
|
-
continue
|
929
|
-
if rdr.type_ is SingleClassRDR:
|
930
|
-
if attribute_name not in conclusions or \
|
931
|
-
(attribute_name in conclusions and conclusions[attribute_name] != pred_atts):
|
932
|
-
conclusions[attribute_name] = pred_atts
|
933
|
-
new_conclusions[attribute_name] = pred_atts
|
934
|
-
else:
|
935
|
-
pred_atts = make_set(pred_atts)
|
936
|
-
if attribute_name in conclusions:
|
937
|
-
pred_atts = {p for p in pred_atts if p not in conclusions[attribute_name]}
|
938
|
-
if len(pred_atts) > 0:
|
939
|
-
new_conclusions[attribute_name] = pred_atts
|
940
|
-
if attribute_name not in conclusions:
|
941
|
-
conclusions[attribute_name] = set()
|
942
|
-
conclusions[attribute_name].update(pred_atts)
|
943
|
-
if attribute_name in new_conclusions:
|
944
|
-
mutually_exclusive = True if rdr.type_ is SingleClassRDR else False
|
945
|
-
case_query = CaseQuery(case_cp, attribute_name, rdr.conclusion_type, mutually_exclusive)
|
946
|
-
update_case(case_query, new_conclusions)
|
947
|
-
if len(new_conclusions) == 0:
|
948
|
-
break
|
949
|
-
return conclusions
|
945
|
+
return general_rdr_classify(self.start_rules_dict, case, modify_original_case=modify_case)
|
950
946
|
|
951
947
|
def _fit_case(self, case_query: CaseQuery, expert: Optional[Expert] = None, **kwargs) \
|
952
948
|
-> Dict[str, Any]:
|
@@ -1043,7 +1039,7 @@ class GeneralRDR(RippleDownRules):
|
|
1043
1039
|
f.write(func_def)
|
1044
1040
|
f.write(f"{' ' * 4}if not isinstance(case, Case):\n"
|
1045
1041
|
f"{' ' * 4} case = create_case(case, max_recursion_idx=3)\n""")
|
1046
|
-
f.write(f"{' ' * 4}return
|
1042
|
+
f.write(f"{' ' * 4}return general_rdr_classify(classifiers_dict, case)\n")
|
1047
1043
|
|
1048
1044
|
@property
|
1049
1045
|
def _default_generated_python_file_name(self) -> Optional[str]:
|
@@ -1068,7 +1064,7 @@ class GeneralRDR(RippleDownRules):
|
|
1068
1064
|
# add type hints
|
1069
1065
|
imports += f"from typing_extensions import Dict, Any\n"
|
1070
1066
|
# import rdr type
|
1071
|
-
imports += f"from ripple_down_rules.
|
1067
|
+
imports += f"from ripple_down_rules.helpers import general_rdr_classify\n"
|
1072
1068
|
# add case type
|
1073
1069
|
imports += f"from ripple_down_rules.datastructures.case import Case, create_case\n"
|
1074
1070
|
imports += f"from {self.case_type.__module__} import {self.case_type.__name__}\n"
|
{ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/rdr_decorators.py
RENAMED
@@ -7,13 +7,14 @@ import os.path
|
|
7
7
|
from functools import wraps
|
8
8
|
|
9
9
|
from pyparsing.tools.cvt_pyparsing_pep8_names import camel_to_snake
|
10
|
-
from typing_extensions import Callable, Optional, Type, Tuple, Dict, Any, Self, get_type_hints, List, Union
|
10
|
+
from typing_extensions import Callable, Optional, Type, Tuple, Dict, Any, Self, get_type_hints, List, Union, Sequence
|
11
11
|
|
12
12
|
from ripple_down_rules.datastructures.case import create_case, Case
|
13
13
|
from ripple_down_rules.datastructures.dataclasses import CaseQuery
|
14
14
|
from ripple_down_rules.datastructures.enums import Category
|
15
15
|
from ripple_down_rules.experts import Expert, Human
|
16
16
|
from ripple_down_rules.rdr import GeneralRDR, RippleDownRules
|
17
|
+
from ripple_down_rules.user_interface.gui import RDRCaseViewer
|
17
18
|
from ripple_down_rules.utils import get_method_args_as_dict, get_func_rdr_model_name, make_set, \
|
18
19
|
get_method_class_if_exists, get_method_name, str_to_snake_case
|
19
20
|
|
@@ -26,7 +27,10 @@ class RDRDecorator:
|
|
26
27
|
mutual_exclusive: bool,
|
27
28
|
output_name: str = "output_",
|
28
29
|
fit: bool = True,
|
29
|
-
expert: Optional[Expert] = None
|
30
|
+
expert: Optional[Expert] = None,
|
31
|
+
ask_always: bool = False,
|
32
|
+
update_existing_rules: bool = True,
|
33
|
+
viewer: Optional[RDRCaseViewer] = None):
|
30
34
|
"""
|
31
35
|
:param models_dir: The directory to save/load the RDR models.
|
32
36
|
:param output_type: The type of the output. This is used to create the RDR model.
|
@@ -38,6 +42,9 @@ class RDRDecorator:
|
|
38
42
|
classification mode. This means that the RDR will classify the function's output based on the RDR model.
|
39
43
|
:param expert: The expert that will be used to prompt the user for the correct output. If None, a Human
|
40
44
|
expert will be used.
|
45
|
+
:param ask_always: If True, the function will ask the user for a target if it doesn't exist.
|
46
|
+
:param update_existing_rules: If True, the function will update the existing RDR rules
|
47
|
+
even if they gave an output.
|
41
48
|
:return: A decorator to use a GeneralRDR as a classifier that monitors and modifies the function's output.
|
42
49
|
"""
|
43
50
|
self.rdr_models_dir = models_dir
|
@@ -48,6 +55,9 @@ class RDRDecorator:
|
|
48
55
|
self.output_name = output_name
|
49
56
|
self.fit: bool = fit
|
50
57
|
self.expert: Optional[Expert] = expert
|
58
|
+
self.ask_always = ask_always
|
59
|
+
self.update_existing_rules = update_existing_rules
|
60
|
+
self.viewer = viewer
|
51
61
|
self.load()
|
52
62
|
|
53
63
|
def decorator(self, func: Callable) -> Callable:
|
@@ -59,59 +69,77 @@ class RDRDecorator:
|
|
59
69
|
self.parsed_output_type = self.parse_output_type(func, self.output_type, *args)
|
60
70
|
if self.model_name is None:
|
61
71
|
self.initialize_rdr_model_name_and_load(func)
|
72
|
+
if self.expert is None:
|
73
|
+
self.expert = Human(viewer=self.viewer,
|
74
|
+
answers_save_path=self.rdr_models_dir + f'/expert_answers')
|
75
|
+
|
76
|
+
func_output = {self.output_name: func(*args, **kwargs)}
|
62
77
|
|
63
78
|
if self.fit:
|
64
|
-
case_query = self.create_case_query_from_method(func,
|
65
|
-
self.
|
79
|
+
case_query = self.create_case_query_from_method(func, func_output,
|
80
|
+
self.parsed_output_type,
|
81
|
+
self.mutual_exclusive,
|
66
82
|
*args, **kwargs)
|
67
|
-
output = self.rdr.fit_case(case_query, expert=self.expert
|
83
|
+
output = self.rdr.fit_case(case_query, expert=self.expert,
|
84
|
+
ask_always_for_target=self.ask_always,
|
85
|
+
update_existing_rules=self.update_existing_rules,
|
86
|
+
viewer=self.viewer)
|
87
|
+
else:
|
88
|
+
case, case_dict = self.create_case_from_method(func, func_output, *args, **kwargs)
|
89
|
+
output = self.rdr.classify(case)
|
90
|
+
|
91
|
+
if self.output_name in output:
|
68
92
|
return output[self.output_name]
|
69
93
|
else:
|
70
|
-
|
71
|
-
return self.rdr.classify(case)[self.output_name]
|
94
|
+
return func_output[self.output_name]
|
72
95
|
|
73
96
|
return wrapper
|
74
97
|
|
75
98
|
@staticmethod
|
76
|
-
def create_case_query_from_method(func: Callable,
|
77
|
-
|
99
|
+
def create_case_query_from_method(func: Callable,
|
100
|
+
func_output: Dict[str, Any],
|
101
|
+
output_type: Sequence[Type],
|
102
|
+
mutual_exclusive: bool,
|
103
|
+
*args, **kwargs) -> CaseQuery:
|
78
104
|
"""
|
79
105
|
Create a CaseQuery from the function and its arguments.
|
80
106
|
|
81
107
|
:param func: The function to create a case from.
|
82
|
-
:param
|
108
|
+
:param func_output: The output of the function as a dictionary, where the key is the output name.
|
109
|
+
:param output_type: The type of the output as a sequence of types.
|
83
110
|
:param mutual_exclusive: If True, the output types are mutually exclusive.
|
84
|
-
:param output_name: The name of the output in the case. Defaults to 'output_'.
|
85
111
|
:param args: The positional arguments of the function.
|
86
112
|
:param kwargs: The keyword arguments of the function.
|
87
113
|
:return: A CaseQuery object representing the case.
|
88
114
|
"""
|
89
115
|
output_type = make_set(output_type)
|
90
|
-
case, case_dict = RDRDecorator.create_case_from_method(func,
|
116
|
+
case, case_dict = RDRDecorator.create_case_from_method(func, func_output, *args, **kwargs)
|
91
117
|
scope = func.__globals__
|
92
118
|
scope.update(case_dict)
|
93
119
|
func_args_type_hints = get_type_hints(func)
|
120
|
+
output_name = list(func_output.keys())[0]
|
94
121
|
func_args_type_hints.update({output_name: Union[tuple(output_type)]})
|
95
122
|
return CaseQuery(case, output_name, Union[tuple(output_type)],
|
96
123
|
mutual_exclusive, scope=scope,
|
97
124
|
is_function=True, function_args_type_hints=func_args_type_hints)
|
98
125
|
|
99
126
|
@staticmethod
|
100
|
-
def create_case_from_method(func: Callable,
|
127
|
+
def create_case_from_method(func: Callable,
|
128
|
+
func_output: Dict[str, Any],
|
129
|
+
*args, **kwargs) -> Tuple[Case, Dict[str, Any]]:
|
101
130
|
"""
|
102
131
|
Create a Case from the function and its arguments.
|
103
132
|
|
104
133
|
:param func: The function to create a case from.
|
105
|
-
:param
|
134
|
+
:param func_output: A dictionary containing the output of the function, where the key is the output name.
|
106
135
|
:param args: The positional arguments of the function.
|
107
136
|
:param kwargs: The keyword arguments of the function.
|
108
137
|
:return: A Case object representing the case.
|
109
138
|
"""
|
110
139
|
case_dict = get_method_args_as_dict(func, *args, **kwargs)
|
111
|
-
func_output
|
112
|
-
case_dict.update({output_name: func_output})
|
140
|
+
case_dict.update(func_output)
|
113
141
|
case_name = get_func_rdr_model_name(func)
|
114
|
-
return
|
142
|
+
return Case(dict, id(case_dict), case_name, case_dict, **case_dict), case_dict
|
115
143
|
|
116
144
|
def initialize_rdr_model_name_and_load(self, func: Callable) -> None:
|
117
145
|
model_file_name = get_func_rdr_model_name(func, include_file_name=True)
|
@@ -148,8 +176,10 @@ class RDRDecorator:
|
|
148
176
|
model_path = os.path.join(self.rdr_models_dir, self.model_name + f"/rdr_metadata/{self.model_name}.json")
|
149
177
|
if os.path.exists(os.path.join(self.rdr_models_dir, model_path)):
|
150
178
|
self.rdr = GeneralRDR.load(self.rdr_models_dir, self.model_name)
|
179
|
+
self.rdr.set_viewer(self.viewer)
|
151
180
|
if self.rdr is None:
|
152
|
-
self.rdr = GeneralRDR(save_dir=self.rdr_models_dir, model_name=self.model_name
|
181
|
+
self.rdr = GeneralRDR(save_dir=self.rdr_models_dir, model_name=self.model_name,
|
182
|
+
viewer=self.viewer)
|
153
183
|
|
154
184
|
def update_from_python(self):
|
155
185
|
"""
|
@@ -12,7 +12,7 @@ from typing_extensions import List, Optional, Self, Union, Dict, Any, Tuple
|
|
12
12
|
from .datastructures.callable_expression import CallableExpression
|
13
13
|
from .datastructures.case import Case
|
14
14
|
from .datastructures.enums import RDREdge, Stop
|
15
|
-
from .utils import SubclassJSONSerializer, conclusion_to_json
|
15
|
+
from .utils import SubclassJSONSerializer, conclusion_to_json, get_full_class_name
|
16
16
|
|
17
17
|
|
18
18
|
class Rule(NodeMixin, SubclassJSONSerializer, ABC):
|
@@ -150,11 +150,16 @@ class Rule(NodeMixin, SubclassJSONSerializer, ABC):
|
|
150
150
|
pass
|
151
151
|
|
152
152
|
def _to_json(self) -> Dict[str, Any]:
|
153
|
-
|
153
|
+
try:
|
154
|
+
corner_case = SubclassJSONSerializer.to_json_static(self.corner_case) if self.corner_case else None
|
155
|
+
except Exception as e:
|
156
|
+
logging.debug("Failed to serialize corner case to json, setting it to None. Error: %s", e)
|
157
|
+
corner_case = None
|
158
|
+
json_serialization = {"_type": get_full_class_name(type(self)),
|
159
|
+
"conditions": self.conditions.to_json(),
|
154
160
|
"conclusion": conclusion_to_json(self.conclusion),
|
155
161
|
"parent": self.parent.json_serialization if self.parent else None,
|
156
|
-
"corner_case":
|
157
|
-
if self.corner_case else None,
|
162
|
+
"corner_case": corner_case,
|
158
163
|
"conclusion_name": self.conclusion_name,
|
159
164
|
"weight": self.weight,
|
160
165
|
"uid": self.uid}
|
{ripple_down_rules-0.5.62 → ripple_down_rules-0.5.64}/src/ripple_down_rules/user_interface/gui.py
RENAMED
@@ -281,12 +281,14 @@ class RDRCaseViewer(QMainWindow):
|
|
281
281
|
main_obj: Optional[Dict[str, Any]] = None
|
282
282
|
user_input: Optional[str] = None
|
283
283
|
attributes_widget: Optional[QWidget] = None
|
284
|
-
save_function: Optional[Callable[str], None] = None
|
284
|
+
save_function: Optional[Callable[str, str], None] = None
|
285
285
|
|
286
|
-
|
287
|
-
|
286
|
+
def __init__(self, parent=None,
|
287
|
+
save_dir: Optional[str] = None,
|
288
|
+
save_model_name: Optional[str] = None):
|
288
289
|
super().__init__(parent)
|
289
|
-
self.
|
290
|
+
self.save_dir = save_dir
|
291
|
+
self.save_model_name = save_model_name
|
290
292
|
|
291
293
|
self.setWindowTitle("RDR Case Viewer")
|
292
294
|
|
@@ -323,17 +325,17 @@ class RDRCaseViewer(QMainWindow):
|
|
323
325
|
|
324
326
|
# Add both to main layout
|
325
327
|
main_layout.addWidget(self.attributes_widget, stretch=1)
|
326
|
-
main_layout.addWidget(middle_widget, stretch=
|
328
|
+
main_layout.addWidget(middle_widget, stretch=1)
|
327
329
|
main_layout.addWidget(self.obj_diagram_viewer, stretch=2)
|
328
330
|
|
329
|
-
def set_save_function(self, save_function: Callable[[str], None]) -> None:
|
331
|
+
def set_save_function(self, save_function: Callable[[str, str], None]) -> None:
|
330
332
|
"""
|
331
333
|
Set the function to save the file.
|
332
334
|
|
333
335
|
:param save_function: The function to save the file.
|
334
336
|
"""
|
335
337
|
self.save_function = save_function
|
336
|
-
self.save_btn.clicked.connect(lambda: self.save_function(self.
|
338
|
+
self.save_btn.clicked.connect(lambda: self.save_function(self.save_dir, self.save_model_name))
|
337
339
|
|
338
340
|
def print(self, msg):
|
339
341
|
"""
|
@@ -134,7 +134,7 @@ class IPythonShell:
|
|
134
134
|
"""
|
135
135
|
Update the user input from the code lines captured in the shell.
|
136
136
|
"""
|
137
|
-
if
|
137
|
+
if self.shell.all_lines[0].replace('return', '').strip() == '':
|
138
138
|
self.user_input = None
|
139
139
|
else:
|
140
140
|
self.all_code_lines = extract_dependencies(self.shell.all_lines)
|
@@ -1,5 +1,9 @@
|
|
1
1
|
import logging
|
2
2
|
|
3
|
+
from ripple_down_rules.datastructures.case import Case
|
4
|
+
from ripple_down_rules.datastructures.dataclasses import CaseQuery
|
5
|
+
from ripple_down_rules.utils import SubclassJSONSerializer
|
6
|
+
|
3
7
|
try:
|
4
8
|
import graphviz
|
5
9
|
except ImportError:
|
@@ -77,7 +81,11 @@ def generate_object_graph(obj, name='root', seen=None, graph=None, current_depth
|
|
77
81
|
for attr in dir(obj):
|
78
82
|
if attr.startswith('_'):
|
79
83
|
continue
|
80
|
-
if attr == 'scope':
|
84
|
+
if isinstance(obj, CaseQuery) and attr == 'scope':
|
85
|
+
continue
|
86
|
+
if isinstance(obj, Case) and attr in ['data']:
|
87
|
+
continue
|
88
|
+
if isinstance(obj, SubclassJSONSerializer) and attr == 'data_class_refs':
|
81
89
|
continue
|
82
90
|
value = getattr(obj, attr)
|
83
91
|
if callable(value):
|