ripple-down-rules 0.2.0__tar.gz → 0.2.2__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 (32) hide show
  1. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/PKG-INFO +1 -1
  2. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/pyproject.toml +1 -1
  3. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/datastructures/callable_expression.py +10 -8
  4. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/datastructures/dataclasses.py +7 -0
  5. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/prompt.py +6 -6
  6. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/utils.py +14 -0
  7. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules.egg-info/PKG-INFO +1 -1
  8. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/test/test_rdr_alchemy.py +1 -1
  9. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/test/test_relational_rdr.py +2 -1
  10. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/test/test_relational_rdr_alchemy.py +2 -1
  11. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/LICENSE +0 -0
  12. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/README.md +0 -0
  13. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/setup.cfg +0 -0
  14. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/__init__.py +0 -0
  15. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/datasets.py +0 -0
  16. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/datastructures/__init__.py +0 -0
  17. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/datastructures/case.py +0 -0
  18. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/datastructures/enums.py +0 -0
  19. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/experts.py +0 -0
  20. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/failures.py +0 -0
  21. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/helpers.py +0 -0
  22. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/rdr.py +0 -0
  23. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/rdr_decorators.py +0 -0
  24. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules/rules.py +0 -0
  25. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules.egg-info/SOURCES.txt +0 -0
  26. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules.egg-info/dependency_links.txt +0 -0
  27. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/src/ripple_down_rules.egg-info/top_level.txt +0 -0
  28. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/test/test_json_serialization.py +0 -0
  29. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/test/test_on_mutagenic.py +0 -0
  30. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/test/test_rdr.py +0 -0
  31. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/test/test_rdr_world.py +0 -0
  32. {ripple_down_rules-0.2.0 → ripple_down_rules-0.2.2}/test/test_sql_model.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.2.0
3
+ Version: 0.2.2
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
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "ripple_down_rules"
9
- version = "0.2.0"
9
+ version = "0.2.2"
10
10
  description = "Implements the various versions of Ripple Down Rules (RDR) for knowledge representation and reasoning."
11
11
  readme = "README.md"
12
12
  authors = [{ name = "Abdelrhman Bassiouny", email = "abassiou@uni-bremen.de" }]
@@ -9,7 +9,8 @@ from typing_extensions import Type, Optional, Any, List, Union, Tuple, Dict, Set
9
9
 
10
10
  from .case import create_case, Case
11
11
  from ..utils import SubclassJSONSerializer, get_full_class_name, get_type_from_string, conclusion_to_json, is_iterable, \
12
- build_user_input_from_conclusion, encapsulate_user_input, extract_function_source
12
+ build_user_input_from_conclusion, encapsulate_user_input, extract_function_source, are_results_subclass_of_types, \
13
+ make_list
13
14
 
14
15
 
15
16
  class VariableVisitor(ast.NodeVisitor):
@@ -133,15 +134,16 @@ class CallableExpression(SubclassJSONSerializer):
133
134
  if output is None:
134
135
  output = scope['_get_value'](case)
135
136
  if self.conclusion_type is not None:
136
- if is_iterable(output) and not isinstance(output, self.conclusion_type):
137
- assert isinstance(list(output)[0], self.conclusion_type), (f"Expected output type {self.conclusion_type},"
138
- f" got {type(output)}")
139
- else:
140
- assert isinstance(output, self.conclusion_type), (f"Expected output type {self.conclusion_type},"
141
- f" got {type(output)}")
137
+ output_types = {type(o) for o in make_list(output)}
138
+ output_types.add(type(output))
139
+ if not are_results_subclass_of_types(output_types, self.conclusion_type):
140
+ raise ValueError(f"Not all result types {output_types} are subclasses of expected types"
141
+ f" {self.conclusion_type}")
142
142
  return output
143
- else:
143
+ elif self.conclusion is not None:
144
144
  return self.conclusion
145
+ else:
146
+ raise ValueError("Either user_input or conclusion must be provided.")
145
147
  except Exception as e:
146
148
  raise ValueError(f"Error during evaluation: {e}")
147
149
 
@@ -83,6 +83,13 @@ class CaseQuery:
83
83
  raise ValueError("The case must be a Case or SQLTable object.")
84
84
  self._case = value
85
85
 
86
+ @property
87
+ def core_attribute_type(self) -> Tuple[Type]:
88
+ """
89
+ :return: The core type of the attribute.
90
+ """
91
+ return (t for t in self.attribute_type if t not in (set, list))
92
+
86
93
  @property
87
94
  def attribute_type(self) -> Tuple[Type]:
88
95
  """
@@ -10,14 +10,15 @@ from textwrap import indent, dedent
10
10
  from IPython.core.magic import register_line_magic, line_magic, Magics, magics_class
11
11
  from IPython.terminal.embed import InteractiveShellEmbed
12
12
  from traitlets.config import Config
13
- from typing_extensions import List, Optional, Tuple, Dict, Type, Union
13
+ from typing_extensions import List, Optional, Tuple, Dict, Type, Union, Any
14
14
 
15
15
  from .datastructures.enums import PromptFor
16
16
  from .datastructures.case import Case
17
17
  from .datastructures.callable_expression import CallableExpression, parse_string_to_expression
18
18
  from .datastructures.dataclasses import CaseQuery
19
19
  from .utils import extract_dependencies, contains_return_statement, make_set, get_imports_from_scope, make_list, \
20
- get_import_from_type, get_imports_from_types, is_iterable, extract_function_source, encapsulate_user_input
20
+ get_import_from_type, get_imports_from_types, is_iterable, extract_function_source, encapsulate_user_input, \
21
+ are_results_subclass_of_types
21
22
 
22
23
 
23
24
  @magics_class
@@ -259,7 +260,6 @@ class IPythonShell:
259
260
  if len(self.all_code_lines) == 1 and self.all_code_lines[0].strip() == '':
260
261
  self.user_input = None
261
262
  else:
262
- import pdb; pdb.set_trace()
263
263
  self.user_input = '\n'.join(self.all_code_lines)
264
264
  self.user_input = encapsulate_user_input(self.user_input, self.shell.my_magics.function_signature,
265
265
  self.func_doc)
@@ -278,6 +278,7 @@ def prompt_user_for_expression(case_query: CaseQuery, prompt_for: PromptFor, pro
278
278
  :return: A callable expression that takes a case and executes user expression on it.
279
279
  """
280
280
  prev_user_input: Optional[str] = None
281
+ callable_expression: Optional[CallableExpression] = None
281
282
  while True:
282
283
  user_input, expression_tree = prompt_user_about_case(case_query, prompt_for, prompt_str,
283
284
  code_to_modify=prev_user_input)
@@ -294,9 +295,8 @@ def prompt_user_for_expression(case_query: CaseQuery, prompt_for: PromptFor, pro
294
295
  scope=case_query.scope)
295
296
  try:
296
297
  result = callable_expression(case_query.case)
297
- result = make_list(result)
298
- if len(result) == 0:
299
- print(f"The given expression gave an empty result for case {case_query.name}, please modify")
298
+ if len(make_list(result)) == 0:
299
+ print(f"The given expression gave an empty result for case {case_query.name}. Please modify!")
300
300
  continue
301
301
  break
302
302
  except Exception as e:
@@ -36,6 +36,20 @@ import ast
36
36
  matplotlib.use("Qt5Agg") # or "Qt5Agg", depending on availability
37
37
 
38
38
 
39
+ def are_results_subclass_of_types(result_types: List[Any], types_: List[Type]) -> bool:
40
+ """
41
+ Check if all results are subclasses of the given types.
42
+
43
+ :param result_types: The list of result types to check.
44
+ :param types_: The list of types to check against.
45
+ :return: True if all results are subclasses of the given types, False otherwise.
46
+ """
47
+ for rt in result_types:
48
+ if not any(issubclass(rt, t) for t in types_):
49
+ return False
50
+ return True
51
+
52
+
39
53
  def get_imports_from_types(types: List[Type]) -> List[str]:
40
54
  """
41
55
  Get the import statements for a list of types.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.2.0
3
+ Version: 0.2.2
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
@@ -128,7 +128,7 @@ class TestAlchemyRDR(TestCase):
128
128
  case_queries = []
129
129
  for case, targets in zip(self.all_cases[:n], habitat_targets):
130
130
  for attr, target in targets.items():
131
- case_queries.append(CaseQuery(case, attr, (Species,) if attr == 'species' else (Habitat,),
131
+ case_queries.append(CaseQuery(case, attr, (Species,) if attr == 'species' else (HabitatTable,),
132
132
  True if attr == 'species' else False, _target=target))
133
133
  grdr.fit(case_queries, expert=expert, animate_tree=draw_tree)
134
134
  for rule in grdr.start_rules:
@@ -5,6 +5,7 @@ from unittest import TestCase
5
5
 
6
6
  from typing_extensions import List, Optional, Any
7
7
 
8
+ from ripple_down_rules.datastructures.case import CaseAttribute
8
9
  from ripple_down_rules.datastructures.dataclasses import CaseQuery, CallableExpression
9
10
  from ripple_down_rules.experts import Human
10
11
  from ripple_down_rules.rdr import SingleClassRDR
@@ -103,7 +104,7 @@ class RelationalRDRTestCase(TestCase):
103
104
 
104
105
  def test_parse_relational_conclusions(self):
105
106
  user_input = "case.parts.contained_objects"
106
- conclusion = CallableExpression(user_input, list)
107
+ conclusion = CallableExpression(user_input, (CaseAttribute, PhysicalObject,))
107
108
  print(conclusion)
108
109
  print(conclusion(self.robot))
109
110
  assert conclusion(self.robot) == self.target
@@ -7,6 +7,7 @@ from sqlalchemy import ForeignKey
7
7
  from sqlalchemy.orm import MappedAsDataclass, DeclarativeBase, declared_attr, Mapped, mapped_column, relationship
8
8
  from typing_extensions import List, Any, Set
9
9
 
10
+ from ripple_down_rules.datastructures.case import CaseAttribute
10
11
  from ripple_down_rules.datastructures.dataclasses import CaseQuery
11
12
  from ripple_down_rules.datastructures.callable_expression import CallableExpression
12
13
  from ripple_down_rules.experts import Human
@@ -170,7 +171,7 @@ class RelationalRDRTestCase(TestCase):
170
171
 
171
172
  def test_parse_relational_conclusions(self):
172
173
  user_input = "case.parts.contained_objects"
173
- conclusion = CallableExpression(user_input, list)
174
+ conclusion = CallableExpression(user_input, (CaseAttribute, PhysicalObject,))
174
175
  print(conclusion)
175
176
  print(conclusion(self.robot))
176
177
  assert conclusion(self.robot) == self.target