ripple-down-rules 0.6.19__tar.gz → 0.6.21__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.6.19 → ripple_down_rules-0.6.21}/PKG-INFO +1 -1
  2. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/scripts/live_dot_server.py +1 -5
  3. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/__init__.py +1 -1
  4. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/rdr.py +3 -2
  5. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/rdr_decorators.py +6 -1
  6. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/utils.py +264 -11
  7. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules.egg-info/PKG-INFO +1 -1
  8. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/.github/workflows/build_and_deploy_doc.yml +0 -0
  9. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/.github/workflows/ci.yml +0 -0
  10. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/.github/workflows/publish-to-test-pypi.yml +0 -0
  11. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/.gitignore +0 -0
  12. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/.idea/shelf/Uncommitted_changes_before_Checkout_at_2_4_25,_6_32_PM_[Changes]/shelved.patch +0 -0
  13. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/.idea/shelf/Uncommitted_changes_before_Checkout_at_2_4_25,_6_32_PM_[Changes]1/shelved.patch +0 -0
  14. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/LICENSE +0 -0
  15. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/README.md +0 -0
  16. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/doc/_config.yml +0 -0
  17. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/doc/_toc.yml +0 -0
  18. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/doc/bibliography.md +0 -0
  19. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/doc/intro.md +0 -0
  20. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/doc/references.bib +0 -0
  21. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/doc/requirements.txt +0 -0
  22. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/examples/__init__.py +0 -0
  23. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/examples/animal_species.py +0 -0
  24. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/examples/part_containment_rdr/__init__.py +0 -0
  25. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/examples/part_containment_rdr/rdr_metadata/part_containment_rdr.json +0 -0
  26. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/examples/part_containment_rdr/robot_contained_objects_mcrdr.py +0 -0
  27. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/examples/part_containment_rdr/robot_contained_objects_mcrdr_defs.py +0 -0
  28. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/examples/part_containment_rdr/robot_rdr.py +0 -0
  29. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/examples/relational_example.py +0 -0
  30. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/images/scrdr.dot +0 -0
  31. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/images/scrdr.png +0 -0
  32. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/images/thinking_pr2.jpg +0 -0
  33. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/pyproject.toml +0 -0
  34. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/pytest.ini +0 -0
  35. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/requirements-dev-ci.txt +0 -0
  36. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/requirements-dev.txt +0 -0
  37. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/requirements-gui.txt +0 -0
  38. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/requirements-viz.txt +0 -0
  39. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/requirements.txt +0 -0
  40. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_mcrdr_extra.dot +0 -0
  41. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_mcrdr_extra.png +0 -0
  42. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_mcrdr_stop_only.dot +0 -0
  43. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_mcrdr_stop_only.png +0 -0
  44. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_mcrdr_stop_plus_rule.dot +0 -0
  45. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_mcrdr_stop_plus_rule.png +0 -0
  46. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_scrdr.dot +0 -0
  47. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_scrdr.png +0 -0
  48. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_scrdr_2.dot +0 -0
  49. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_scrdr_2.png +0 -0
  50. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_scrdr_3.dot +0 -0
  51. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/complete_scrdr_3.png +0 -0
  52. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/grdr_Habitat.dot +0 -0
  53. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/grdr_Habitat.png +0 -0
  54. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/grdr_Species.dot +0 -0
  55. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/grdr_Species.png +0 -0
  56. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/mcrdr_extra.dot +0 -0
  57. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/mcrdr_extra.png +0 -0
  58. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/mcrdr_extra_classify.dot +0 -0
  59. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/mcrdr_extra_classify.png +0 -0
  60. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/mcrdr_stop_plus_rule_combined.dot +0 -0
  61. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/mcrdr_stop_plus_rule_combined.png +0 -0
  62. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/partial_mcrdr_extra.dot +0 -0
  63. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/partial_mcrdr_extra.png +0 -0
  64. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/relational_scrdr_classify.dot +0 -0
  65. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/results/relational_scrdr_classify.png +0 -0
  66. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/setup.cfg +0 -0
  67. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/datastructures/__init__.py +0 -0
  68. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/datastructures/callable_expression.py +0 -0
  69. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/datastructures/case.py +0 -0
  70. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/datastructures/dataclasses.py +0 -0
  71. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/datastructures/enums.py +0 -0
  72. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/experts.py +0 -0
  73. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/helpers.py +0 -0
  74. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/rules.py +0 -0
  75. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/start-code-server.sh +0 -0
  76. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/user_interface/__init__.py +0 -0
  77. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/user_interface/gui.py +0 -0
  78. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/user_interface/ipython_custom_shell.py +0 -0
  79. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/user_interface/object_diagram.py +0 -0
  80. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/user_interface/prompt.py +0 -0
  81. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules/user_interface/template_file_creator.py +0 -0
  82. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules.egg-info/SOURCES.txt +0 -0
  83. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules.egg-info/dependency_links.txt +0 -0
  84. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules.egg-info/requires.txt +0 -0
  85. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/src/ripple_down_rules.egg-info/top_level.txt +0 -0
  86. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/__init__.py +0 -0
  87. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/conf/__init__.py +0 -0
  88. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/conf/world/__init__.py +0 -0
  89. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/conf/world/base_config.py +0 -0
  90. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/conf/world/handles_and_containers.py +0 -0
  91. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/conftest.py +0 -0
  92. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/datasets.py +0 -0
  93. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/factories/__init__.py +0 -0
  94. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/factories/world/__init__.py +0 -0
  95. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/factories/world/handles_and_containers.py +0 -0
  96. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/correct_drawer_rdr_expert_answers_fit.json +0 -0
  97. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/drawer_cabinet_expert_answers_fit.json +0 -0
  98. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/grdr_expert_answers_classify.json +0 -0
  99. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/grdr_expert_answers_fit.json +0 -0
  100. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/grdr_expert_answers_fit_extra.json +0 -0
  101. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/grdr_expert_answers_fit_no_targets.json +0 -0
  102. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/mcrdr_expert_answers_classify.json +0 -0
  103. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/mcrdr_expert_answers_fit_no_targets.json +0 -0
  104. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/mcrdr_expert_answers_stop_only_fit.json +0 -0
  105. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/mcrdr_extra_expert_answers_classify.json +0 -0
  106. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/mcrdr_extra_expert_answers_fit.json +0 -0
  107. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/mcrdr_multi_line_expert_answers_fit.json +0 -0
  108. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/mcrdr_stop_only_answers_fit.json +0 -0
  109. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/mcrdr_stop_plus_rule_answers_fit.json +0 -0
  110. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/mcrdr_stop_plus_rule_combined_expert_answers_fit.json +0 -0
  111. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/mcrdr_stop_plus_rule_expert_answers_fit.json +0 -0
  112. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/mutagenic_expert_answers.json +0 -0
  113. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/relational_scrdr_expert_answers_classify.json +0 -0
  114. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/scrdr_expert_answers_classify.json +0 -0
  115. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/scrdr_expert_answers_fit.json +0 -0
  116. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/scrdr_expert_answers_fit_no_targets.json +0 -0
  117. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/scrdr_multi_line_expert_answers_fit.json +0 -0
  118. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_expert_answers/scrdr_world_expert_answers_fit.json +0 -0
  119. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_generated_rdrs/__init__.py +0 -0
  120. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_helpers/__init__.py +0 -0
  121. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_helpers/helpers.py +0 -0
  122. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_helpers/object_diagram_case_query.png +0 -0
  123. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_helpers/object_diagram_person.png +0 -0
  124. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_json_serialization.py +0 -0
  125. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_object_diagram.py +0 -0
  126. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_on_mutagenic.py +0 -0
  127. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_rdr.py +0 -0
  128. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_rdr_alchemy.py +0 -0
  129. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_rdr_decorators.py +0 -0
  130. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_rdr_helpers_rdrs.py +0 -0
  131. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_rdr_world/__init__.py +0 -0
  132. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_rdr_world/conftest.py +0 -0
  133. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_rdr_world/test_rdr_world.py +0 -0
  134. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_relational_rdr.py +0 -0
  135. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_relational_rdr_alchemy.py +0 -0
  136. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_results/datasets_physical_object_is_a_robot/__init__.py +0 -0
  137. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_output__scrdr.py +0 -0
  138. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_output__scrdr_defs.py +0 -0
  139. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_results/datasets_physical_object_is_a_robot/physical_object_is_a_robot_rdr.py +0 -0
  140. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_results/datasets_physical_object_is_a_robot/rdr_metadata/datasets_physical_object_is_a_robot.json +0 -0
  141. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/__init__.py +0 -0
  142. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/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 +0 -0
  143. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/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 +0 -0
  144. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_results/datasets_physical_object_select_objects_that_are_parts_of_robot/physical_object_select_objects_that_are_parts_of_robot_rdr.py +0 -0
  145. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/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 +0 -0
  146. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_sql_model.py +0 -0
  147. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_template_file_creator.py +0 -0
  148. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_user_interface/__init__.py +0 -0
  149. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_user_interface/test_ipython.py +0 -0
  150. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_user_interface/test_ipython_copilot.py +0 -0
  151. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_user_interface/test_prompt.py +0 -0
  152. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_user_interface/test_qt_gui_inline.py +0 -0
  153. {ripple_down_rules-0.6.19 → ripple_down_rules-0.6.21}/test/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.6.19
3
+ Version: 0.6.21
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
@@ -41,7 +41,7 @@ def generate_html(dot_output="graph.html"):
41
41
  }}
42
42
 
43
43
  fetchAndRender();
44
- setInterval(fetchAndRender, 2000); // Refresh every 2 seconds
44
+ setInterval(fetchAndRender, 50); // Refresh every 2 seconds
45
45
  </script>
46
46
  </body>
47
47
  </html>"""
@@ -82,8 +82,4 @@ if __name__ == "__main__":
82
82
  dot_file = sys.argv[1]
83
83
  port = int(sys.argv[2]) if len(sys.argv) > 2 else 8000
84
84
 
85
- if not os.path.exists(dot_file):
86
- print(f"Error: File '{dot_file}' does not exist.")
87
- sys.exit(1)
88
-
89
85
  serve_dot(dot_file, port)
@@ -1,4 +1,4 @@
1
- __version__ = "0.6.19"
1
+ __version__ = "0.6.21"
2
2
 
3
3
  import logging
4
4
  logger = logging.Logger("rdr")
@@ -110,7 +110,7 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
110
110
 
111
111
  def render_evaluated_rule_tree(self, filename: str) -> None:
112
112
  evaluated_rules = self.get_evaluated_rule_tree()
113
- if len(evaluated_rules) > 0:
113
+ if evaluated_rules is not None and len(evaluated_rules) > 0:
114
114
  render_tree(evaluated_rules[0], use_dot_exporter=True, filename=filename,
115
115
  only_nodes=evaluated_rules)
116
116
 
@@ -121,7 +121,8 @@ class RippleDownRules(SubclassJSONSerializer, ABC):
121
121
  :return: The evaluated rule tree.
122
122
  """
123
123
  if self.start_rule is None:
124
- raise ValueError("The start rule is not set. Please set the start rule before getting the evaluated rule tree.")
124
+ return
125
+ # raise ValueError("The start rule is not set. Please set the start rule before getting the evaluated rule tree.")
125
126
  evaluated_rule_tree = [r for r in [self.start_rule] + list(self.start_rule.descendants) if r.evaluated]
126
127
  return evaluated_rule_tree
127
128
 
@@ -75,6 +75,7 @@ class RDRDecorator:
75
75
  self.fitting_decorator = fitting_decorator if fitting_decorator is not None else \
76
76
  lambda f: f # Default to no fitting decorator
77
77
  self.generate_dot_file = generate_dot_file
78
+ self.not_none_output_found: bool = False
78
79
  self.load()
79
80
 
80
81
  def decorator(self, func: Callable) -> Callable:
@@ -117,7 +118,11 @@ class RDRDecorator:
117
118
  else:
118
119
  output = self.rdr.classify(case)
119
120
  if self.generate_dot_file:
120
- self.rdr.render_evaluated_rule_tree(self.rdr_models_dir + f'/{self.model_name}')
121
+ eval_rule_tree = self.rdr.get_evaluated_rule_tree()
122
+ if not self.not_none_output_found or (eval_rule_tree and len(eval_rule_tree) > 1):
123
+ self.rdr.render_evaluated_rule_tree(self.rdr_models_dir + f'/{self.model_name}')
124
+ if eval_rule_tree and len(eval_rule_tree) > 1:
125
+ self.not_none_output_found = True
121
126
 
122
127
  if self.output_name in output:
123
128
  return output[self.output_name]
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import builtins
4
+ import codecs
4
5
  import copyreg
5
6
  import importlib
6
7
  import json
@@ -16,9 +17,12 @@ from dataclasses import is_dataclass, fields
16
17
  from enum import Enum
17
18
  from os.path import dirname
18
19
  from pathlib import Path
20
+ from subprocess import check_call
21
+ from tempfile import NamedTemporaryFile
19
22
  from textwrap import dedent
20
23
  from types import NoneType
21
24
 
25
+ import six
22
26
  from sqlalchemy.exc import NoInspectionAvailable
23
27
 
24
28
  try:
@@ -1629,12 +1633,204 @@ def edge_attr_setter(parent, child):
1629
1633
  return ""
1630
1634
 
1631
1635
 
1632
- class FilteredDotExporter(DotExporter):
1633
- def __init__(self, root, include_nodes=None, **kwargs):
1636
+ _RE_ESC = re.compile(r'["\\]')
1637
+ class FilteredDotExporter(object):
1638
+
1639
+ def __init__(self, node, include_nodes=None, graph="digraph", name="tree", options=None,
1640
+ indent=4, nodenamefunc=None, nodeattrfunc=None,
1641
+ edgeattrfunc=None, edgetypefunc=None, maxlevel=None):
1642
+ """
1643
+ Dot Language Exporter.
1644
+
1645
+ Args:
1646
+ node (Node): start node.
1647
+
1648
+ Keyword Args:
1649
+ graph: DOT graph type.
1650
+
1651
+ name: DOT graph name.
1652
+
1653
+ options: list of options added to the graph.
1654
+
1655
+ indent (int): number of spaces for indent.
1656
+
1657
+ nodenamefunc: Function to extract node name from `node` object.
1658
+ The function shall accept one `node` object as
1659
+ argument and return the name of it.
1660
+
1661
+ nodeattrfunc: Function to decorate a node with attributes.
1662
+ The function shall accept one `node` object as
1663
+ argument and return the attributes.
1664
+
1665
+ edgeattrfunc: Function to decorate a edge with attributes.
1666
+ The function shall accept two `node` objects as
1667
+ argument. The first the node and the second the child
1668
+ and return the attributes.
1669
+
1670
+ edgetypefunc: Function to which gives the edge type.
1671
+ The function shall accept two `node` objects as
1672
+ argument. The first the node and the second the child
1673
+ and return the edge (i.e. '->').
1674
+
1675
+ maxlevel (int): Limit export to this number of levels.
1676
+
1677
+ >>> from anytree import Node
1678
+ >>> root = Node("root")
1679
+ >>> s0 = Node("sub0", parent=root, edge=2)
1680
+ >>> s0b = Node("sub0B", parent=s0, foo=4, edge=109)
1681
+ >>> s0a = Node("sub0A", parent=s0, edge="")
1682
+ >>> s1 = Node("sub1", parent=root, edge="")
1683
+ >>> s1a = Node("sub1A", parent=s1, edge=7)
1684
+ >>> s1b = Node("sub1B", parent=s1, edge=8)
1685
+ >>> s1c = Node("sub1C", parent=s1, edge=22)
1686
+ >>> s1ca = Node("sub1Ca", parent=s1c, edge=42)
1687
+
1688
+ .. note:: If the node names are not unqiue, see :any:`UniqueDotExporter`.
1689
+
1690
+ A directed graph:
1691
+
1692
+ >>> from anytree.exporter import DotExporter
1693
+ >>> for line in DotExporter(root):
1694
+ ... print(line)
1695
+ digraph tree {
1696
+ "root";
1697
+ "sub0";
1698
+ "sub0B";
1699
+ "sub0A";
1700
+ "sub1";
1701
+ "sub1A";
1702
+ "sub1B";
1703
+ "sub1C";
1704
+ "sub1Ca";
1705
+ "root" -> "sub0";
1706
+ "root" -> "sub1";
1707
+ "sub0" -> "sub0B";
1708
+ "sub0" -> "sub0A";
1709
+ "sub1" -> "sub1A";
1710
+ "sub1" -> "sub1B";
1711
+ "sub1" -> "sub1C";
1712
+ "sub1C" -> "sub1Ca";
1713
+ }
1714
+
1715
+ The resulting graph:
1716
+
1717
+ .. image:: ../static/dotexporter0.png
1718
+
1719
+ An undirected graph:
1720
+
1721
+ >>> def nodenamefunc(node):
1722
+ ... return '%s:%s' % (node.name, node.depth)
1723
+ >>> def edgeattrfunc(node, child):
1724
+ ... return 'label="%s:%s"' % (node.name, child.name)
1725
+ >>> def edgetypefunc(node, child):
1726
+ ... return '--'
1727
+ >>> from anytree.exporter import DotExporter
1728
+ >>> for line in DotExporter(root, graph="graph",
1729
+ ... nodenamefunc=nodenamefunc,
1730
+ ... nodeattrfunc=lambda node: "shape=box",
1731
+ ... edgeattrfunc=edgeattrfunc,
1732
+ ... edgetypefunc=edgetypefunc):
1733
+ ... print(line)
1734
+ graph tree {
1735
+ "root:0" [shape=box];
1736
+ "sub0:1" [shape=box];
1737
+ "sub0B:2" [shape=box];
1738
+ "sub0A:2" [shape=box];
1739
+ "sub1:1" [shape=box];
1740
+ "sub1A:2" [shape=box];
1741
+ "sub1B:2" [shape=box];
1742
+ "sub1C:2" [shape=box];
1743
+ "sub1Ca:3" [shape=box];
1744
+ "root:0" -- "sub0:1" [label="root:sub0"];
1745
+ "root:0" -- "sub1:1" [label="root:sub1"];
1746
+ "sub0:1" -- "sub0B:2" [label="sub0:sub0B"];
1747
+ "sub0:1" -- "sub0A:2" [label="sub0:sub0A"];
1748
+ "sub1:1" -- "sub1A:2" [label="sub1:sub1A"];
1749
+ "sub1:1" -- "sub1B:2" [label="sub1:sub1B"];
1750
+ "sub1:1" -- "sub1C:2" [label="sub1:sub1C"];
1751
+ "sub1C:2" -- "sub1Ca:3" [label="sub1C:sub1Ca"];
1752
+ }
1753
+
1754
+ The resulting graph:
1755
+
1756
+ .. image:: ../static/dotexporter1.png
1757
+
1758
+ To export custom node implementations or :any:`AnyNode`, please provide a proper `nodenamefunc`:
1759
+
1760
+ >>> from anytree import AnyNode
1761
+ >>> root = AnyNode(id="root")
1762
+ >>> s0 = AnyNode(id="sub0", parent=root)
1763
+ >>> s0b = AnyNode(id="s0b", parent=s0)
1764
+ >>> s0a = AnyNode(id="s0a", parent=s0)
1765
+
1766
+ >>> from anytree.exporter import DotExporter
1767
+ >>> for line in DotExporter(root, nodenamefunc=lambda n: n.id):
1768
+ ... print(line)
1769
+ digraph tree {
1770
+ "root";
1771
+ "sub0";
1772
+ "s0b";
1773
+ "s0a";
1774
+ "root" -> "sub0";
1775
+ "sub0" -> "s0b";
1776
+ "sub0" -> "s0a";
1777
+ }
1778
+ """
1779
+ self.node = node
1780
+ self.graph = graph
1781
+ self.name = name
1782
+ self.options = options
1783
+ self.indent = indent
1784
+ self.nodenamefunc = nodenamefunc
1785
+ self.nodeattrfunc = nodeattrfunc
1786
+ self.edgeattrfunc = edgeattrfunc
1787
+ self.edgetypefunc = edgetypefunc
1788
+ self.maxlevel = maxlevel
1634
1789
  self.include_nodes = include_nodes
1635
- node_name_func = get_unique_node_names_func(root)
1790
+ node_name_func = get_unique_node_names_func(node)
1636
1791
  self.include_node_names = [node_name_func(n) for n in self.include_nodes] if include_nodes else None
1637
- super().__init__(root, **kwargs)
1792
+
1793
+ def __iter__(self):
1794
+ # prepare
1795
+ indent = " " * self.indent
1796
+ nodenamefunc = self.nodenamefunc or self._default_nodenamefunc
1797
+ nodeattrfunc = self.nodeattrfunc or self._default_nodeattrfunc
1798
+ edgeattrfunc = self.edgeattrfunc or self._default_edgeattrfunc
1799
+ edgetypefunc = self.edgetypefunc or self._default_edgetypefunc
1800
+ return self.__iter(indent, nodenamefunc, nodeattrfunc, edgeattrfunc,
1801
+ edgetypefunc)
1802
+
1803
+ @staticmethod
1804
+ def _default_nodenamefunc(node):
1805
+ return node.name
1806
+
1807
+ @staticmethod
1808
+ def _default_nodeattrfunc(node):
1809
+ return None
1810
+
1811
+ @staticmethod
1812
+ def _default_edgeattrfunc(node, child):
1813
+ return None
1814
+
1815
+ @staticmethod
1816
+ def _default_edgetypefunc(node, child):
1817
+ return "->"
1818
+
1819
+ def __iter(self, indent, nodenamefunc, nodeattrfunc, edgeattrfunc, edgetypefunc):
1820
+ yield "{self.graph} {self.name} {{".format(self=self)
1821
+ for option in self.__iter_options(indent):
1822
+ yield option
1823
+ for node in self.__iter_nodes(indent, nodenamefunc, nodeattrfunc):
1824
+ yield node
1825
+ for edge in self.__iter_edges(indent, nodenamefunc, edgeattrfunc, edgetypefunc):
1826
+ yield edge
1827
+ yield "}"
1828
+
1829
+ def __iter_options(self, indent):
1830
+ options = self.options
1831
+ if options:
1832
+ for option in options:
1833
+ yield "%s%s" % (indent, option)
1638
1834
 
1639
1835
  def __iter_nodes(self, indent, nodenamefunc, nodeattrfunc):
1640
1836
  for node in PreOrderIter(self.node, maxlevel=self.maxlevel):
@@ -1643,7 +1839,7 @@ class FilteredDotExporter(DotExporter):
1643
1839
  continue
1644
1840
  nodeattr = nodeattrfunc(node)
1645
1841
  nodeattr = " [%s]" % nodeattr if nodeattr is not None else ""
1646
- yield '%s"%s"%s;' % (indent, DotExporter.esc(nodename), nodeattr)
1842
+ yield '%s"%s"%s;' % (indent, FilteredDotExporter.esc(nodename), nodeattr)
1647
1843
 
1648
1844
  def __iter_edges(self, indent, nodenamefunc, edgeattrfunc, edgetypefunc):
1649
1845
  maxlevel = self.maxlevel - 1 if self.maxlevel else None
@@ -1653,11 +1849,67 @@ class FilteredDotExporter(DotExporter):
1653
1849
  continue
1654
1850
  for child in node.children:
1655
1851
  childname = nodenamefunc(child)
1852
+ if self.include_nodes is not None and childname not in self.include_node_names:
1853
+ continue
1656
1854
  edgeattr = edgeattrfunc(node, child)
1657
1855
  edgetype = edgetypefunc(node, child)
1658
1856
  edgeattr = " [%s]" % edgeattr if edgeattr is not None else ""
1659
- yield '%s"%s" %s "%s"%s;' % (indent, DotExporter.esc(nodename), edgetype,
1660
- DotExporter.esc(childname), edgeattr)
1857
+ yield '%s"%s" %s "%s"%s;' % (indent, FilteredDotExporter.esc(nodename), edgetype,
1858
+ FilteredDotExporter.esc(childname), edgeattr)
1859
+
1860
+ def to_dotfile(self, filename):
1861
+ """
1862
+ Write graph to `filename`.
1863
+
1864
+ >>> from anytree import Node
1865
+ >>> root = Node("root")
1866
+ >>> s0 = Node("sub0", parent=root)
1867
+ >>> s0b = Node("sub0B", parent=s0)
1868
+ >>> s0a = Node("sub0A", parent=s0)
1869
+ >>> s1 = Node("sub1", parent=root)
1870
+ >>> s1a = Node("sub1A", parent=s1)
1871
+ >>> s1b = Node("sub1B", parent=s1)
1872
+ >>> s1c = Node("sub1C", parent=s1)
1873
+ >>> s1ca = Node("sub1Ca", parent=s1c)
1874
+
1875
+ >>> from anytree.exporter import DotExporter
1876
+ >>> DotExporter(root).to_dotfile("tree.dot")
1877
+
1878
+ The generated file should be handed over to the `dot` tool from the
1879
+ http://www.graphviz.org/ package::
1880
+
1881
+ $ dot tree.dot -T png -o tree.png
1882
+ """
1883
+ with codecs.open(filename, "w", "utf-8") as file:
1884
+ for line in self:
1885
+ file.write("%s\n" % line)
1886
+
1887
+ def to_picture(self, filename):
1888
+ """
1889
+ Write graph to a temporary file and invoke `dot`.
1890
+
1891
+ The output file type is automatically detected from the file suffix.
1892
+
1893
+ *`graphviz` needs to be installed, before usage of this method.*
1894
+ """
1895
+ fileformat = os.path.splitext(filename)[1][1:]
1896
+ with NamedTemporaryFile("wb", delete=False) as dotfile:
1897
+ dotfilename = dotfile.name
1898
+ for line in self:
1899
+ dotfile.write(("%s\n" % line).encode("utf-8"))
1900
+ dotfile.flush()
1901
+ cmd = ["dot", dotfilename, "-T", fileformat, "-o", filename]
1902
+ check_call(cmd)
1903
+ try:
1904
+ os.remove(dotfilename)
1905
+ except Exception: # pragma: no cover
1906
+ msg = 'Could not remove temporary file %s' % dotfilename
1907
+ logging.getLogger(__name__).warn(msg)
1908
+
1909
+ @staticmethod
1910
+ def esc(value):
1911
+ """Escape Strings."""
1912
+ return _RE_ESC.sub(lambda m: r"\%s" % m.group(0), six.text_type(value))
1661
1913
 
1662
1914
 
1663
1915
  def render_tree(root: Node, use_dot_exporter: bool = False,
@@ -1673,18 +1925,19 @@ def render_tree(root: Node, use_dot_exporter: bool = False,
1673
1925
  if not root:
1674
1926
  logging.warning("No rules to render")
1675
1927
  return
1676
- for pre, _, node in RenderTree(root):
1677
- print(f"{pre}{node.weight if hasattr(node, 'weight') and node.weight else ''} {node.__str__()}")
1928
+ # for pre, _, node in RenderTree(root):
1929
+ # print(f"{pre}{node.weight if hasattr(node, 'weight') and node.weight else ''} {node.__str__()}")
1678
1930
  if use_dot_exporter:
1679
1931
  unique_node_names = get_unique_node_names_func(root)
1680
1932
 
1681
1933
  de = FilteredDotExporter(root,
1682
1934
  include_nodes=only_nodes,
1683
1935
  nodenamefunc=unique_node_names,
1684
- edgeattrfunc=edge_attr_setter
1936
+ edgeattrfunc=edge_attr_setter,
1937
+ nodeattrfunc=lambda node: f'style=filled, fillcolor={"green" if node.fired else "red"}'
1685
1938
  )
1686
1939
  de.to_dotfile(f"{filename}{'.dot'}")
1687
- de.to_picture(f"{filename}{'.png'}")
1940
+ # de.to_picture(f"{filename}{'.png'}")
1688
1941
 
1689
1942
 
1690
1943
  def draw_tree(root: Node, fig: Figure):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.6.19
3
+ Version: 0.6.21
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