ripple-down-rules 0.2.4__py3-none-any.whl → 0.4.0__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.
- ripple_down_rules/datasets.py +71 -6
- ripple_down_rules/datastructures/callable_expression.py +13 -5
- ripple_down_rules/datastructures/case.py +33 -5
- ripple_down_rules/datastructures/dataclasses.py +30 -8
- ripple_down_rules/datastructures/enums.py +44 -1
- ripple_down_rules/experts.py +16 -8
- ripple_down_rules/rdr.py +13 -7
- ripple_down_rules/rdr_decorators.py +122 -38
- ripple_down_rules/user_interface/__init__.py +0 -0
- ripple_down_rules/user_interface/gui.py +630 -0
- ripple_down_rules/user_interface/ipython_custom_shell.py +146 -0
- ripple_down_rules/user_interface/object_diagram.py +109 -0
- ripple_down_rules/user_interface/prompt.py +159 -0
- ripple_down_rules/user_interface/template_file_creator.py +293 -0
- ripple_down_rules/utils.py +163 -19
- {ripple_down_rules-0.2.4.dist-info → ripple_down_rules-0.4.0.dist-info}/METADATA +8 -1
- ripple_down_rules-0.4.0.dist-info/RECORD +25 -0
- {ripple_down_rules-0.2.4.dist-info → ripple_down_rules-0.4.0.dist-info}/WHEEL +1 -1
- ripple_down_rules/prompt.py +0 -404
- ripple_down_rules-0.2.4.dist-info/RECORD +0 -20
- {ripple_down_rules-0.2.4.dist-info → ripple_down_rules-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {ripple_down_rules-0.2.4.dist-info → ripple_down_rules-0.4.0.dist-info}/top_level.txt +0 -0
@@ -5,51 +5,135 @@ of the RDRs.
|
|
5
5
|
"""
|
6
6
|
import os.path
|
7
7
|
from functools import wraps
|
8
|
-
from
|
8
|
+
from typing_extensions import Callable, Optional, Type, Tuple, Dict, Any, Self, get_type_hints, List, Union
|
9
9
|
|
10
|
-
from
|
11
|
-
from
|
12
|
-
|
13
|
-
from ripple_down_rules.datastructures import Case, Category, create_case, CaseQuery
|
10
|
+
from ripple_down_rules.datastructures.case import create_case
|
11
|
+
from ripple_down_rules.datastructures.dataclasses import CaseQuery
|
12
|
+
from ripple_down_rules.datastructures.enums import Category
|
14
13
|
from ripple_down_rules.experts import Expert, Human
|
14
|
+
from ripple_down_rules.rdr import GeneralRDR, RippleDownRules
|
15
|
+
from ripple_down_rules.utils import get_method_args_as_dict, get_func_rdr_model_name, make_set, \
|
16
|
+
get_method_class_if_exists
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
18
|
+
|
19
|
+
class RDRDecorator:
|
20
|
+
rdr: GeneralRDR
|
21
|
+
|
22
|
+
def __init__(self, models_dir: str,
|
23
|
+
output_type: Tuple[Type],
|
24
|
+
mutual_exclusive: bool,
|
25
|
+
python_dir: Optional[str] = None,
|
26
|
+
output_name: str = "output_",
|
27
|
+
fit: bool = True,
|
28
|
+
expert: Optional[Expert] = None):
|
29
|
+
"""
|
30
|
+
:param models_dir: The directory to save/load the RDR models.
|
31
|
+
:param output_type: The type of the output. This is used to create the RDR model.
|
32
|
+
:param mutual_exclusive: If True, the output types are mutually exclusive.
|
33
|
+
:param python_dir: The directory to save the RDR model as a python file.
|
34
|
+
If None, the RDR model will not be saved as a python file.
|
35
|
+
:param output_name: The name of the output. This is used to create the RDR model.
|
36
|
+
:param fit: If True, the function will be in fit mode. This means that the RDR will prompt the user for the
|
37
|
+
correct output if the function's output is not in the RDR model. If False, the function will be in
|
38
|
+
classification mode. This means that the RDR will classify the function's output based on the RDR model.
|
39
|
+
:param expert: The expert that will be used to prompt the user for the correct output. If None, a Human
|
40
|
+
expert will be used.
|
41
|
+
:return: A decorator to use a GeneralRDR as a classifier that monitors and modifies the function's output.
|
42
|
+
"""
|
43
|
+
self.rdr_models_dir = models_dir
|
44
|
+
self.output_type = output_type
|
45
|
+
self.parsed_output_type: List[Type] = []
|
46
|
+
self.mutual_exclusive = mutual_exclusive
|
47
|
+
self.rdr_python_path: Optional[str] = python_dir
|
48
|
+
self.output_name = output_name
|
49
|
+
self.fit: bool = fit
|
50
|
+
self.expert = expert if expert else Human()
|
51
|
+
self.rdr_model_path: Optional[str] = None
|
52
|
+
self.load()
|
53
|
+
|
54
|
+
def decorator(self, func: Callable) -> Callable:
|
39
55
|
|
40
56
|
@wraps(func)
|
41
|
-
def wrapper(*args, **kwargs) ->
|
57
|
+
def wrapper(*args, **kwargs) -> Optional[Any]:
|
58
|
+
if len(self.parsed_output_type) == 0:
|
59
|
+
self.parse_output_type(func, *args)
|
60
|
+
if self.rdr_model_path is None:
|
61
|
+
self.initialize_rdr_model_path_and_load(func)
|
42
62
|
case_dict = get_method_args_as_dict(func, *args, **kwargs)
|
43
63
|
func_output = func(*args, **kwargs)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
64
|
+
case_dict.update({self.output_name: func_output})
|
65
|
+
case = create_case(case_dict, obj_name=get_func_rdr_model_name(func), max_recursion_idx=3)
|
66
|
+
if self.fit:
|
67
|
+
scope = func.__globals__
|
68
|
+
scope.update(case_dict)
|
69
|
+
func_args_type_hints = get_type_hints(func)
|
70
|
+
func_args_type_hints.update({self.output_name: Union[tuple(self.parsed_output_type)]})
|
71
|
+
case_query = CaseQuery(case, self.output_name, Union[tuple(self.parsed_output_type)],
|
72
|
+
self.mutual_exclusive,
|
73
|
+
scope=scope, is_function=True, function_args_type_hints=func_args_type_hints)
|
74
|
+
output = self.rdr.fit_case(case_query, expert=self.expert)
|
75
|
+
return output[self.output_name]
|
51
76
|
else:
|
52
|
-
return
|
77
|
+
return self.rdr.classify(case)[self.output_name]
|
78
|
+
|
53
79
|
return wrapper
|
54
80
|
|
55
|
-
|
81
|
+
def initialize_rdr_model_path_and_load(self, func: Callable) -> None:
|
82
|
+
model_file_name = get_func_rdr_model_name(func, include_file_name=True)
|
83
|
+
model_file_name = (''.join(['_' + c.lower() if c.isupper() else c for c in model_file_name]).lstrip('_')
|
84
|
+
.replace('__', '_') + ".json")
|
85
|
+
self.rdr_model_path = os.path.join(self.rdr_models_dir, model_file_name)
|
86
|
+
self.load()
|
87
|
+
|
88
|
+
def parse_output_type(self, func: Callable, *args) -> None:
|
89
|
+
for ot in make_set(self.output_type):
|
90
|
+
if ot is Self:
|
91
|
+
func_class = get_method_class_if_exists(func, *args)
|
92
|
+
if func_class is not None:
|
93
|
+
self.parsed_output_type.append(func_class)
|
94
|
+
else:
|
95
|
+
raise ValueError(f"The function {func} is not a method of a class,"
|
96
|
+
f" and the output type is {Self}.")
|
97
|
+
else:
|
98
|
+
self.parsed_output_type.append(ot)
|
99
|
+
|
100
|
+
def save(self):
|
101
|
+
"""
|
102
|
+
Save the RDR model to the specified directory.
|
103
|
+
"""
|
104
|
+
self.rdr.save(self.rdr_model_path)
|
105
|
+
|
106
|
+
if self.rdr_python_path is not None:
|
107
|
+
if not os.path.exists(self.rdr_python_path):
|
108
|
+
os.makedirs(self.rdr_python_path)
|
109
|
+
if not os.path.exists(os.path.join(self.rdr_python_path, "__init__.py")):
|
110
|
+
# add __init__.py file to the directory
|
111
|
+
with open(os.path.join(self.rdr_python_path, "__init__.py"), "w") as f:
|
112
|
+
f.write("# This is an empty __init__.py file to make the directory a package.")
|
113
|
+
# write the RDR model to a python file
|
114
|
+
self.rdr.write_to_python_file(self.rdr_python_path)
|
115
|
+
|
116
|
+
def load(self):
|
117
|
+
"""
|
118
|
+
Load the RDR model from the specified directory.
|
119
|
+
"""
|
120
|
+
if self.rdr_model_path is not None and os.path.exists(self.rdr_model_path):
|
121
|
+
self.rdr = GeneralRDR.load(self.rdr_model_path)
|
122
|
+
else:
|
123
|
+
self.rdr = GeneralRDR()
|
124
|
+
|
125
|
+
def write_to_python_file(self, package_dir: str, file_name_postfix: str = ""):
|
126
|
+
"""
|
127
|
+
Write the RDR model to a python file.
|
128
|
+
|
129
|
+
:param package_dir: The path to the directory to write the python file.
|
130
|
+
"""
|
131
|
+
self.rdr.write_to_python_file(package_dir, postfix=file_name_postfix)
|
132
|
+
|
133
|
+
def update_from_python_file(self, package_dir: str):
|
134
|
+
"""
|
135
|
+
Update the RDR model from a python file.
|
136
|
+
|
137
|
+
:param package_dir: The directory of the package that contains the generated python file.
|
138
|
+
"""
|
139
|
+
self.rdr.update_from_python_file(package_dir)
|
File without changes
|