ripple-down-rules 0.4.4__tar.gz → 0.4.8__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 (42) hide show
  1. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/PKG-INFO +17 -3
  2. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/pyproject.toml +19 -6
  3. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/rules.py +2 -2
  4. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/user_interface/gui.py +4 -1
  5. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/user_interface/ipython_custom_shell.py +4 -1
  6. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/user_interface/prompt.py +2 -2
  7. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/user_interface/template_file_creator.py +59 -31
  8. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/utils.py +9 -6
  9. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules.egg-info/PKG-INFO +17 -3
  10. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules.egg-info/SOURCES.txt +2 -0
  11. ripple_down_rules-0.4.8/src/ripple_down_rules.egg-info/requires.txt +16 -0
  12. ripple_down_rules-0.4.8/test/test_template_file_creator.py +57 -0
  13. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/LICENSE +0 -0
  14. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/README.md +0 -0
  15. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/setup.cfg +0 -0
  16. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/__init__.py +0 -0
  17. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/datasets.py +0 -0
  18. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/datastructures/__init__.py +0 -0
  19. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/datastructures/callable_expression.py +0 -0
  20. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/datastructures/case.py +0 -0
  21. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/datastructures/dataclasses.py +0 -0
  22. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/datastructures/enums.py +0 -0
  23. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/experts.py +0 -0
  24. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/failures.py +0 -0
  25. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/helpers.py +0 -0
  26. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/rdr.py +0 -0
  27. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/rdr_decorators.py +0 -0
  28. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/user_interface/__init__.py +0 -0
  29. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules/user_interface/object_diagram.py +0 -0
  30. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules.egg-info/dependency_links.txt +0 -0
  31. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/src/ripple_down_rules.egg-info/top_level.txt +0 -0
  32. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/test/test_json_serialization.py +0 -0
  33. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/test/test_object_diagram.py +0 -0
  34. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/test/test_on_mutagenic.py +0 -0
  35. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/test/test_rdr.py +0 -0
  36. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/test/test_rdr_alchemy.py +0 -0
  37. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/test/test_rdr_decorators.py +0 -0
  38. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/test/test_rdr_world.py +0 -0
  39. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/test/test_relational_rdr.py +0 -0
  40. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/test/test_relational_rdr_alchemy.py +0 -0
  41. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/test/test_sql_model.py +0 -0
  42. {ripple_down_rules-0.4.4 → ripple_down_rules-0.4.8}/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.4.4
3
+ Version: 0.4.8
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
@@ -677,12 +677,26 @@ License: GNU GENERAL PUBLIC LICENSE
677
677
  the library. If this is what you want to do, use the GNU Lesser General
678
678
  Public License instead of this License. But first, please read
679
679
  <https://www.gnu.org/licenses/why-not-lgpl.html>.
680
- Project-URL: Homepage, https://github.com/AbdelrhmanBassiouny/ripple_down_rules
681
680
  Keywords: robotics,knowledge,reasoning,representation
682
681
  Classifier: Programming Language :: Python :: 3
683
- Requires-Python: >=3.10
684
682
  Description-Content-Type: text/markdown
685
683
  License-File: LICENSE
684
+ Requires-Dist: anytree>=2.8.0
685
+ Requires-Dist: pandas>=2.0.3
686
+ Requires-Dist: networkx>=3.1
687
+ Requires-Dist: ordered_set>=4
688
+ Requires-Dist: pygraphviz>=1.7
689
+ Requires-Dist: ucimlrepo>=0.0.7
690
+ Requires-Dist: typing-extensions>=4.12.2
691
+ Requires-Dist: matplotlib>=3.7.5
692
+ Requires-Dist: sqlalchemy
693
+ Requires-Dist: pyqt6
694
+ Requires-Dist: qtconsole
695
+ Requires-Dist: graphviz
696
+ Requires-Dist: tabulate
697
+ Requires-Dist: ipython
698
+ Requires-Dist: requests
699
+ Requires-Dist: colorama
686
700
  Dynamic: license-file
687
701
 
688
702
  # Ripple Down Rules (RDR)
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "ripple_down_rules"
9
- version = "0.4.4"
9
+ version = "0.4.8"
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" }]
@@ -15,8 +15,21 @@ classifiers = [
15
15
  "Programming Language :: Python :: 3",
16
16
  ]
17
17
  keywords = ["robotics", "knowledge", "reasoning", "representation"]
18
- dependencies = []
19
- requires-python = ">=3.10"
20
-
21
- [project.urls]
22
- Homepage = "https://github.com/AbdelrhmanBassiouny/ripple_down_rules"
18
+ dependencies = [
19
+ "anytree>=2.8.0",
20
+ "pandas>=2.0.3",
21
+ "networkx>=3.1",
22
+ "ordered_set>=4",
23
+ "pygraphviz>=1.7",
24
+ "ucimlrepo>=0.0.7",
25
+ "typing-extensions>=4.12.2",
26
+ "matplotlib>=3.7.5",
27
+ "sqlalchemy",
28
+ "pyqt6",
29
+ "qtconsole",
30
+ "graphviz",
31
+ "tabulate",
32
+ "ipython",
33
+ "requests",
34
+ "colorama",
35
+ ]
@@ -1,11 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
3
4
  import re
4
5
  from abc import ABC, abstractmethod
5
6
  from uuid import uuid4
6
7
 
7
8
  from anytree import NodeMixin
8
- from rospy import logwarn, logdebug
9
9
  from sqlalchemy.orm import DeclarativeBase as SQLTable
10
10
  from typing_extensions import List, Optional, Self, Union, Dict, Any, Tuple
11
11
 
@@ -164,7 +164,7 @@ class Rule(NodeMixin, SubclassJSONSerializer, ABC):
164
164
  try:
165
165
  corner_case = Case.from_json(data["corner_case"])
166
166
  except Exception as e:
167
- logdebug("Failed to load corner case from json, setting it to None.")
167
+ logging.debug("Failed to load corner case from json, setting it to None.")
168
168
  corner_case = None
169
169
  loaded_rule = cls(conditions=CallableExpression.from_json(data["conditions"]),
170
170
  conclusion=CallableExpression.from_json(data["conclusion"]),
@@ -475,7 +475,10 @@ class RDRCaseViewer(QMainWindow):
475
475
  def _load(self):
476
476
  if not self.template_file_creator:
477
477
  return
478
- self.code_lines = self.template_file_creator.load()
478
+ self.code_lines, updates = self.template_file_creator.load(self.template_file_creator.temp_file_path,
479
+ self.template_file_creator.func_name,
480
+ self.template_file_creator.print_func)
481
+ self.ipython_console.kernel.shell.user_ns.update(updates)
479
482
  if self.code_lines is not None:
480
483
  self.user_input = encapsulate_code_lines_into_a_function(
481
484
  self.code_lines, self.template_file_creator.func_name,
@@ -30,7 +30,10 @@ class MyMagics(Magics):
30
30
 
31
31
  @line_magic
32
32
  def load(self, line):
33
- self.all_code_lines = self.rule_editor.load()
33
+ self.all_code_lines, updates = self.rule_editor.load(self.rule_editor.temp_file_path,
34
+ self.rule_editor.func_name,
35
+ self.rule_editor.print_func)
36
+ self.shell.user_ns.update(updates)
34
37
 
35
38
  @line_magic
36
39
  def help(self, line):
@@ -134,14 +134,14 @@ class UserPrompt:
134
134
  """
135
135
  while True:
136
136
  if user_input is None:
137
- if QApplication.instance() is None:
137
+ if self.viewer is None:
138
138
  shell = IPythonShell() if shell is None else shell
139
139
  shell.run()
140
140
  user_input = shell.user_input
141
141
  else:
142
142
  app = QApplication.instance()
143
143
  if app is None:
144
- app = QApplication([])
144
+ raise RuntimeError("QApplication instance is None. Please run the application first.")
145
145
  self.viewer.show()
146
146
  app.exec()
147
147
  user_input = self.viewer.user_input
@@ -9,13 +9,13 @@ from textwrap import indent, dedent
9
9
 
10
10
  from colorama import Fore, Style
11
11
  from ipykernel.inprocess.ipkernel import InProcessInteractiveShell
12
- from typing_extensions import Optional, Type, List, Callable
12
+ from typing_extensions import Optional, Type, List, Callable, Tuple, Dict
13
13
 
14
14
  from ..datastructures.case import Case
15
15
  from ..datastructures.dataclasses import CaseQuery
16
16
  from ..datastructures.enums import Editor, PromptFor
17
17
  from ..utils import str_to_snake_case, get_imports_from_scope, make_list, typing_hint_to_str, \
18
- get_imports_from_types, extract_function_source
18
+ get_imports_from_types, extract_function_source, extract_imports
19
19
 
20
20
 
21
21
  def detect_available_editor() -> Optional[Editor]:
@@ -49,6 +49,9 @@ def start_code_server(workspace):
49
49
  stderr=subprocess.PIPE, text=True)
50
50
 
51
51
 
52
+ FunctionData = Tuple[Optional[List[str]], Optional[Dict[str, Callable]]]
53
+
54
+
52
55
  class TemplateFileCreator:
53
56
  """
54
57
  A class to create a rule template file for a given case and prompt for the user to edit it.
@@ -71,15 +74,15 @@ class TemplateFileCreator:
71
74
  """
72
75
 
73
76
  def __init__(self, shell: InProcessInteractiveShell, case_query: CaseQuery, prompt_for: PromptFor,
74
- code_to_modify: Optional[str] = None, print_func: Optional[Callable[[str], None]] = None):
75
- self.print_func = print_func if print_func else print
77
+ code_to_modify: Optional[str] = None, print_func: Callable[[str], None] = print):
78
+ self.print_func = print_func
76
79
  self.shell = shell
77
80
  self.code_to_modify = code_to_modify
78
81
  self.prompt_for = prompt_for
79
82
  self.case_query = case_query
80
83
  self.output_type = self.get_output_type()
81
84
  self.user_edit_line = 0
82
- self.func_name: str = self.get_func_name()
85
+ self.func_name: str = self.get_func_name(self.prompt_for, self.case_query)
83
86
  self.func_doc: str = self.get_func_doc()
84
87
  self.function_signature: str = self.get_function_signature()
85
88
  self.editor: Optional[Editor] = detect_available_editor()
@@ -146,7 +149,7 @@ class TemplateFileCreator:
146
149
 
147
150
  def get_function_signature(self) -> str:
148
151
  if self.func_name is None:
149
- self.func_name = self.get_func_name()
152
+ self.func_name = self.get_func_name(self.prompt_for, self.case_query)
150
153
  output_type_hint = self.get_output_type_hint()
151
154
  func_args = self.get_func_args()
152
155
  return f"def {self.func_name}({func_args}){output_type_hint}:"
@@ -228,25 +231,40 @@ class TemplateFileCreator:
228
231
  imports = set(imports)
229
232
  return '\n'.join(imports)
230
233
 
234
+ @staticmethod
235
+ def get_core_attribute_types(case_query: CaseQuery) -> List[Type]:
236
+ """
237
+ Get the core attribute types of the case query.
238
+
239
+ :return: A list of core attribute types.
240
+ """
241
+ attr_types = [t for t in case_query.core_attribute_type if t.__module__ != "builtins" and t is not None
242
+ and t is not type(None)]
243
+ return attr_types
244
+
231
245
  def get_func_doc(self) -> Optional[str]:
232
246
  """
233
247
  :return: A string containing the function docstring.
234
248
  """
249
+ type_data = f" of type {' or '.join(map(lambda c: c.__name__, self.get_core_attribute_types(self.case_query)))}"
235
250
  if self.prompt_for == PromptFor.Conditions:
236
251
  return (f"Get conditions on whether it's possible to conclude a value"
237
- f" for {self.case_query.name}")
252
+ f" for {self.case_query.name} {type_data}.")
238
253
  else:
239
- return f"Get possible value(s) for {self.case_query.name}"
254
+ return f"Get possible value(s) for {self.case_query.name} {type_data}."
240
255
 
241
- def get_func_name(self) -> Optional[str]:
256
+ @staticmethod
257
+ def get_func_name(prompt_for, case_query) -> Optional[str]:
242
258
  func_name = ""
243
- if self.prompt_for == PromptFor.Conditions:
244
- func_name = f"{self.prompt_for.value.lower()}_for_"
245
- case_name = self.case_query.name.replace(".", "_")
246
- if self.case_query.is_function:
259
+ if prompt_for == PromptFor.Conditions:
260
+ func_name = f"{prompt_for.value.lower()}_for_"
261
+ case_name = case_query.name.replace(".", "_")
262
+ if case_query.is_function:
247
263
  # convert any CamelCase word into snake_case by adding _ before each capital letter
248
- case_name = case_name.replace(f"_{self.case_query.attribute_name}", "")
264
+ case_name = case_name.replace(f"_{case_query.attribute_name}", "")
249
265
  func_name += case_name
266
+ func_name += f"_of_type_{'_or_'.join(map(lambda c: c.__name__,
267
+ TemplateFileCreator.get_core_attribute_types(case_query)))}"
250
268
  return str_to_snake_case(func_name)
251
269
 
252
270
  @cached_property
@@ -259,33 +277,43 @@ class TemplateFileCreator:
259
277
  case = self.case_query.scope['case']
260
278
  return case._obj_type if isinstance(case, Case) else type(case)
261
279
 
262
- def load(self) -> Optional[List[str]]:
263
- if not self.temp_file_path:
264
- self.print_func(f"{Fore.RED}ERROR:: No file to load. Run %edit first.{Style.RESET_ALL}")
265
- return None
280
+ @staticmethod
281
+ def load(file_path: str, func_name: str, print_func: Callable = print) -> FunctionData:
282
+ """
283
+ Load the function from the given file path.
284
+
285
+ :param file_path: The path to the file to load.
286
+ :param func_name: The name of the function to load.
287
+ :param print_func: The function to use for printing messages.
288
+ :return: A tuple containing the function source code and the function object as a dictionary
289
+ with the function name as the key and the function object as the value.
290
+ """
291
+ if not file_path:
292
+ print_func(f"{Fore.RED}ERROR:: No file to load. Run %edit first.{Style.RESET_ALL}")
293
+ return None, None
266
294
 
267
- with open(self.temp_file_path, 'r') as f:
295
+ with open(file_path, 'r') as f:
268
296
  source = f.read()
269
297
 
270
298
  tree = ast.parse(source)
271
299
  updates = {}
272
300
  for node in tree.body:
273
- if isinstance(node, ast.FunctionDef) and node.name == self.func_name:
301
+ if isinstance(node, ast.FunctionDef) and node.name == func_name:
274
302
  exec_globals = {}
275
- exec(source, self.case_query.scope, exec_globals)
276
- user_function = exec_globals[self.func_name]
277
- updates[self.func_name] = user_function
278
- self.print_func(f"{Fore.BLUE}Loaded `{self.func_name}` function into user namespace.{Style.RESET_ALL}")
303
+ scope = extract_imports(tree=tree)
304
+ exec(source, scope, exec_globals)
305
+ user_function = exec_globals[func_name]
306
+ updates[func_name] = user_function
307
+ print_func(f"{Fore.BLUE}Loaded `{func_name}` function into user namespace.{Style.RESET_ALL}")
279
308
  break
280
309
  if updates:
281
- self.shell.user_ns.update(updates)
282
- self.all_code_lines = extract_function_source(self.temp_file_path,
283
- [self.func_name],
284
- join_lines=False)[self.func_name]
285
- return self.all_code_lines
310
+ all_code_lines = extract_function_source(file_path,
311
+ [func_name],
312
+ join_lines=False)[func_name]
313
+ return all_code_lines, updates
286
314
  else:
287
- self.print_func(f"{Fore.RED}ERROR:: Function `{self.func_name}` not found.{Style.RESET_ALL}")
288
- return None
315
+ print_func(f"{Fore.RED}ERROR:: Function `{func_name}` not found.{Style.RESET_ALL}")
316
+ return None, None
289
317
 
290
318
  def __del__(self):
291
319
  if hasattr(self, 'process') and self.process is not None and self.process.poll() is None:
@@ -14,6 +14,7 @@ from collections import UserDict
14
14
  from copy import deepcopy, copy
15
15
  from dataclasses import is_dataclass, fields
16
16
  from enum import Enum
17
+ from textwrap import dedent
17
18
  from types import NoneType
18
19
 
19
20
  import matplotlib
@@ -22,7 +23,6 @@ import requests
22
23
  from anytree import Node, RenderTree
23
24
  from anytree.exporter import DotExporter
24
25
  from matplotlib import pyplot as plt
25
- from rospy import logwarn
26
26
  from sqlalchemy import MetaData, inspect
27
27
  from sqlalchemy.orm import Mapped, registry, class_mapper, DeclarativeBase as SQLTable, Session
28
28
  from tabulate import tabulate
@@ -106,9 +106,12 @@ def get_imports_from_scope(scope: Dict[str, Any]) -> List[str]:
106
106
  return imports
107
107
 
108
108
 
109
- def extract_imports(file_path):
110
- with open(file_path, "r") as f:
111
- tree = ast.parse(f.read(), filename=file_path)
109
+ def extract_imports(file_path: Optional[str] = None, tree: Optional[ast.AST] = None) -> Dict[str, Any]:
110
+ if tree is None:
111
+ if file_path is None:
112
+ raise ValueError("Either file_path or tree must be provided")
113
+ with open(file_path, "r") as f:
114
+ tree = ast.parse(f.read(), filename=file_path)
112
115
 
113
116
  scope = {}
114
117
 
@@ -130,7 +133,7 @@ def extract_imports(file_path):
130
133
  module = importlib.import_module(module_name)
131
134
  scope[asname] = getattr(module, name)
132
135
  except (ImportError, AttributeError) as e:
133
- logwarn(f"Could not import {module_name}: {e} while extracting imports from {file_path}")
136
+ logging.warning(f"Could not import {module_name}: {e} while extracting imports from {file_path}")
134
137
 
135
138
  return scope
136
139
 
@@ -168,7 +171,7 @@ def extract_function_source(file_path: str,
168
171
  if not include_signature:
169
172
  func_lines = func_lines[1:]
170
173
  line_numbers.append((node.lineno, node.end_lineno))
171
- functions_source[node.name] = "\n".join(func_lines) if join_lines else func_lines
174
+ functions_source[node.name] = dedent("\n".join(func_lines)) if join_lines else func_lines
172
175
  if len(functions_source) == len(function_names):
173
176
  break
174
177
  if len(functions_source) != len(function_names):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ripple_down_rules
3
- Version: 0.4.4
3
+ Version: 0.4.8
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
@@ -677,12 +677,26 @@ License: GNU GENERAL PUBLIC LICENSE
677
677
  the library. If this is what you want to do, use the GNU Lesser General
678
678
  Public License instead of this License. But first, please read
679
679
  <https://www.gnu.org/licenses/why-not-lgpl.html>.
680
- Project-URL: Homepage, https://github.com/AbdelrhmanBassiouny/ripple_down_rules
681
680
  Keywords: robotics,knowledge,reasoning,representation
682
681
  Classifier: Programming Language :: Python :: 3
683
- Requires-Python: >=3.10
684
682
  Description-Content-Type: text/markdown
685
683
  License-File: LICENSE
684
+ Requires-Dist: anytree>=2.8.0
685
+ Requires-Dist: pandas>=2.0.3
686
+ Requires-Dist: networkx>=3.1
687
+ Requires-Dist: ordered_set>=4
688
+ Requires-Dist: pygraphviz>=1.7
689
+ Requires-Dist: ucimlrepo>=0.0.7
690
+ Requires-Dist: typing-extensions>=4.12.2
691
+ Requires-Dist: matplotlib>=3.7.5
692
+ Requires-Dist: sqlalchemy
693
+ Requires-Dist: pyqt6
694
+ Requires-Dist: qtconsole
695
+ Requires-Dist: graphviz
696
+ Requires-Dist: tabulate
697
+ Requires-Dist: ipython
698
+ Requires-Dist: requests
699
+ Requires-Dist: colorama
686
700
  Dynamic: license-file
687
701
 
688
702
  # Ripple Down Rules (RDR)
@@ -13,6 +13,7 @@ src/ripple_down_rules/utils.py
13
13
  src/ripple_down_rules.egg-info/PKG-INFO
14
14
  src/ripple_down_rules.egg-info/SOURCES.txt
15
15
  src/ripple_down_rules.egg-info/dependency_links.txt
16
+ src/ripple_down_rules.egg-info/requires.txt
16
17
  src/ripple_down_rules.egg-info/top_level.txt
17
18
  src/ripple_down_rules/datastructures/__init__.py
18
19
  src/ripple_down_rules/datastructures/callable_expression.py
@@ -35,4 +36,5 @@ test/test_rdr_world.py
35
36
  test/test_relational_rdr.py
36
37
  test/test_relational_rdr_alchemy.py
37
38
  test/test_sql_model.py
39
+ test/test_template_file_creator.py
38
40
  test/test_utils.py
@@ -0,0 +1,16 @@
1
+ anytree>=2.8.0
2
+ pandas>=2.0.3
3
+ networkx>=3.1
4
+ ordered_set>=4
5
+ pygraphviz>=1.7
6
+ ucimlrepo>=0.0.7
7
+ typing-extensions>=4.12.2
8
+ matplotlib>=3.7.5
9
+ sqlalchemy
10
+ pyqt6
11
+ qtconsole
12
+ graphviz
13
+ tabulate
14
+ ipython
15
+ requests
16
+ colorama
@@ -0,0 +1,57 @@
1
+ import os
2
+ from textwrap import dedent
3
+
4
+ from ripple_down_rules.datastructures.dataclasses import CaseQuery
5
+ from ripple_down_rules.datastructures.enums import PromptFor
6
+ from ripple_down_rules.user_interface.template_file_creator import TemplateFileCreator
7
+ from test_rdr_world import World, Handle, Container
8
+
9
+
10
+
11
+ def test_func_name_with_one_type():
12
+ # Test the function name
13
+ world = World()
14
+ case_query: CaseQuery = CaseQuery(world, "views", (Handle,), False)
15
+
16
+ func_name = TemplateFileCreator.get_func_name(PromptFor.Conclusion, case_query)
17
+ assert func_name == "world_views_of_type_handle"
18
+
19
+ func_name = TemplateFileCreator.get_func_name(PromptFor.Conditions, case_query)
20
+ assert func_name == "conditions_for_world_views_of_type_handle"
21
+
22
+ def test_func_name_with_two_type():
23
+ # Test the function name
24
+ world = World()
25
+ case_query: CaseQuery = CaseQuery(world, "views", (Handle, Container), False)
26
+
27
+ func_name = TemplateFileCreator.get_func_name(PromptFor.Conclusion, case_query)
28
+ assert func_name == "world_views_of_type_handle_or_container"
29
+
30
+ func_name = TemplateFileCreator.get_func_name(PromptFor.Conditions, case_query)
31
+ assert func_name == "conditions_for_world_views_of_type_handle_or_container"
32
+
33
+ def test_func_name_with_not_needed_types():
34
+ # Test the function name
35
+ world = World()
36
+ case_query: CaseQuery = CaseQuery(world, "views", (Handle, list, bool, type(None)), False)
37
+
38
+ func_name = TemplateFileCreator.get_func_name(PromptFor.Conclusion, case_query)
39
+ assert func_name == "world_views_of_type_handle"
40
+
41
+ func_name = TemplateFileCreator.get_func_name(PromptFor.Conditions, case_query)
42
+ assert func_name == "conditions_for_world_views_of_type_handle"
43
+
44
+ def test_load():
45
+ # Test the load function
46
+ world = World()
47
+ imports = "from test_rdr_world import World\n\n\n"
48
+ func_code = "def test_func(case):\n return case"
49
+ source_code = f"{imports}{func_code}\n"
50
+ source_code = dedent(source_code)
51
+ with open("test.py", "w") as f:
52
+ f.write(source_code)
53
+ code_lines, updates = TemplateFileCreator.load("test.py", "test_func")
54
+ assert code_lines == func_code.splitlines()
55
+ assert list(updates.keys()) == ["test_func"]
56
+ assert updates["test_func"](world) == world
57
+ os.remove("test.py")