Skip to content

Commit

Permalink
Merge pull request #241 from tangkong/enh_template_gui
Browse files Browse the repository at this point in the history
GUI/ENH: add template steps to passive and active checkout GUI
  • Loading branch information
tangkong authored Jun 6, 2024
2 parents 1ed08e5 + b8ff61e commit 11b15c0
Show file tree
Hide file tree
Showing 12 changed files with 591 additions and 39 deletions.
32 changes: 27 additions & 5 deletions atef/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from .cache import DataCache
from .check import Comparison
from .enums import GroupResultMode, Severity
from .exceptions import PreparedComparisonException
from .exceptions import PreparationError, PreparedComparisonException
from .result import Result, incomplete_result
from .type_hints import AnyPath
from .yaml_support import init_yaml_support
Expand Down Expand Up @@ -1307,16 +1307,29 @@ def from_config(

# convert and apply edits
edits = [e.to_action() for e in config.edits]
for edit in edits:
edit.apply(target=config_file)
edit_results = [edit.apply(target=config_file) for edit in edits]

if not all(edit_results):
return FailedConfiguration(
config=config,
parent=parent,
exception=PreparationError,
result=Result(
severity=Severity.internal_error,
reason=(
f'Failed to prepare templated config: ({config.name}) '
f'Could not apply all edits.'
)
)
)

# verify edited file
success, msg = config_file.validate()
if not success:
return FailedConfiguration(
config=config,
parent=parent,
exception=ValueError, # TODO: get a better exception
exception=PreparationError,
result=Result(
severity=Severity.internal_error,
reason=(
Expand All @@ -1339,11 +1352,20 @@ def from_config(
return prepared

async def compare(self) -> Result:
"""Run the edited checkoutand return the combined result"""
"""Run the edited checkout and return the combined result"""
result = await self.file.compare()
self.combined_result = result
return result

@property
def result(self) -> Result:
"""
Re-compute combined result and return it. Override standard since this
configuration has no comparisons
"""
self.combined_result = self.file.root.result
return self.combined_result


@dataclass
class PreparedComparison:
Expand Down
5 changes: 5 additions & 0 deletions atef/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ class UnpreparedComparisonException(ComparisonException):
...


class PreparationError(Exception):
"""Raise this when failing to prepare a configuration or procedure"""
...


class PreparedComparisonException(Exception):
"""Exception caught during preparation of comparisons."""
#: The exception instance itself.
Expand Down
53 changes: 49 additions & 4 deletions atef/find_replace.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ def patch_client_cache():
try:
happi.loader.cache = {}
dcache = DataCache()
# Clear the global signal cache to prevent previous signals from leaking
dcache.signals.clear()
yield
finally:
happi.loader.cache = old_happi_cache
dcache.signals.clear()


def walk_find_match(
Expand Down Expand Up @@ -138,6 +138,38 @@ def simplify_path(path: List[Tuple[Any, Any]]) -> List[Tuple[str, Any]]:
return simplified_path


def expand_path(path: List[Tuple[str, Any]], target: Any) -> List[Tuple[Any, Any]]:
"""
Expands ``path`` using ``target`` as the object to traverse.
Replaces all string type references with the object in question.
The inverse of ``simplify_path``
Parameters
----------
path : List[Tuple[str, Any]]
the simplified path to expand
target : Any
the object that ``path`` is referring to
Returns
-------
List[Tuple[Any, Any]]
the expanded path
"""
new_path = []
new_path.append((target, path[0][1]))
# Look at all path segments after index 0, replace the next object in line.
for idx in range(len(path) - 1):
if path[idx+1][0].startswith("__") and path[idx+1][0].endswith("__"):
seg_object = path[idx+1][0]
else:
seg_object = get_item_from_path(path[:idx+1], item=target)
new_path.append((seg_object, path[idx + 1][1]))

return new_path


def get_item_from_path(
path: List[Tuple[Any, Any]],
item: Optional[Any] = None
Expand Down Expand Up @@ -323,15 +355,26 @@ class RegexFindReplace:

def to_action(self, target: Optional[Any] = None) -> FindReplaceAction:
"""Create FindReplaceAction from a SerializableFindReplaceAction"""
flags = re.IGNORECASE if not self.case_sensitive else 0
try:
re.compile(self.search_regex)
search_regex = re.compile(self.search_regex, flags=flags)
except re.error:
raise ValueError(f'regex is not valid: {self.search_regex}, '
'could not construct FindReplaceAction')
replace_fn = get_default_replace_fn(
self.replace_text, re.compile(self.search_regex)
self.replace_text, search_regex
)
if target:
path = expand_path(self.path, target=target)
else:
path = self.path

return FindReplaceAction(
path=path,
replace_fn=replace_fn,
target=target,
origin=self,
)
return FindReplaceAction(target=target, path=self.path, replace_fn=replace_fn)


@dataclass
Expand All @@ -341,6 +384,8 @@ class FindReplaceAction:
# Union[ConfigurationFile, ProcedureFile], but circular imports
target: Optional[Any] = None

origin: Optional[RegexFindReplace] = None

def apply(
self,
target: Optional[Union[ConfigurationFile, ProcedureFile]] = None,
Expand Down
25 changes: 21 additions & 4 deletions atef/procedure.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from atef.config import (ConfigurationFile, PreparedComparison, PreparedFile,
PreparedSignalComparison, run_passive_step)
from atef.enums import GroupResultMode, PlanDestination, Severity
from atef.exceptions import PreparedComparisonException
from atef.exceptions import PreparationError, PreparedComparisonException
from atef.find_replace import RegexFindReplace
from atef.plan_utils import (BlueskyState, GlobalRunEngine,
get_default_namespace, register_run_identifier,
Expand Down Expand Up @@ -616,6 +616,10 @@ def from_origin(
return PreparedPlanStep.from_origin(
origin=step, parent=parent
)
if isinstance(step, TemplateStep):
return PreparedTemplateStep.from_origin(
step=step, parent=parent,
)

raise NotImplementedError(f"Step type unsupported: {type(step)}")
except Exception as ex:
Expand Down Expand Up @@ -940,16 +944,29 @@ def from_origin(

# convert and apply edits
edits = [e.to_action() for e in step.edits]
for edit in edits:
edit.apply(target=orig_file)
edit_results = [edit.apply(target=orig_file) for edit in edits]

if not all(edit_results):
return FailedStep(
origin=step,
parent=parent,
exception=PreparationError,
result=Result(
severity=Severity.internal_error,
reason=(
f'Failed to prepare templated config: ({step.name}) '
f'Could not apply all edits.'
)
)
)

# verify edited file
success, msg = orig_file.validate()
if not success:
return FailedStep(
origin=step,
parent=parent,
exception=ValueError, # TODO: get a better exception
exception=PreparationError,
combined_result=Result(
severity=Severity.internal_error,
reason=(
Expand Down
24 changes: 24 additions & 0 deletions atef/tests/test_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pytestqt.qtbot import QtBot
from qtpy import QtCore, QtWidgets

from atef.config import TemplateConfiguration
from atef.type_hints import AnyDataclass
from atef.widgets.config.page import ComparisonPage, ConfigurationGroupPage

Expand Down Expand Up @@ -178,3 +179,26 @@ def condition():
full_tree.select_by_data(group_data)
full_tree.select_by_data(new_data)
comp_page = full_tree.current_widget


def test_template_page(
qtbot: QtBot,
template_configuration: TemplateConfiguration,
make_page: Callable,
):
group_page = make_page(template_configuration)

# Does the configuration initialize properly?
qtbot.wait_until(
lambda: group_page.template_page_widget.staged_list.count() == 1
)

# test preparation
group_page.full_tree.mode = 'run'
group_page.full_tree.switch_mode('run')

qtbot.wait_signal(group_page.full_tree.mode_switch_finished)
qtbot.wait_until(
lambda: group_page.template_page_widget.staged_list.count() == 1
)
qtbot.addWidget(group_page)
41 changes: 41 additions & 0 deletions atef/ui/template_group_page.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>380</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="name_desc_tags_placeholder" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="template_page_placeholder" native="true"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
41 changes: 41 additions & 0 deletions atef/ui/template_run_widget.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>342</width>
<height>297</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeView" name="tree_view"/>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="edits_list"/>
</item>
<item>
<widget class="QPushButton" name="refresh_button">
<property name="text">
<string>Refresh Status Icons</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
Loading

0 comments on commit 11b15c0

Please sign in to comment.