ripple-down-rules 0.5.63__tar.gz → 0.5.71__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.
Files changed (153) hide show
  1. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/PKG-INFO +2 -1
  2. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/examples/part_containment_rdr/robot_rdr.py +3 -2
  3. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/requirements.txt +2 -1
  4. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/__init__.py +1 -1
  5. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/datastructures/case.py +10 -4
  6. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/datastructures/dataclasses.py +62 -3
  7. ripple_down_rules-0.5.71/src/ripple_down_rules/helpers.py +97 -0
  8. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/rdr.py +141 -101
  9. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/rdr_decorators.py +54 -23
  10. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/rules.py +63 -13
  11. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/user_interface/gui.py +9 -7
  12. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/user_interface/ipython_custom_shell.py +1 -1
  13. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/user_interface/object_diagram.py +9 -1
  14. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/user_interface/template_file_creator.py +17 -22
  15. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/utils.py +235 -62
  16. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules.egg-info/PKG-INFO +2 -1
  17. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules.egg-info/SOURCES.txt +13 -2
  18. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules.egg-info/requires.txt +1 -0
  19. ripple_down_rules-0.5.71/test/conf/world/base_config.py +48 -0
  20. ripple_down_rules-0.5.71/test/conf/world/handles_and_containers.py +50 -0
  21. ripple_down_rules-0.5.71/test/conftest.py +112 -0
  22. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/datasets.py +82 -0
  23. ripple_down_rules-0.5.71/test/factories/world/__init__.py +0 -0
  24. ripple_down_rules-0.5.71/test/factories/world/handles_and_containers.py +22 -0
  25. ripple_down_rules-0.5.71/test/test_generated_rdrs/__init__.py +1 -0
  26. ripple_down_rules-0.5.71/test/test_helpers/__init__.py +0 -0
  27. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_helpers/helpers.py +27 -17
  28. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_on_mutagenic.py +12 -8
  29. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_rdr.py +47 -33
  30. ripple_down_rules-0.5.71/test/test_rdr_helpers_rdrs.py +59 -0
  31. ripple_down_rules-0.5.71/test/test_rdr_world/__init__.py +0 -0
  32. ripple_down_rules-0.5.71/test/test_rdr_world/conftest.py +147 -0
  33. ripple_down_rules-0.5.71/test/test_rdr_world/test_rdr_world.py +52 -0
  34. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_output__scrdr.py +2 -3
  35. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_rdr.py +3 -3
  36. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_results/datasets_physical_object_is_a_robot/rdr_metadata/datasets_physical_object_is_a_robot.json +4 -4
  37. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/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 +2 -3
  38. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/physical_object_select_objects_that_are_parts_of_robot_rdr.py +3 -3
  39. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/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 +2 -2
  40. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_template_file_creator.py +5 -6
  41. ripple_down_rules-0.5.71/test/test_user_interface/__init__.py +0 -0
  42. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_user_interface/test_prompt.py +1 -1
  43. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_user_interface/test_qt_gui_inline.py +4 -1
  44. ripple_down_rules-0.5.71/test/test_utils.py +38 -0
  45. ripple_down_rules-0.5.63/src/ripple_down_rules/helpers.py +0 -51
  46. ripple_down_rules-0.5.63/test/test_rdr_world.py +0 -191
  47. ripple_down_rules-0.5.63/test/test_utils.py +0 -23
  48. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/.github/workflows/build_and_deploy_doc.yml +0 -0
  49. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/.github/workflows/ci.yml +0 -0
  50. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/.github/workflows/publish-to-test-pypi.yml +0 -0
  51. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/.idea/shelf/Uncommitted_changes_before_Checkout_at_2_4_25,_6_32_PM_[Changes]/shelved.patch +0 -0
  52. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/.idea/shelf/Uncommitted_changes_before_Checkout_at_2_4_25,_6_32_PM_[Changes]1/shelved.patch +0 -0
  53. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/LICENSE +0 -0
  54. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/README.md +0 -0
  55. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/doc/_config.yml +0 -0
  56. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/doc/_toc.yml +0 -0
  57. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/doc/bibliography.md +0 -0
  58. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/doc/intro.md +0 -0
  59. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/doc/references.bib +0 -0
  60. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/doc/requirements.txt +0 -0
  61. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/examples/__init__.py +0 -0
  62. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/examples/animal_species.py +0 -0
  63. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/examples/part_containment_rdr/__init__.py +0 -0
  64. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/examples/part_containment_rdr/rdr_metadata/part_containment_rdr.json +0 -0
  65. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/examples/part_containment_rdr/robot_contained_objects_mcrdr.py +0 -0
  66. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/examples/part_containment_rdr/robot_contained_objects_mcrdr_defs.py +0 -0
  67. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/examples/relational_example.py +0 -0
  68. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/images/scrdr.dot +0 -0
  69. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/images/scrdr.png +0 -0
  70. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/images/thinking_pr2.jpg +0 -0
  71. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/pyproject.toml +0 -0
  72. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/pytest.ini +0 -0
  73. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/requirements-dev-ci.txt +0 -0
  74. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/requirements-dev.txt +0 -0
  75. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/requirements-gui.txt +0 -0
  76. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/requirements-viz.txt +0 -0
  77. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_mcrdr_extra.dot +0 -0
  78. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_mcrdr_extra.png +0 -0
  79. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_mcrdr_stop_only.dot +0 -0
  80. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_mcrdr_stop_only.png +0 -0
  81. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_mcrdr_stop_plus_rule.dot +0 -0
  82. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_mcrdr_stop_plus_rule.png +0 -0
  83. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_scrdr.dot +0 -0
  84. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_scrdr.png +0 -0
  85. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_scrdr_2.dot +0 -0
  86. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_scrdr_2.png +0 -0
  87. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_scrdr_3.dot +0 -0
  88. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/complete_scrdr_3.png +0 -0
  89. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/grdr_Habitat.dot +0 -0
  90. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/grdr_Habitat.png +0 -0
  91. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/grdr_Species.dot +0 -0
  92. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/grdr_Species.png +0 -0
  93. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/mcrdr_extra.dot +0 -0
  94. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/mcrdr_extra.png +0 -0
  95. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/mcrdr_extra_classify.dot +0 -0
  96. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/mcrdr_extra_classify.png +0 -0
  97. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/mcrdr_stop_plus_rule_combined.dot +0 -0
  98. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/mcrdr_stop_plus_rule_combined.png +0 -0
  99. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/partial_mcrdr_extra.dot +0 -0
  100. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/partial_mcrdr_extra.png +0 -0
  101. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/relational_scrdr_classify.dot +0 -0
  102. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/results/relational_scrdr_classify.png +0 -0
  103. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/setup.cfg +0 -0
  104. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/datastructures/__init__.py +0 -0
  105. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/datastructures/callable_expression.py +0 -0
  106. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/datastructures/enums.py +0 -0
  107. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/experts.py +0 -0
  108. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/start-code-server.sh +0 -0
  109. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/user_interface/__init__.py +0 -0
  110. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules/user_interface/prompt.py +0 -0
  111. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules.egg-info/dependency_links.txt +0 -0
  112. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/src/ripple_down_rules.egg-info/top_level.txt +0 -0
  113. {ripple_down_rules-0.5.63/test/test_generated_rdrs → ripple_down_rules-0.5.71/test/conf}/__init__.py +0 -0
  114. {ripple_down_rules-0.5.63/test/test_helpers → ripple_down_rules-0.5.71/test/conf/world}/__init__.py +0 -0
  115. {ripple_down_rules-0.5.63/test/test_user_interface → ripple_down_rules-0.5.71/test/factories}/__init__.py +0 -0
  116. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/correct_drawer_rdr_expert_answers_fit.json +0 -0
  117. ripple_down_rules-0.5.63/test/test_expert_answers/view_rdr_expert_answers_fit.json → ripple_down_rules-0.5.71/test/test_expert_answers/drawer_cabinet_expert_answers_fit.json +0 -0
  118. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/grdr_expert_answers_classify.json +0 -0
  119. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/grdr_expert_answers_fit.json +0 -0
  120. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/grdr_expert_answers_fit_extra.json +0 -0
  121. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/grdr_expert_answers_fit_no_targets.json +0 -0
  122. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/mcrdr_expert_answers_classify.json +0 -0
  123. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/mcrdr_expert_answers_fit_no_targets.json +0 -0
  124. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/mcrdr_expert_answers_stop_only_fit.json +0 -0
  125. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/mcrdr_extra_expert_answers_classify.json +0 -0
  126. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/mcrdr_extra_expert_answers_fit.json +0 -0
  127. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/mcrdr_multi_line_expert_answers_fit.json +0 -0
  128. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/mcrdr_stop_only_answers_fit.json +0 -0
  129. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/mcrdr_stop_plus_rule_answers_fit.json +0 -0
  130. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/mcrdr_stop_plus_rule_combined_expert_answers_fit.json +0 -0
  131. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/mcrdr_stop_plus_rule_expert_answers_fit.json +0 -0
  132. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/mutagenic_expert_answers.json +0 -0
  133. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/relational_scrdr_expert_answers_classify.json +0 -0
  134. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/scrdr_expert_answers_classify.json +0 -0
  135. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/scrdr_expert_answers_fit.json +0 -0
  136. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/scrdr_expert_answers_fit_no_targets.json +0 -0
  137. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/scrdr_multi_line_expert_answers_fit.json +0 -0
  138. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_expert_answers/scrdr_world_expert_answers_fit.json +0 -0
  139. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_helpers/object_diagram_case_query.png +0 -0
  140. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_helpers/object_diagram_person.png +0 -0
  141. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_json_serialization.py +0 -0
  142. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_object_diagram.py +0 -0
  143. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_rdr_alchemy.py +0 -0
  144. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_rdr_decorators.py +0 -0
  145. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_relational_rdr.py +0 -0
  146. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_relational_rdr_alchemy.py +0 -0
  147. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_results/datasets_physical_object_is_a_robot/__init__.py +0 -0
  148. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_output__scrdr_defs.py +1 -1
  149. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/__init__.py +0 -0
  150. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/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
  151. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_sql_model.py +0 -0
  152. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_user_interface/test_ipython.py +0 -0
  153. {ripple_down_rules-0.5.63 → ripple_down_rules-0.5.71}/test/test_user_interface/test_ipython_copilot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.5.63
3
+ Version: 0.5.71
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
@@ -694,6 +694,7 @@ Requires-Dist: pygments
694
694
  Requires-Dist: sqlalchemy
695
695
  Requires-Dist: pandas
696
696
  Requires-Dist: pyparsing>=3.2.3
697
+ Requires-Dist: omegaconf
697
698
  Provides-Extra: viz
698
699
  Requires-Dist: networkx>=3.1; extra == "viz"
699
700
  Requires-Dist: matplotlib>=3.7.5; extra == "viz"
@@ -1,5 +1,6 @@
1
1
  from typing_extensions import Dict, Any
2
- from ripple_down_rules.rdr import GeneralRDR
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 GeneralRDR._classify(classifiers_dict, case)
16
+ return general_rdr_classify(classifiers_dict, case)
@@ -10,4 +10,5 @@ colorama
10
10
  pygments
11
11
  sqlalchemy
12
12
  pandas
13
- pyparsing>=3.2.3
13
+ pyparsing>=3.2.3
14
+ omegaconf
@@ -1,4 +1,4 @@
1
- __version__ = "0.5.63"
1
+ __version__ = "0.5.71"
2
2
 
3
3
  import logging
4
4
  logger = logging.Logger("rdr")
@@ -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
- _type = type(list(values)[0]) if len(values) > 0 else get_value_type_from_type_hint(name, obj)
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
- setattr(case_attr, sub_attr, val)
323
+ try:
324
+ setattr(case_attr, sub_attr, val)
325
+ except AttributeError:
326
+ pass
321
327
  return case_attr
322
328
 
323
329
 
@@ -5,9 +5,11 @@ import typing
5
5
  from dataclasses import dataclass, field
6
6
 
7
7
  import typing_extensions
8
+ from omegaconf import MISSING
8
9
  from sqlalchemy.orm import DeclarativeBase as SQLTable
9
- from typing_extensions import Any, Optional, Dict, Type, Tuple, Union, List, get_origin, Set
10
+ from typing_extensions import Any, Optional, Dict, Type, Tuple, Union, List, get_origin, Set, Callable
10
11
 
12
+ from ..utils import get_method_name, get_function_import_path_and_representation
11
13
  from .callable_expression import CallableExpression
12
14
  from .case import create_case, Case
13
15
  from ..utils import copy_case, make_list, make_set, get_origin_and_args_from_type_hint, get_value_type_from_type_hint, \
@@ -37,6 +39,24 @@ class CaseQuery:
37
39
  """
38
40
  Whether the attribute can only take one value (i.e. True) or multiple values (i.e. False).
39
41
  """
42
+ case_factory: Optional[Callable[[], Any]] = None
43
+ """
44
+ The factory method that can be used to recreate the original case.
45
+ """
46
+ case_factory_idx: Optional[int] = None
47
+ """
48
+ This is used when the case factory is a list of cases, this index is used to select the case from the list.
49
+ """
50
+ case_conf: Optional[CaseConf] = None
51
+ """
52
+ The case configuration that is used to (re)create the original case, recommended to be used when you want to
53
+ the case to persist in the rule base, this would allow it to be used for merging with other similar conclusion RDRs.
54
+ """
55
+ scenario: Optional[Callable] = None
56
+ """
57
+ The executable scenario is the root callable that recreates the situation that the case is
58
+ created in, for example, when the case is created from a test function, this would be the test function itself.
59
+ """
40
60
  _target: Optional[CallableExpression] = None
41
61
  """
42
62
  The target expression of the attribute.
@@ -95,7 +115,7 @@ class CaseQuery:
95
115
  """
96
116
  if self._case is not None:
97
117
  return self._case
98
- elif not isinstance(self.original_case, (Case, SQLTable)):
118
+ elif not isinstance(self.original_case, Case):
99
119
  self._case = create_case(self.original_case, max_recursion_idx=3)
100
120
  else:
101
121
  self._case = self.original_case
@@ -225,4 +245,43 @@ class CaseQuery:
225
245
  self.mutually_exclusive, _target=self.target, default_value=self.default_value,
226
246
  scope=self.scope, _case=copy_case(self.case), _target_value=self.target_value,
227
247
  conditions=self.conditions, is_function=self.is_function,
228
- function_args_type_hints=self.function_args_type_hints)
248
+ function_args_type_hints=self.function_args_type_hints,
249
+ case_factory=self.case_factory, case_factory_idx=self.case_factory_idx,
250
+ case_conf=self.case_conf, scenario=self.scenario)
251
+
252
+
253
+ @dataclass
254
+ class CaseConf:
255
+ factory_method: Callable[[Any], Any] = MISSING
256
+
257
+ def create(self) -> Any:
258
+ return self.factory_method()
259
+
260
+
261
+ @dataclass
262
+ class CaseFactoryMetaData:
263
+ factory_method: Optional[Callable[[Optional[CaseConf]], Any]] = None
264
+ factory_idx: Optional[int] = None
265
+ case_conf: Optional[CaseConf] = None
266
+ scenario: Optional[Callable] = None
267
+
268
+ @classmethod
269
+ def from_case_query(cls, case_query: CaseQuery) -> CaseFactoryMetaData:
270
+ return cls(factory_method=case_query.case_factory, factory_idx=case_query.case_factory_idx,
271
+ case_conf=case_query.case_conf, scenario=case_query.scenario)
272
+
273
+ def __repr__(self):
274
+ factory_method_repr = None
275
+ scenario_repr = None
276
+ if self.factory_method is not None:
277
+ _, factory_method_repr = get_function_import_path_and_representation(self.factory_method)
278
+ if self.scenario is not None:
279
+ _, scenario_repr = get_function_import_path_and_representation(self.scenario)
280
+ return (f"CaseFactoryMetaData("
281
+ f"factory_method={factory_method_repr}, "
282
+ f"factory_idx={self.factory_idx}, "
283
+ f"case_conf={self.case_conf},"
284
+ f" scenario={scenario_repr})")
285
+
286
+ def __str__(self):
287
+ return self.__repr__()
@@ -0,0 +1,97 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from types import ModuleType
5
+
6
+ from ripple_down_rules.datastructures.dataclasses import CaseFactoryMetaData
7
+
8
+ from .datastructures.case import create_case
9
+ from .datastructures.dataclasses import CaseQuery
10
+ from typing_extensions import Type, Optional, Callable, Any, Dict, TYPE_CHECKING, Union
11
+
12
+ from .utils import get_func_rdr_model_name, copy_case, make_set, update_case
13
+ from .utils import calculate_precision_and_recall
14
+
15
+ if TYPE_CHECKING:
16
+ from .rdr import RippleDownRules
17
+
18
+
19
+ def general_rdr_classify(classifiers_dict: Dict[str, Union[ModuleType, RippleDownRules]],
20
+ case: Any, modify_original_case: bool = False,
21
+ case_query: Optional[CaseQuery] = None) -> Dict[str, Any]:
22
+ """
23
+ Classify a case by going through all classifiers and adding the categories that are classified,
24
+ and then restarting the classification until no more categories can be added.
25
+
26
+ :param classifiers_dict: A dictionary mapping conclusion types to the classifiers that produce them.
27
+ :param case: The case to classify.
28
+ :param modify_original_case: Whether to modify the original case or create a copy and modify it.
29
+ :param case_query: The case query to extract metadata from if needed.
30
+ :return: The categories that the case belongs to.
31
+ """
32
+ conclusions = {}
33
+ case = create_case(case)
34
+ case_cp = copy_case(case) if not modify_original_case else case
35
+ while True:
36
+ new_conclusions = {}
37
+ for attribute_name, rdr in classifiers_dict.items():
38
+ pred_atts = rdr.classify(case_cp, case_query=case_query)
39
+ if pred_atts is None:
40
+ continue
41
+ if rdr.mutually_exclusive:
42
+ if attribute_name not in conclusions or \
43
+ (attribute_name in conclusions and conclusions[attribute_name] != pred_atts):
44
+ conclusions[attribute_name] = pred_atts
45
+ new_conclusions[attribute_name] = pred_atts
46
+ else:
47
+ pred_atts = make_set(pred_atts)
48
+ if attribute_name in conclusions:
49
+ pred_atts = {p for p in pred_atts if p not in conclusions[attribute_name]}
50
+ if len(pred_atts) > 0:
51
+ new_conclusions[attribute_name] = pred_atts
52
+ if attribute_name not in conclusions:
53
+ conclusions[attribute_name] = set()
54
+ conclusions[attribute_name].update(pred_atts)
55
+ if attribute_name in new_conclusions:
56
+ temp_case_query = CaseQuery(case_cp, attribute_name, rdr.conclusion_type, rdr.mutually_exclusive)
57
+ update_case(temp_case_query, new_conclusions)
58
+ if len(new_conclusions) == 0:
59
+ break
60
+ return conclusions
61
+
62
+
63
+ def is_matching(classifier: Callable[[Any], Any], case_query: CaseQuery, pred_cat: Optional[Dict[str, Any]] = None) -> bool:
64
+ """
65
+ :param classifier: The RDR classifier to check the prediction of.
66
+ :param case_query: The case query to check.
67
+ :param pred_cat: The predicted category.
68
+ :return: Whether the classifier prediction is matching case_query target or not.
69
+ """
70
+ if case_query.target is None:
71
+ return False
72
+ if pred_cat is None:
73
+ pred_cat = classifier(case_query.case)
74
+ if not isinstance(pred_cat, dict):
75
+ pred_cat = {case_query.attribute_name: pred_cat}
76
+ target = {case_query.attribute_name: case_query.target_value}
77
+ precision, recall = calculate_precision_and_recall(pred_cat, target)
78
+ return all(recall) and all(precision)
79
+
80
+
81
+ def load_or_create_func_rdr_model(func, model_dir: str, rdr_type: Type[RippleDownRules],
82
+ **rdr_kwargs) -> RippleDownRules:
83
+ """
84
+ Load the RDR model of the function if it exists, otherwise create a new one.
85
+
86
+ :param func: The function to load the model for.
87
+ :param model_dir: The directory where the model is stored.
88
+ :param rdr_type: The type of the RDR model to load.
89
+ :param rdr_kwargs: Additional arguments to pass to the RDR constructor in the case of a new model.
90
+ """
91
+ model_name = get_func_rdr_model_name(func)
92
+ model_path = os.path.join(model_dir, model_name, "rdr_metadata", f"{model_name}.json")
93
+ if os.path.exists(model_path):
94
+ rdr = rdr_type.load(load_dir=model_dir, model_name=model_name)
95
+ else:
96
+ rdr = rdr_type(**rdr_kwargs)
97
+ return rdr