ripple-down-rules 0.1.66__py3-none-any.whl → 0.1.67__py3-none-any.whl

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.
@@ -125,7 +125,7 @@ class HabitatTable(MappedAsDataclass, Base):
125
125
  return hash(self.habitat)
126
126
 
127
127
  def __str__(self):
128
- return self.habitat.value
128
+ return f"{HabitatTable.__name__}({Habitat.__name__}.{self.habitat.name})"
129
129
 
130
130
  def __repr__(self):
131
131
  return self.__str__()
@@ -3,11 +3,13 @@ from __future__ import annotations
3
3
  import ast
4
4
  import logging
5
5
  from _ast import AST
6
+ from enum import Enum
6
7
 
7
8
  from typing_extensions import Type, Optional, Any, List, Union, Tuple, Dict, Set
8
9
 
9
10
  from .case import create_case, Case
10
- from ..utils import SubclassJSONSerializer, get_full_class_name, get_type_from_string, conclusion_to_json, is_iterable
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
11
13
 
12
14
 
13
15
  class VariableVisitor(ast.NodeVisitor):
@@ -88,6 +90,7 @@ class CallableExpression(SubclassJSONSerializer):
88
90
  """
89
91
  A callable that is constructed from a string statement written by an expert.
90
92
  """
93
+ encapsulating_function: str = "def _get_value(case):"
91
94
 
92
95
  def __init__(self, user_input: Optional[str] = None, conclusion_type: Optional[Tuple[Type]] = None,
93
96
  expression_tree: Optional[AST] = None,
@@ -103,8 +106,10 @@ class CallableExpression(SubclassJSONSerializer):
103
106
  """
104
107
  if user_input is None and conclusion is None:
105
108
  raise ValueError("Either user_input or conclusion must be provided.")
109
+ if user_input is None:
110
+ user_input = build_user_input_from_conclusion(conclusion)
106
111
  self.conclusion: Optional[Any] = conclusion
107
- self.user_input: str = user_input
112
+ self.user_input: str = encapsulate_user_input(user_input, self.encapsulating_function)
108
113
  if conclusion_type is not None:
109
114
  if is_iterable(conclusion_type):
110
115
  conclusion_type = tuple(conclusion_type)
@@ -112,12 +117,11 @@ class CallableExpression(SubclassJSONSerializer):
112
117
  conclusion_type = (conclusion_type,)
113
118
  self.conclusion_type = conclusion_type
114
119
  self.scope: Optional[Dict[str, Any]] = scope if scope is not None else {}
115
- if conclusion is None:
116
- self.scope = get_used_scope(self.user_input, self.scope)
117
- self.expression_tree: AST = expression_tree if expression_tree else parse_string_to_expression(self.user_input)
118
- self.code = compile_expression_to_code(self.expression_tree)
119
- self.visitor = VariableVisitor()
120
- self.visitor.visit(self.expression_tree)
120
+ self.scope = get_used_scope(self.user_input, self.scope)
121
+ self.expression_tree: AST = expression_tree if expression_tree else parse_string_to_expression(self.user_input)
122
+ self.code = compile_expression_to_code(self.expression_tree)
123
+ self.visitor = VariableVisitor()
124
+ self.visitor.visit(self.expression_tree)
121
125
 
122
126
  def __call__(self, case: Any, **kwargs) -> Any:
123
127
  try:
@@ -145,7 +149,11 @@ class CallableExpression(SubclassJSONSerializer):
145
149
  """
146
150
  Combine this callable expression with another callable expression using the 'and' operator.
147
151
  """
148
- new_user_input = f"({self.user_input}) and ({other.user_input})"
152
+ cond1_user_input = self.user_input.replace(self.encapsulating_function, "def _cond1(case):")
153
+ cond2_user_input = other.user_input.replace(self.encapsulating_function, "def _cond2(case):")
154
+ new_user_input = (f"{cond1_user_input}\n"
155
+ f"{cond2_user_input}\n"
156
+ f"return _cond1(case) and _cond2(case)")
149
157
  return CallableExpression(new_user_input, conclusion_type=self.conclusion_type)
150
158
 
151
159
  def __eq__(self, other):
@@ -120,7 +120,7 @@ class CaseQuery:
120
120
  """
121
121
  :return: The target expression of the attribute.
122
122
  """
123
- if self._target is not None and not isinstance(self._target, CallableExpression):
123
+ if (self._target is not None) and (not isinstance(self._target, CallableExpression)):
124
124
  self._target = CallableExpression(conclusion=self._target, conclusion_type=self.attribute_type,
125
125
  scope=self.scope)
126
126
  return self._target
@@ -80,16 +80,10 @@ class IPythonShell:
80
80
  self.user_input = None
81
81
  else:
82
82
  self.all_code_lines = extract_dependencies(self.shell.all_lines)
83
- if len(self.all_code_lines) == 1:
84
- if self.all_code_lines[0].strip() == '':
85
- self.user_input = None
86
- else:
87
- self.user_input = self.all_code_lines[0].replace('return', '').strip()
83
+ if len(self.all_code_lines) == 1 and self.all_code_lines[0].strip() == '':
84
+ self.user_input = None
88
85
  else:
89
- self.user_input = f"def _get_value(case):\n "
90
- for cl in self.all_code_lines:
91
- sub_code_lines = cl.split('\n')
92
- self.user_input += '\n '.join(sub_code_lines) + '\n '
86
+ self.user_input = '\n'.join(self.all_code_lines)
93
87
 
94
88
 
95
89
  def prompt_user_for_expression(case_query: CaseQuery, prompt_for: PromptFor, prompt_str: Optional[str] = None)\
ripple_down_rules/rdr.py CHANGED
@@ -715,7 +715,7 @@ class GeneralRDR(RippleDownRules):
715
715
 
716
716
  @staticmethod
717
717
  def _classify(classifiers_dict: Dict[str, Union[ModuleType, RippleDownRules]],
718
- case: Any, modify_original_case: bool = True) -> Dict[str, Any]:
718
+ case: Any, modify_original_case: bool = False) -> Dict[str, Any]:
719
719
  """
720
720
  Classify a case by going through all classifiers and adding the categories that are classified,
721
721
  and then restarting the classification until no more categories can be added.
@@ -270,11 +270,11 @@ class SingleClassRule(Rule, HasAlternativeRule, HasRefinementRule):
270
270
 
271
271
  def _conclusion_source_code(self, conclusion: Any, parent_indent: str = "") -> Tuple[Optional[str], str]:
272
272
  conclusion = str(conclusion)
273
- indent = parent_indent + " " * 4
274
- if '\n' not in conclusion:
275
- return None, f"{indent}return {conclusion}\n"
276
- else:
277
- return get_rule_conclusion_as_source_code(self, conclusion, parent_indent=parent_indent)
273
+ # indent = parent_indent + " " * 4
274
+ # if '\n' not in conclusion:
275
+ # return None, f"{indent}return {conclusion}\n"
276
+ # else:
277
+ return get_rule_conclusion_as_source_code(self, conclusion, parent_indent=parent_indent)
278
278
 
279
279
  def _if_statement_source_code_clause(self) -> str:
280
280
  return "elif" if self.weight == RDREdge.Alternative.value else "if"
@@ -367,15 +367,15 @@ class MultiClassTopRule(Rule, HasRefinementRule, HasAlternativeRule):
367
367
  def _conclusion_source_code(self, conclusion: Any, parent_indent: str = "") -> Tuple[str, str]:
368
368
  conclusion_str = str(conclusion)
369
369
  indent = parent_indent + " " * 4
370
- if '\n' not in conclusion_str:
371
- func = None
372
- if is_iterable(conclusion):
373
- conclusion_str = "{" + ", ".join([str(c) for c in conclusion]) + "}"
374
- else:
375
- conclusion_str = "{" + str(conclusion) + "}"
376
- else:
377
- func, func_call = get_rule_conclusion_as_source_code(self, conclusion_str, parent_indent=parent_indent)
378
- conclusion_str = func_call.replace("return ", "").strip()
370
+ # if '\n' not in conclusion_str:
371
+ # func = None
372
+ # if is_iterable(conclusion):
373
+ # conclusion_str = "{" + ", ".join([str(c) for c in conclusion]) + "}"
374
+ # else:
375
+ # conclusion_str = "{" + str(conclusion) + "}"
376
+ # else:
377
+ func, func_call = get_rule_conclusion_as_source_code(self, conclusion_str, parent_indent=parent_indent)
378
+ conclusion_str = func_call.replace("return ", "").strip()
379
379
 
380
380
  statement = f"{indent}conclusions.update(make_set({conclusion_str}))\n"
381
381
  if self.alternative is None:
@@ -10,6 +10,7 @@ import re
10
10
  from collections import UserDict
11
11
  from copy import deepcopy
12
12
  from dataclasses import is_dataclass, fields
13
+ from enum import Enum
13
14
  from types import NoneType
14
15
 
15
16
  import matplotlib
@@ -34,6 +35,58 @@ import ast
34
35
  matplotlib.use("Qt5Agg") # or "Qt5Agg", depending on availability
35
36
 
36
37
 
38
+ def encapsulate_user_input(user_input: str, func_signature: str) -> str:
39
+ """
40
+ Encapsulate the user input string with a function definition.
41
+
42
+ :param user_input: The user input string.
43
+ :param func_signature: The function signature to use for encapsulation.
44
+ :return: The encapsulated user input string.
45
+ """
46
+ if func_signature not in user_input:
47
+ new_user_input = func_signature + "\n "
48
+ if "return " not in user_input:
49
+ if '\n' not in user_input:
50
+ new_user_input += f"return {user_input}"
51
+ else:
52
+ raise ValueError("User input must contain a return statement or be a single line.")
53
+ else:
54
+ for cl in user_input.split('\n'):
55
+ sub_code_lines = cl.split('\n')
56
+ new_user_input += '\n '.join(sub_code_lines) + '\n '
57
+ else:
58
+ new_user_input = user_input
59
+ return new_user_input
60
+
61
+
62
+ def build_user_input_from_conclusion(conclusion: Any) -> str:
63
+ """
64
+ Build a user input string from the conclusion.
65
+
66
+ :param conclusion: The conclusion to use for the callable expression.
67
+ :return: The user input string.
68
+ """
69
+
70
+ # set user_input to the string representation of the conclusion
71
+ if isinstance(conclusion, set):
72
+ user_input = '{' + f"{', '.join([conclusion_to_str(t) for t in conclusion])}" + '}'
73
+ elif isinstance(conclusion, list):
74
+ user_input = '[' + f"{', '.join([conclusion_to_str(t) for t in conclusion])}" + ']'
75
+ elif isinstance(conclusion, tuple):
76
+ user_input = '(' + f"{', '.join([conclusion_to_str(t) for t in conclusion])}" + ')'
77
+ else:
78
+ user_input = conclusion_to_str(conclusion)
79
+
80
+ return user_input
81
+
82
+
83
+ def conclusion_to_str(conclusion_: Any) -> str:
84
+ if isinstance(conclusion_, Enum):
85
+ return type(conclusion_).__name__ + '.' + conclusion_.name
86
+ else:
87
+ return str(conclusion_)
88
+
89
+
37
90
  def update_case(case_query: CaseQuery, conclusions: Dict[str, Any]):
38
91
  """
39
92
  Update the case with the conclusions.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.1.66
3
+ Version: 0.1.67
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
@@ -0,0 +1,20 @@
1
+ ripple_down_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ ripple_down_rules/datasets.py,sha256=mjJh1GLD_5qMgHaukdDWSGphXS9k_BPEF001ZXPchr8,4687
3
+ ripple_down_rules/experts.py,sha256=JGVvSNiWhm4FpRpg76f98tl8Ii_C7x_aWD9FxD-JDLQ,6130
4
+ ripple_down_rules/failures.py,sha256=E6ajDUsw3Blom8eVLbA7d_Qnov2conhtZ0UmpQ9ZtSE,302
5
+ ripple_down_rules/helpers.py,sha256=TvTJU0BA3dPcAyzvZFvAu7jZqsp8Lu0HAAwvuizlGjg,2018
6
+ ripple_down_rules/prompt.py,sha256=gdLV2qhq-1kbmlJhVmkCwGGFhIeDxV0p5u2hwrbGUpk,6370
7
+ ripple_down_rules/rdr.py,sha256=LNXkiS-Dc7inGPKvTxZDarYPFsw9mSEWB6YDHl58g2A,41536
8
+ ripple_down_rules/rdr_decorators.py,sha256=8SclpceI3EtrsbuukWJu8HGLh7Q1ZCgYGLX-RPlG-w0,2018
9
+ ripple_down_rules/rules.py,sha256=SxP94X7I0XImG5zuEWLDfmINihwBKL-h_j1v2EMcVtg,16557
10
+ ripple_down_rules/utils.py,sha256=MABj84x3v6gF5j2HscoQXnAZnupAGio3WjtsrTtVlRo,36473
11
+ ripple_down_rules/datastructures/__init__.py,sha256=V2aNgf5C96Y5-IGghra3n9uiefpoIm_QdT7cc_C8cxQ,111
12
+ ripple_down_rules/datastructures/callable_expression.py,sha256=4AxguiVeSTxzYti5AyOWHdqWzS6KdSPWqf0unR9ZuhA,9636
13
+ ripple_down_rules/datastructures/case.py,sha256=uM5YkJOfYfARrZZ3oAxKMWE5QBSnvHLLZa9Atoxb7eY,13800
14
+ ripple_down_rules/datastructures/dataclasses.py,sha256=TAOAeEvh0BeTis3rEHu8rpCeqNNhU0vK3to0JaBwTio,5961
15
+ ripple_down_rules/datastructures/enums.py,sha256=RdyPUp9Ls1QuLmkcMMkBbCWrmXIZI4xWuM-cLPYZhR0,4666
16
+ ripple_down_rules-0.1.67.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
17
+ ripple_down_rules-0.1.67.dist-info/METADATA,sha256=qlVXkthupuQp5K2PIWp_aFfGVP28rGWW3UXDx2enFBc,42576
18
+ ripple_down_rules-0.1.67.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
19
+ ripple_down_rules-0.1.67.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
20
+ ripple_down_rules-0.1.67.dist-info/RECORD,,
@@ -1,20 +0,0 @@
1
- ripple_down_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- ripple_down_rules/datasets.py,sha256=rCSpeFeu1gTuKESwjHUdQkPPvomI5OMRNGpbdKmHwMg,4639
3
- ripple_down_rules/experts.py,sha256=JGVvSNiWhm4FpRpg76f98tl8Ii_C7x_aWD9FxD-JDLQ,6130
4
- ripple_down_rules/failures.py,sha256=E6ajDUsw3Blom8eVLbA7d_Qnov2conhtZ0UmpQ9ZtSE,302
5
- ripple_down_rules/helpers.py,sha256=TvTJU0BA3dPcAyzvZFvAu7jZqsp8Lu0HAAwvuizlGjg,2018
6
- ripple_down_rules/prompt.py,sha256=cHqhMJqubGhfGpOOY_uXv5L7PBNb64O0IBWSfiY0ui0,6682
7
- ripple_down_rules/rdr.py,sha256=iRoQcfXlCtAP9e_TxWY26ZZOv-Ki4MVRkmfPjlJ2vYY,41535
8
- ripple_down_rules/rdr_decorators.py,sha256=8SclpceI3EtrsbuukWJu8HGLh7Q1ZCgYGLX-RPlG-w0,2018
9
- ripple_down_rules/rules.py,sha256=Ms9uDDM7QaFrzzPB-nsPOWbIrT-QVqvhmHA4z8HQOgI,16547
10
- ripple_down_rules/utils.py,sha256=A4ArFvF4lTsjicfN2fV2oIIo2HSapA9LXb92sY6n-Rs,34538
11
- ripple_down_rules/datastructures/__init__.py,sha256=V2aNgf5C96Y5-IGghra3n9uiefpoIm_QdT7cc_C8cxQ,111
12
- ripple_down_rules/datastructures/callable_expression.py,sha256=TW_u6CJfelW2CiJj9pWFpdOBNIxeEuhhsQEz_pLpFVE,9092
13
- ripple_down_rules/datastructures/case.py,sha256=uM5YkJOfYfARrZZ3oAxKMWE5QBSnvHLLZa9Atoxb7eY,13800
14
- ripple_down_rules/datastructures/dataclasses.py,sha256=V757VwxROlevXh5ZVFLVuzwY4JIJKG8ARlCfjhubfy8,5957
15
- ripple_down_rules/datastructures/enums.py,sha256=RdyPUp9Ls1QuLmkcMMkBbCWrmXIZI4xWuM-cLPYZhR0,4666
16
- ripple_down_rules-0.1.66.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
17
- ripple_down_rules-0.1.66.dist-info/METADATA,sha256=PFdnyrcmNvbrVchvwNW0zl19IkCPhmsCAcpJ3VYO49Y,42576
18
- ripple_down_rules-0.1.66.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
19
- ripple_down_rules-0.1.66.dist-info/top_level.txt,sha256=VeoLhEhyK46M1OHwoPbCQLI1EifLjChqGzhQ6WEUqeM,18
20
- ripple_down_rules-0.1.66.dist-info/RECORD,,