ripple-down-rules 0.6.29__py3-none-any.whl → 0.6.31__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.
@@ -1,15 +1,19 @@
1
- from __future__ import annotations
1
+ from __future__ import annotations
2
2
 
3
3
  import inspect
4
4
  import logging
5
+ import os.path
5
6
  from types import MethodType
6
7
 
8
+ from graphviz import Digraph, Source
9
+
7
10
  try:
8
11
  from PyQt6.QtCore import Qt
9
12
  from PyQt6.QtGui import QPixmap, QPainter, QPalette
10
13
  from PyQt6.QtWidgets import (
11
14
  QWidget, QVBoxLayout, QLabel, QScrollArea,
12
- QSizePolicy, QToolButton, QHBoxLayout, QPushButton, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem
15
+ QSizePolicy, QToolButton, QHBoxLayout, QPushButton, QMainWindow, QGraphicsView, QGraphicsScene,
16
+ QGraphicsPixmapItem
13
17
  )
14
18
  from qtconsole.inprocess import QtInProcessKernelManager
15
19
  from qtconsole.rich_jupyter_widget import RichJupyterWidget
@@ -120,7 +124,7 @@ class BackgroundWidget(QWidget):
120
124
 
121
125
 
122
126
  class CollapsibleBox(QWidget):
123
- def __init__(self, title="", parent=None, viewer: Optional[RDRCaseViewer]=None, chain_name: Optional[str] = None,
127
+ def __init__(self, title="", parent=None, viewer: Optional[RDRCaseViewer] = None, chain_name: Optional[str] = None,
124
128
  main_obj: Optional[Dict[str, Any]] = None):
125
129
  super().__init__(parent)
126
130
 
@@ -259,12 +263,14 @@ def color_name_to_html(color_name):
259
263
  'g': 'green',
260
264
  'b': 'blue',
261
265
  'o': 'orange',
266
+ 'm': 'magenta',
262
267
  }
263
268
  color_map = {
264
269
  'red': '#d6336c',
265
270
  'green': '#2eb872',
266
271
  'blue': '#007acc',
267
272
  'orange': '#FFA07A',
273
+ 'magenta': '#a22bcf',
268
274
  }
269
275
  if len(color_name) == 1:
270
276
  color_name = single_char_to_name.get(color_name, color_name)
@@ -285,16 +291,10 @@ class RDRCaseViewer(QMainWindow):
285
291
  instances: List[RDRCaseViewer] = []
286
292
  exit_status: ExitStatus = ExitStatus.CLOSE
287
293
 
288
- def __init__(self, parent=None,
289
- save_dir: Optional[str] = None,
290
- save_model_name: Optional[str] = None):
294
+ def __init__(self, parent=None):
291
295
  super().__init__(parent)
292
- self.exit_status = ExitStatus.CLOSE
293
296
  self.instances.clear()
294
297
  self.instances.append(self)
295
- self.save_dir = save_dir
296
- self.save_model_name = save_model_name
297
-
298
298
  self.setWindowTitle("RDR Case Viewer")
299
299
 
300
300
  self.setBaseSize(1600, 600) # or your preferred initial size
@@ -333,35 +333,28 @@ class RDRCaseViewer(QMainWindow):
333
333
  main_layout.addWidget(middle_widget, stretch=2)
334
334
  main_layout.addWidget(self.obj_diagram_viewer, stretch=2)
335
335
 
336
- def set_save_function(self, save_function: Callable[[str, str], None]) -> None:
337
- """
338
- Set the function to save the file.
339
-
340
- :param save_function: The function to save the file.
341
- """
342
- self.save_function = save_function
343
- self.save_btn.clicked.connect(lambda: self.save_function(self.save_dir, self.save_model_name))
344
-
345
336
  def print(self, msg):
346
337
  """
347
338
  Print a message to the console.
348
339
  """
349
340
  self.ipython_console._append_plain_text(msg + '\n', True)
350
341
 
351
- def update_for_case_query(self, case_query: CaseQuery, title_txt: Optional[str] = None,
352
- prompt_for: Optional[PromptFor] = None, code_to_modify: Optional[str] = None):
342
+ def update_for_case_query(self, case_query: CaseQuery, prompt_str: Optional[str] = None,
343
+ prompt_for: Optional[PromptFor] = None, code_to_modify: Optional[str] = None,
344
+ title: Optional[str] = None):
353
345
  self.case_query = case_query
354
346
  self.prompt_for = prompt_for
355
347
  self.code_to_modify = code_to_modify
356
- title_text = title_txt or ""
348
+ title_text = title or ""
357
349
  case_attr_type = ', '.join([t.__name__ for t in case_query.core_attribute_type])
358
350
  case_attr_type = style(f"{case_attr_type}", 'g', 28, 'bold')
359
351
  case_name = style(f"{case_query.name}", 'b', 28, 'bold')
360
352
  title_text = style(f"{title_text} {case_name} of type {case_attr_type}", 'o', 28, 'bold')
361
- self.update_for_object(case_query.case, case_query.case_name, case_query.scope, title_text)
353
+ self.update_for_object(case_query.case, case_query.case_name, scope=case_query.scope, title_text=title_text,
354
+ header=prompt_str)
362
355
 
363
356
  def update_for_object(self, obj: Any, name: str, scope: Optional[dict] = None,
364
- title_text: Optional[str] = None):
357
+ title_text: Optional[str] = None, header: Optional[str] = None):
365
358
  self.update_main_obj(obj, name)
366
359
  title_text = title_text or style(f"{name}", 'o', 28, 'bold')
367
360
  scope = scope or {}
@@ -370,6 +363,9 @@ class RDRCaseViewer(QMainWindow):
370
363
  self.update_attribute_layout(obj, name)
371
364
  self.title_label.setText(title_text)
372
365
  self.ipython_console.update_namespace(scope)
366
+ if header is not None and len(header) > 0:
367
+ self.ipython_console.print(header)
368
+ self.exit_status = ExitStatus.CLOSE
373
369
 
374
370
  def update_main_obj(self, obj, name):
375
371
  self.main_obj = {name: obj}
@@ -441,8 +437,6 @@ class RDRCaseViewer(QMainWindow):
441
437
  if isinstance(item.widget(), CollapsibleBox):
442
438
  self.expand_collapse_all(item.widget(), expand=True, curr_depth=curr_depth + 1, max_depth=max_depth)
443
439
 
444
-
445
-
446
440
  def create_buttons_widget(self):
447
441
  button_widget = QWidget()
448
442
  button_widget_layout = QVBoxLayout(button_widget)
@@ -466,13 +460,19 @@ class RDRCaseViewer(QMainWindow):
466
460
  load_btn.clicked.connect(self._load)
467
461
  load_btn.setStyleSheet(f"background-color: {color_name_to_html('b')}; color: white;") # Blue button
468
462
 
469
- self.save_btn = QPushButton("Save")
470
- self.save_btn.setStyleSheet(f"background-color: {color_name_to_html('b')}; color: white;") # Blue button
463
+ current_value_btn = QPushButton("Current Value")
464
+ current_value_btn.clicked.connect(self._show_current_value)
465
+ current_value_btn.setStyleSheet(f"background-color: {color_name_to_html('m')}; color: white;")
466
+
467
+ rule_tree_btn = QPushButton("Rule Tree")
468
+ rule_tree_btn.clicked.connect(self._show_rule_tree) # Placeholder for rule tree functionality
469
+ rule_tree_btn.setStyleSheet(f"background-color: {color_name_to_html('r')}; color: white;")
471
470
 
472
471
  row_1_button_widget_layout.addWidget(edit_btn)
473
472
  row_1_button_widget_layout.addWidget(load_btn)
474
- row_1_button_widget_layout.addWidget(accept_btn)
475
- row_2_button_widget_layout.addWidget(self.save_btn)
473
+ row_1_button_widget_layout.addWidget(current_value_btn)
474
+ row_2_button_widget_layout.addWidget(rule_tree_btn)
475
+ row_2_button_widget_layout.addWidget(accept_btn)
476
476
  return button_widget
477
477
 
478
478
  def _accept(self):
@@ -481,16 +481,15 @@ class RDRCaseViewer(QMainWindow):
481
481
  self.close()
482
482
 
483
483
  def _edit(self):
484
- self.template_file_creator = TemplateFileCreator(self.case_query, self.prompt_for, self.code_to_modify,
485
- self.print)
484
+ self.template_file_creator = self.create_template_file_creator()
486
485
  self.template_file_creator.edit()
487
486
 
488
487
  def _load(self):
489
488
  if not self.template_file_creator:
490
489
  return
491
490
  self.code_lines, updates = self.template_file_creator.load(self.template_file_creator.temp_file_path,
492
- self.template_file_creator.func_name,
493
- self.template_file_creator.print_func)
491
+ self.template_file_creator.func_name,
492
+ self.template_file_creator.print_func)
494
493
  self.ipython_console.kernel.shell.user_ns.update(updates)
495
494
  if self.code_lines is not None:
496
495
  self.user_input = encapsulate_code_lines_into_a_function(
@@ -498,7 +497,22 @@ class RDRCaseViewer(QMainWindow):
498
497
  self.template_file_creator.function_signature,
499
498
  self.template_file_creator.func_doc, self.case_query)
500
499
  self.case_query.scope.update(updates)
501
- self.template_file_creator = None
500
+
501
+ def _show_current_value(self):
502
+ self.ipython_console.print(self.case_query.current_value_str)
503
+
504
+ def _show_rule_tree(self):
505
+ if self.case_query is None:
506
+ self.ipython_console.print("No case query provided.")
507
+ return
508
+ if not self.case_query.rdr:
509
+ self.ipython_console.print("No rule tree available for this case query.")
510
+ return
511
+ self.case_query.render_rule_tree(view=True)
512
+
513
+ def create_template_file_creator(self) -> TemplateFileCreator:
514
+ return TemplateFileCreator(self.case_query, self.prompt_for, self.code_to_modify,
515
+ self.print)
502
516
 
503
517
  def update_attribute_layout(self, obj, name: str):
504
518
  # Clear the existing layout
@@ -547,7 +561,8 @@ class RDRCaseViewer(QMainWindow):
547
561
  attr = f"{attr}"
548
562
  try:
549
563
  if is_iterable(value) or hasattr(value, "__dict__") and not inspect.isfunction(value):
550
- self.add_collapsible(attr, value, layout, current_depth + 1, max_depth, chain_name=f"{chain_name}.{attr}")
564
+ self.add_collapsible(attr, value, layout, current_depth + 1, max_depth,
565
+ chain_name=f"{chain_name}.{attr}")
551
566
  else:
552
567
  self.add_non_collapsible(attr, value, layout)
553
568
  except Exception as e:
@@ -559,7 +574,7 @@ class RDRCaseViewer(QMainWindow):
559
574
  type_name = type(value) if not isinstance(value, type) else value
560
575
  collapsible = CollapsibleBox(
561
576
  f'<b><span style="color:#FFA07A;">{attr}</span></b> {python_colored_repr(type_name)}', viewer=self,
562
- chain_name=chain_name, main_obj=self.main_obj)
577
+ chain_name=chain_name, main_obj=self.main_obj)
563
578
  self.add_attributes(value, attr, collapsible.content_layout, current_depth, max_depth, chain_name=chain_name)
564
579
  layout.addWidget(collapsible)
565
580
 
@@ -587,7 +602,6 @@ class IPythonConsole(RichJupyterWidget):
587
602
 
588
603
  # Monkey patch its run_cell method
589
604
  def custom_run_cell(this, raw_cell, **kwargs):
590
- print(raw_cell)
591
605
  if contains_return_statement(raw_cell) and 'def ' not in raw_cell:
592
606
  if self.parent.template_file_creator and self.parent.template_file_creator.func_name in raw_cell:
593
607
  self.command_log = self.parent.code_lines
@@ -601,7 +615,6 @@ class IPythonConsole(RichJupyterWidget):
601
615
 
602
616
  original_run_cell = self.kernel.shell.run_cell
603
617
  self.kernel.shell.run_cell = MethodType(custom_run_cell, self.kernel.shell)
604
-
605
618
  self.kernel_client = self.kernel_manager.client()
606
619
  self.kernel_client.start_channels()
607
620
 
@@ -630,7 +643,7 @@ class IPythonConsole(RichJupyterWidget):
630
643
  .in-prompt { font-weight: bold; color: %(in_prompt_color)s }
631
644
  .out-prompt-number { font-weight: bold; color: %(out_prompt_number_color)s }
632
645
  .out-prompt { font-weight: bold; color: %(out_prompt_color)s }
633
- '''%dict(
646
+ ''' % dict(
634
647
  bgcolor='#0b0d0b', fgcolor='#47d9cc', select="#555",
635
648
  in_prompt_number_color='lime', in_prompt_color='lime',
636
649
  out_prompt_number_color='red', out_prompt_color='red'
@@ -648,6 +661,12 @@ class IPythonConsole(RichJupyterWidget):
648
661
  """
649
662
  self.kernel.shell.user_ns.update(namespace)
650
663
 
664
+ def print(self, msg):
665
+ """
666
+ Custom print function to append messages to the command log.
667
+ """
668
+ self.execute(f"print(\"\\n\\n{msg}\")", hidden=True)
669
+
651
670
  def execute(self, source=None, hidden=False, interactive=False):
652
671
  # Log the command before execution
653
672
  source = source if source is not None else self.input_buffer
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import os
2
3
  from typing import Optional, List
3
4
 
4
5
  from IPython.core.magic import magics_class, Magics, line_magic
@@ -6,9 +7,9 @@ from IPython.terminal.embed import InteractiveShellEmbed
6
7
  from colorama import Fore, Style
7
8
  from traitlets.config import Config
8
9
 
10
+ from .template_file_creator import TemplateFileCreator
9
11
  from ..datastructures.dataclasses import CaseQuery
10
12
  from ..datastructures.enums import PromptFor
11
- from .template_file_creator import TemplateFileCreator
12
13
  from ..utils import contains_return_statement, extract_dependencies, encapsulate_code_lines_into_a_function
13
14
 
14
15
 
@@ -20,6 +21,7 @@ class MyMagics(Magics):
20
21
  prompt_for: Optional[PromptFor] = None,
21
22
  case_query: Optional[CaseQuery] = None):
22
23
  super().__init__(shell)
24
+ self.case_query: Optional[CaseQuery] = case_query
23
25
  self.rule_editor = TemplateFileCreator(case_query, prompt_for=prompt_for, code_to_modify=code_to_modify)
24
26
  self.all_code_lines: Optional[List[str]] = None
25
27
 
@@ -34,6 +36,29 @@ class MyMagics(Magics):
34
36
  self.rule_editor.print_func)
35
37
  self.shell.user_ns.update(updates)
36
38
 
39
+ @line_magic
40
+ def current_value(self, line):
41
+ """
42
+ Display the current value of the attribute of the case.
43
+ """
44
+ if self.case_query is None:
45
+ print(f"{Fore.RED}No case query provided.{Style.RESET_ALL}")
46
+ return
47
+ print(self.case_query.current_value_str)
48
+
49
+ @line_magic
50
+ def show_rule_tree(self, line):
51
+ """
52
+ Display the rule tree for the current case query.
53
+ """
54
+ if self.case_query is None:
55
+ print(f"{Fore.RED}No case query provided.{Style.RESET_ALL}")
56
+ return
57
+ if self.case_query.rdr is None:
58
+ print(f"{Fore.RED}No RDR available for the current case query.{Style.RESET_ALL}")
59
+ return
60
+ self.case_query.render_rule_tree(view=True)
61
+
37
62
  @line_magic
38
63
  def help(self, line):
39
64
  """
@@ -42,12 +67,16 @@ class MyMagics(Magics):
42
67
  help_text = f"""
43
68
  Directly write python code in the shell, and then `{Fore.GREEN}return {Fore.RESET}output`. Or use
44
69
  the magic commands to write the code in a temporary file and edit it in PyCharm:
45
- {Fore.MAGENTA}Usage: %edit{Style.RESET_ALL}
70
+ {Fore.MAGENTA}%edit{Style.RESET_ALL}
46
71
  Opens a temporary file in PyCharm for editing a function (conclusion or conditions for case)
47
72
  that will be executed on the case object.
48
- {Fore.MAGENTA}Usage: %load{Style.RESET_ALL}
73
+ {Fore.MAGENTA}%load{Style.RESET_ALL}
49
74
  Loads the function defined in the temporary file into the user namespace, that can then be used inside the
50
75
  Ipython shell. You can then do `{Fore.GREEN}return {Fore.RESET}function_name(case)`.
76
+ {Fore.MAGENTA}%current_value{Style.RESET_ALL}
77
+ Shows the current value of the case attribute on which the rule are being fit.
78
+ {Fore.MAGENTA}%show_rule_tree{Style.RESET_ALL}
79
+ Displays the rule tree for the current case query.
51
80
  """
52
81
  print(help_text)
53
82
 
@@ -144,7 +173,7 @@ class IPythonShell:
144
173
  self.user_input = None
145
174
  else:
146
175
  self.user_input = encapsulate_code_lines_into_a_function(self.all_code_lines,
147
- function_name=self.shell.my_magics.rule_editor.func_name,
148
- function_signature=self.shell.my_magics.rule_editor.function_signature,
149
- func_doc=self.shell.my_magics.rule_editor.func_doc,
150
- case_query=self.case_query)
176
+ function_name=self.shell.my_magics.rule_editor.func_name,
177
+ function_signature=self.shell.my_magics.rule_editor.function_signature,
178
+ func_doc=self.shell.my_magics.rule_editor.func_doc,
179
+ case_query=self.case_query)
@@ -1,7 +1,10 @@
1
1
  import ast
2
2
  import logging
3
+ import sys
3
4
  from _ast import AST
4
5
 
6
+ from .. import logger
7
+
5
8
  try:
6
9
  from PyQt6.QtWidgets import QApplication
7
10
  from .gui import RDRCaseViewer, style
@@ -29,7 +32,7 @@ class UserPrompt:
29
32
  """
30
33
  shell_lock: RLock = RLock() # To ensure that only one thread can access the shell at a time
31
34
 
32
- def __init__(self):
35
+ def __init__(self, prompt_user: bool = True):
33
36
  """
34
37
  Initialize the UserPrompt class.
35
38
  """
@@ -51,18 +54,19 @@ class UserPrompt:
51
54
  callable_expression: Optional[CallableExpression] = None
52
55
  while True:
53
56
  with self.shell_lock:
54
- user_input, expression_tree = self.prompt_user_about_case(case_query, prompt_for, prompt_str, code_to_modify=prev_user_input)
57
+ user_input, expression_tree = self.prompt_user_about_case(case_query, prompt_for, prompt_str,
58
+ code_to_modify=prev_user_input)
55
59
  if user_input is None:
56
60
  if prompt_for == PromptFor.Conclusion:
57
- self.print_func(f"{Fore.YELLOW}No conclusion provided. Exiting.{Style.RESET_ALL}")
61
+ self.print_func(f"\n{Fore.YELLOW}No conclusion provided. Exiting.{Style.RESET_ALL}")
58
62
  return None, None
59
63
  else:
60
- self.print_func(f"{Fore.RED}Conditions must be provided. Please try again.{Style.RESET_ALL}")
64
+ self.print_func(f"\n{Fore.RED}Conditions must be provided. Please try again.{Style.RESET_ALL}")
61
65
  continue
62
- elif user_input == "exit":
63
- self.print_func(f"{Fore.YELLOW}Exiting.{Style.RESET_ALL}")
66
+ elif user_input in ["exit", 'quit']:
67
+ self.print_func(f"\n{Fore.YELLOW}Exiting.{Style.RESET_ALL}")
64
68
  return user_input, None
65
-
69
+
66
70
  prev_user_input = '\n'.join(user_input.split('\n')[2:-1])
67
71
  conclusion_type = bool if prompt_for == PromptFor.Conditions else case_query.attribute_type
68
72
  callable_expression = CallableExpression(user_input, conclusion_type, expression_tree=expression_tree,
@@ -73,8 +77,9 @@ class UserPrompt:
73
77
  if len(make_list(result)) == 0 and (user_input_to_modify is not None
74
78
  and (prev_user_input != user_input_to_modify)):
75
79
  user_input_to_modify = prev_user_input
76
- self.print_func(f"{Fore.YELLOW}The given expression gave an empty result for case {case_query.name}."
77
- f" Please accept or modify!{Style.RESET_ALL}")
80
+ self.print_func(
81
+ f"{Fore.YELLOW}The given expression gave an empty result for case {case_query.name}."
82
+ f" Please accept or modify!{Style.RESET_ALL}")
78
83
  continue
79
84
  break
80
85
  except Exception as e:
@@ -82,7 +87,6 @@ class UserPrompt:
82
87
  self.print_func(f"{Fore.RED}{e}{Style.RESET_ALL}")
83
88
  return user_input, callable_expression
84
89
 
85
-
86
90
  def prompt_user_about_case(self, case_query: CaseQuery, prompt_for: PromptFor,
87
91
  prompt_str: Optional[str] = None,
88
92
  code_to_modify: Optional[str] = None) -> Tuple[Optional[str], Optional[AST]]:
@@ -95,7 +99,7 @@ class UserPrompt:
95
99
  :param code_to_modify: The code to modify. If given will be used as a start for user to modify.
96
100
  :return: The user input, and the executable expression that was parsed from the user input.
97
101
  """
98
- self.print_func("Entered shell")
102
+ logger.debug("Entered shell")
99
103
  initial_prompt_str = f"{prompt_str}\n" if prompt_str is not None else ''
100
104
  if prompt_for == PromptFor.Conclusion:
101
105
  prompt_for_str = f"Give possible value(s) for:"
@@ -103,20 +107,32 @@ class UserPrompt:
103
107
  prompt_for_str = f"Give conditions for:"
104
108
  case_query.scope.update({'case': case_query.case})
105
109
  shell = None
110
+
106
111
  if self.viewer is None:
112
+ prompt_for_str = prompt_for_str.replace(":", f" {case_query.name}:")
107
113
  prompt_str = f"{Fore.WHITE}{initial_prompt_str}{Fore.MAGENTA}{prompt_for_str}"
108
114
  prompt_str = self.construct_prompt_str_for_shell(case_query, prompt_for, prompt_str)
109
115
  shell = IPythonShell(header=prompt_str, prompt_for=prompt_for, case_query=case_query,
110
- code_to_modify=code_to_modify)
116
+ code_to_modify=code_to_modify)
111
117
  else:
112
- prompt_str = initial_prompt_str + prompt_for_str
113
- self.viewer.update_for_case_query(case_query, prompt_str,
114
- prompt_for=prompt_for, code_to_modify=code_to_modify)
118
+ prompt_str = case_query.current_value_str
119
+ self.viewer.update_for_case_query(case_query, prompt_for=prompt_for, code_to_modify=code_to_modify,
120
+ title=prompt_for_str, prompt_str=prompt_str)
115
121
  user_input, expression_tree = self.prompt_user_input_and_parse_to_expression(shell=shell)
116
- self.print_func("Exited shell")
122
+ logger.debug("Exited shell")
117
123
  return user_input, expression_tree
118
124
 
119
-
125
+ def build_prompt_str_for_ai(self, case_query: CaseQuery, prompt_for: PromptFor,
126
+ initial_prompt_str: Optional[str] = None) -> str:
127
+ initial_prompt_str = f"{initial_prompt_str}\n" if initial_prompt_str is not None else ''
128
+ if prompt_for == PromptFor.Conclusion:
129
+ prompt_for_str = f"Give possible value(s) for:"
130
+ else:
131
+ prompt_for_str = f"Give conditions for:"
132
+ prompt_for_str = prompt_for_str.replace(":", f" {case_query.name}:")
133
+ prompt_str = f"{Fore.WHITE}{initial_prompt_str}{Fore.MAGENTA}{prompt_for_str}"
134
+ prompt_str += '\n' + case_query.current_value_str
135
+ return prompt_str
120
136
 
121
137
  def construct_prompt_str_for_shell(self, case_query: CaseQuery, prompt_for: PromptFor,
122
138
  prompt_str: Optional[str] = None) -> str:
@@ -127,8 +143,7 @@ class UserPrompt:
127
143
  :param prompt_for: The type of information the user should provide for the given case.
128
144
  :param prompt_str: The prompt string to display to the user.
129
145
  """
130
- prompt_str += (f"\n{Fore.CYAN}{case_query.name}{Fore.MAGENTA} of type(s) "
131
- f"{Fore.CYAN}({', '.join(map(lambda x: x.__name__, case_query.core_attribute_type))}){Fore.MAGENTA}")
146
+ prompt_str += '\n' + case_query.current_value_str
132
147
  if prompt_for == PromptFor.Conditions:
133
148
  prompt_str += (f"\ne.g. `{Fore.GREEN}return {Fore.BLUE}len{Fore.RESET}(case.attribute) > {Fore.BLUE}0` "
134
149
  f"{Fore.MAGENTA}\nOR `{Fore.GREEN}return {Fore.YELLOW}True`{Fore.MAGENTA} (If you want the"
@@ -138,7 +153,6 @@ class UserPrompt:
138
153
  prompt_str = f"{Fore.MAGENTA}{prompt_str}{Fore.YELLOW}\n(Write %help for guide){Fore.RESET}\n"
139
154
  return prompt_str
140
155
 
141
-
142
156
  def prompt_user_input_and_parse_to_expression(self, shell: Optional[IPythonShell] = None,
143
157
  user_input: Optional[str] = None) \
144
158
  -> Tuple[Optional[str], Optional[ast.AST]]:
@@ -152,17 +166,18 @@ class UserPrompt:
152
166
  while True:
153
167
  if user_input is None:
154
168
  user_input = self.start_shell_and_get_user_input(shell=shell)
155
- if user_input is None or user_input == 'exit':
169
+ if user_input is None or user_input in ['exit', 'quit']:
156
170
  return user_input, None
157
- self.print_func(f"{Fore.GREEN}Captured User input: {Style.RESET_ALL}")
158
- highlighted_code = highlight(user_input, PythonLexer(), TerminalFormatter())
159
- self.print_func(highlighted_code)
171
+ if logger.level <= logging.DEBUG:
172
+ self.print_func(f"\n{Fore.GREEN}Captured User input: {Style.RESET_ALL}")
173
+ highlighted_code = highlight(user_input, PythonLexer(), TerminalFormatter())
174
+ self.print_func(highlighted_code)
160
175
  try:
161
176
  return user_input, parse_string_to_expression(user_input)
162
177
  except Exception as e:
163
178
  msg = f"Error parsing expression: {e}"
164
179
  logging.error(msg)
165
- self.print_func(f"{Fore.RED}{msg}{Style.RESET_ALL}")
180
+ self.print_func(f"\n{Fore.RED}{msg}{Style.RESET_ALL}")
166
181
  user_input = None
167
182
 
168
183
  def start_shell_and_get_user_input(self, shell: Optional[IPythonShell] = None) -> Optional[str]:
@@ -185,6 +200,6 @@ class UserPrompt:
185
200
  self.viewer.show()
186
201
  app.exec()
187
202
  if self.viewer.exit_status == ExitStatus.CLOSE:
188
- exit(0)
203
+ sys.exit()
189
204
  user_input = self.viewer.user_input
190
205
  return user_input
@@ -109,20 +109,21 @@ class TemplateFileCreator:
109
109
 
110
110
  self.open_file_in_editor()
111
111
 
112
- def open_file_in_editor(self):
112
+ def open_file_in_editor(self, file_path: Optional[str] = None):
113
113
  """
114
114
  Open the file in the available editor.
115
115
  """
116
+ file_path = file_path or self.temp_file_path
116
117
  if self.editor_cmd is not None:
117
- subprocess.Popen([self.editor_cmd, self.temp_file_path],
118
+ subprocess.Popen([self.editor_cmd, file_path],
118
119
  stdout=subprocess.DEVNULL,
119
120
  stderr=subprocess.DEVNULL)
120
121
  elif self.editor == Editor.Pycharm:
121
- subprocess.Popen(["pycharm", "--line", str(self.user_edit_line), self.temp_file_path],
122
+ subprocess.Popen(["pycharm", "--line", str(self.user_edit_line), file_path],
122
123
  stdout=subprocess.DEVNULL,
123
124
  stderr=subprocess.DEVNULL)
124
125
  elif self.editor == Editor.Code:
125
- subprocess.Popen(["code", self.temp_file_path])
126
+ subprocess.Popen(["code", file_path])
126
127
  elif self.editor == Editor.CodeServer:
127
128
  try:
128
129
  subprocess.check_output(["pgrep", "-f", "code-server"])
@@ -134,7 +135,8 @@ class TemplateFileCreator:
134
135
  except (subprocess.CalledProcessError, ValueError) as e:
135
136
  self.process = start_code_server(self.workspace)
136
137
  self.print_func(f"Open code-server in your browser at http://localhost:{self.port}?folder={self.workspace}")
137
- self.print_func(f"Edit the file: {Fore.MAGENTA}{self.temp_file_path}")
138
+ if file_path.endswith('.py'):
139
+ self.print_func(f"Edit the file: {Fore.MAGENTA}{file_path}")
138
140
 
139
141
  def build_boilerplate_code(self):
140
142
  imports = self.get_imports()
@@ -183,7 +185,7 @@ class TemplateFileCreator:
183
185
  func_args = ', '.join([f"{k}: {v}" if str(v) not in ["NoneType", "None"] else str(k)
184
186
  for k, v in func_args.items()])
185
187
  else:
186
- func_args = f"case: {self.case_type.__name__}"
188
+ func_args = f"case: {self.case_query.case_type.__name__}"
187
189
  return func_args
188
190
 
189
191
  def write_to_file(self, code: str):
@@ -212,7 +214,7 @@ class TemplateFileCreator:
212
214
  else:
213
215
  case_type_imports.append(v)
214
216
  else:
215
- case_type_imports.append(self.case_type)
217
+ case_type_imports.append(self.case_query.case_type)
216
218
  if self.output_type is None:
217
219
  output_type_imports = [Any]
218
220
  else:
@@ -302,7 +304,7 @@ class TemplateFileCreator:
302
304
  exec(source, scope, exec_globals)
303
305
  user_function = exec_globals[func_name]
304
306
  updates[func_name] = user_function
305
- print_func(f"{Fore.BLUE}Loaded `{func_name}` function into user namespace.{Style.RESET_ALL}")
307
+ print_func(f"\n{Fore.WHITE}Loaded the following function into user namespace:\n{Fore.GREEN}{func_name}{Style.RESET_ALL}")
306
308
  break
307
309
  if updates:
308
310
  all_code_lines = extract_function_source(file_path,