def test_execute_synchronous_no_links_not_enough_kwargs( self, sum_op, square_op, negative_op): # TODO -- expect exception workflow = Workflow() workflow.add_operations(sum_op, square_op, negative_op) results = workflow.execute_synchronous(n1=2, n2=5) assert results == ({"negative": -49}, )
def __init__(self): self.workflow = Workflow() self.headermodel = QStandardItemModel() # self.alignmenttabview = TabView(self.headermodel) self.rawtabview = TabView(self.headermodel, widgetcls=RAWViewer, field='primary') self.recontabs = QTabWidget() self.workfloweditor = WorkflowEditor(self.workflow) self.workfloweditor.setHidden(True) self.tomotoolbar = TomoToolbar() self.tomotoolbar.sigSliceReconstruction.connect(self.sliceReconstruct) self.tomotoolbar.sigFullReconstruction.connect(self.fullReconstruction) self.stages = { 'Alignment': GUILayout(QLabel('Alignment'), right=self.workfloweditor, top=self.tomotoolbar), 'Preprocess': GUILayout(self.rawtabview, right=self.workfloweditor, top=self.tomotoolbar), 'Reconstruct': GUILayout(self.recontabs, top=self.tomotoolbar, right=self.workfloweditor), } super(TomographyPlugin, self).__init__()
def __init__(self): # Set up an invert operation @OperationPlugin @output_names('inverted_data') def invert(data: np.ndarray = None) -> np.ndarray: if issubclass(data.dtype.type, np.integer): max = np.iinfo(data.dtype).max else: max = np.finfo(data.dtype).max return max - data # Set up our workflow workflow = Workflow() workflow.add_operation(invert) workflow_editor = WorkflowEditor(workflow) # Set up a GUI layout center_widget = QLabel("test") sample_stage_layout = GUILayout(center_widget) # Set up stages stages = { "Sample Stage": GUILayout(center_widget, right=workflow_editor) } self.stages = stages super(SamplePlugin, self).__init__()
def test_execute_no_links_diff_input_names(self, sum_op, square_op, negative_op): # do the input names have to match in this case (more than one entry op) operations = [sum_op, square_op, negative_op] workflow = Workflow(name="test", operations=operations) results = workflow.execute(n1=3, n2=-3, n=10, num=33).result() assert len(results) == 3 assert {"sum": 0} in results assert {"square": 100} in results assert {"negative": -33} in results
def custom_parameter_workflow(custom_parameter_op): from xicam.core.execution.workflow import Workflow wf = Workflow() custom_parameter_op = custom_parameter_op() wf.add_operation(custom_parameter_op) return wf
def test_execute_operation_no_default_no_value(self, sum_op): # not sure how to test this.... def handle_exception(exception): with pytest.raises(TypeError): raise exception workflow = Workflow() workflow.add_operation(sum_op) results = workflow.execute(except_slot=handle_exception).result() print(results)
def test_execute_operation_default_input_value(self): @operation @output_names("doubled") def double_op(x=10): return x * 2 workflow = Workflow() workflow.add_operation(double_op) results = workflow.execute().result() assert results == ({"doubled": 20}, )
def test_no_output_names(self): @operation def my_func(a1, a2): return a1, a2 op = my_func() w = Workflow() w.add_operations(op) result = w.execute_synchronous(a1=1, a2=2) assert result == ({"my_func": 1}, )
def test_end_node(self, double_and_triple_op, sum_op, square_op): workflow = Workflow() workflow.add_operations(double_and_triple_op, sum_op, square_op) workflow.add_link(sum_op, square_op, "sum", "n") workflow.add_link(square_op, double_and_triple_op, "square", "n") result = workflow.execute_synchronous(n1=1, n2=2) assert result == ({"double": 18, "triple": 27}, )
def test_execute_synchronous(self, sum_op, square_op, negative_op): workflow = Workflow() workflow.add_operations(sum_op, square_op, negative_op) workflow.add_link(sum_op, square_op, "sum", "n") workflow.add_link(square_op, negative_op, "square", "num") results = workflow.execute_synchronous(n1=2, n2=5) assert results == ({"negative": -49}, )
def test_one_output_name(self): @operation @output_names("return_val") def my_func(a1, a2): return a1, a2 op = my_func() workflow = Workflow() workflow.add_operations(op) result = workflow.execute_synchronous(a1=1, a2=2) assert result == ({"return_val": 1}, )
def test_execute_synchronous_no_links(self, sum_op, square_op, negative_op): workflow = Workflow() workflow.add_operations(sum_op, square_op, negative_op) # n1, n2 -- inputs to sum_op; # n -- input to square_op # num -- input to negative_op results = workflow.execute_synchronous(n1=2, n2=5, n=10, num=50) assert len(results) == 3 assert {"sum": 7} in results assert {"square": 100} in results assert {"negative": -50} in results
def test_start_node(self, double_and_triple_op, sum_op, square_op): workflow = Workflow() workflow.add_operations(double_and_triple_op, sum_op, square_op) workflow.add_link(double_and_triple_op, sum_op, "double", "n1") workflow.add_link(double_and_triple_op, sum_op, "triple", "n2") workflow.add_link(sum_op, square_op, "sum", "n") result = workflow.execute_synchronous(n=1) assert result == ({"square": 25}, )
def doCalibrateWorkflow(self, workflow: Workflow): data = self.calibrationtabview.currentWidget().header.meta_array('primary')[0] device = self.calibrationpanel.parameter['Device'] ai = self.calibrationsettings.AI('pilatus2M') ai.detector = detectors.Pilatus2M() c = calibrant.ALL_CALIBRANTS('AgBh') def setAI(result): self.calibrationsettings.setAI(result['ai'].value, device) self.doMaskingWorkflow(self.maskingworkflow) workflow.execute(None, data=data, ai=ai, calibrant=c, callback_slot=setAI, threadkey='calibrate')
def doDisplayWorkflow(self, workflow: Workflow): currentwidget = self.reducetabview.currentWidget() data = currentwidget.header.meta_array('primary')[currentwidget.timeIndex(currentwidget.timeLine)[0]] ai = self.calibrationsettings.AI('pilatus2M') ai.detector = detectors.Pilatus2M() mask = self.maskingworkflow.lastresult[0]['mask'].value if self.maskingworkflow.lastresult else None outputwidget = currentwidget def showDisplay(*results): outputwidget.setResults(results) workflow.execute(None, data=data, ai=ai, mask=mask, callback_slot=showDisplay, threadkey='display')
def test_all_output_names(self): @operation @output_names('x', 'y') def my_func(a1, a2): return a1, a2 op = my_func() workflow = Workflow() workflow.add_operations(op) result = workflow.execute_synchronous(a1=1, a2=2) print(result) assert result == ({'x': 1, 'y': 2}, )
def doSimulateWorkflow(self, workflow: Workflow): data = self.calibrationtabview.currentWidget().header.meta_array('primary')[0] ai = self.calibrationsettings.AI('pilatus2M') ai.detector = detectors.Pilatus2M() calibrant = self.calibrationpanel.parameter['Calibrant Material'] outputwidget = self.calibrationtabview.currentWidget() def showSimulatedCalibrant(result=None): outputwidget.setCalibrantImage(result['data'].value) workflow.execute(None, data=data, ai=ai, calibrant=calibrant, callback_slot=showSimulatedCalibrant, threadkey='simulate')
def test_mutliple_end_nodes(double_and_triple_op, sum_op, square_op): workflow = Workflow() workflow.add_operations(double_and_triple_op, sum_op, square_op) workflow.add_link(sum_op, double_and_triple_op, "sum", "n") workflow.add_link(sum_op, square_op, "sum", "n") result = workflow.execute_synchronous(n1=2, n2=3) assert len(result) == 2 assert {"double": 10, "triple": 15} in result assert {"square": 25} in result
def __init__(self, workflow: Workflow): super(WorkflowEditor, self).__init__() self.setOrientation(Qt.Vertical) self.processeditor = WorkflowProcessEditor() self.workflowview = LinearWorkflowView(WorkflowModel(workflow)) self.addWidget(self.processeditor) self.addWidget(WorkflowWidget(self.workflowview)) self.workflowview.sigShowParameter.connect(lambda parameter: self.setParameters(parameter)) workflow.attach(self.sigWorkflowChanged.emit)
def __init__(self, workflow: Workflow, operation_filter: Callable[[OperationPlugin], bool] = None, kwargs_callable: Callable[[], dict] = None, execute_iterative: bool = False, **kwargs): """ A Workflow editor that shows each operation in insertion order. This is useful in simplistic workflows, typically when data passes from through a linear sequence. This order may not represent execution order when a workflow is programmatically composed, or when graph-based editing is supported in the future. Parameters ---------- workflow : Workflow A workflow instance; may be initially empty. operation_filter: Callable[[OperationPlugin], bool] A callable that can be used to filter which operations to show in the "Add Operation" menu kwargs_callable: Callable[[], dict] A callable that gets called when auto-run is triggered. This callable is expected to generate a dict of kwargs that will be passed into the workflow as inputs. execute_iterative: bool Determines if the attached workflow will be executed with `.execute` or `.execute_all`. When `.execute_all` is used, all input args get zipped, and the workflow is executed over each arg tuple. """ super(WorkflowEditor, self).__init__() self.workflow = workflow self.kwargs = kwargs self.kwargs_callable = kwargs_callable self.execute_iterative = execute_iterative self.setOrientation(Qt.Vertical) self.operationeditor = WorkflowOperationEditor() self.workflowview = LinearWorkflowView(WorkflowModel(workflow)) self.addWidget(self.operationeditor) workflow_widget = WorkflowWidget(self.workflowview, operation_filter=operation_filter) self.addWidget(workflow_widget) workflow_widget.sigRunWorkflow.connect(self.sigRunWorkflow.emit) workflow_widget.sigRunWorkflow.connect(self.run_workflow) # Should this work internally? How would the start operations get their inputs? # Would the ExamplePlugin need to explicitly set the parameter value (even for hidden image)? # It would be nice to just have this work easily... (to ExamplePlugin's perspective) # workflow_widget.sigRunWorkflow.connect(self.run_workflow) # TODO make work for autorun... # OR is this the outside class's respsonsibility (see SAXSGUIPlugin.maskeditor) self.workflowview.sigShowParameter.connect(self.setParameters) workflow.attach(self.sigWorkflowChanged.emit)
def doCalibrateWorkflow(self, workflow: Workflow): data = self.calibrationtabview.currentWidget().image data = self.checkDataShape(data) if data is None: return device = self.rawtoolbar.detectorcombobox.currentText() ai = self.calibrationsettings.AI(device) # ai.detector = detectors.Pilatus2M() calibrant = self.calibrationpanel.parameter['Calibrant Material'] def setAI(result): self.calibrationsettings.setAI(result['ai'].value, device) self.doMaskingWorkflow() workflow.execute(None, data=data, ai=ai, calibrant=calibrant, callback_slot=setAI, threadkey='calibrate')
def __init__(self, workflow: Workflow): super(WorkflowEditor, self).__init__() self.workflow = workflow self.setOrientation(Qt.Vertical) self.operationeditor = WorkflowOperationEditor() self.workflowview = LinearWorkflowView(WorkflowModel(workflow)) self.addWidget(self.operationeditor) self.addWidget(WorkflowWidget(self.workflowview)) self.workflowview.sigShowParameter.connect(self.setParameters) workflow.attach(self.sigWorkflowChanged.emit)
def test_execute_all(self, qtbot, sum_op, square_op, negative_op): results = [{"negative": (1 + 2) ** 2 * -1}, {"negative": (3 + 4) ** 2 * -1}, {"negative": (5 + 6) ** 2 * -1}] def cb(*result): next_result = results.pop(0) assert result == next_result workflow = Workflow() workflow.add_operations(sum_op, square_op, negative_op) workflow.add_link(sum_op, square_op, "sum", "n") workflow.add_link(square_op, negative_op, "square", "num") n1_values = [1, 3, 5] n2_values = [2, 4, 6] # TODO -- we are only getting one result, should get three (3 pairs of n1/n2). workflow.execute_all(callback_slot=cb, n1=n1_values, n2=n2_values).result()
def doMaskingWorkflow(self, workflow: Workflow): if not self.checkPolygonsSet(workflow): data = self.calibrationtabview.currentWidget().header.meta_array('primary')[0] ai = self.calibrationsettings.AI('pilatus2M') ai.detector = detectors.Pilatus2M() outputwidget = self.masktabview.currentWidget() def showMask(result=None): if result: outputwidget.setMaskImage(result['mask'].value) else: outputwidget.setMaskImage(None) self.doDisplayWorkflow(self.displayworkflow) self.doReduceWorkflow(self.reduceworkflow) workflow.execute(None, data=data, ai=ai, callback_slot=showMask, threadkey='masking')
def test_two_ops_to_one_op(self, negative_op, square_op, sum_op): workflow = Workflow() workflow.add_operations(negative_op, square_op, sum_op) workflow.add_link(negative_op, sum_op, "negative", "n1") workflow.add_link(square_op, sum_op, "square", "n2") print(workflow.get_inbound_links(sum_op)) #from dask import visualize #visualize(workflow.as_dask_graph()[0], filename="/home/ihumphrey/graph") graph = workflow.as_dask_graph()[0] print(graph) for k, op in graph.items(): print(k, op[0].node.name) from dask.threaded import get negative_op.filled_values.update(num=3) square_op.filled_values.update(n=4) print(get(graph, '0')) # WHY is this an issue? Complains that sum missing required 'n2'
def test_execute_no_links(self): @operation @output_names("doubled") def double_op(n): return n * 2 @operation @output_names("tripled") def triple_op(n): return n * 3 workflow = Workflow() workflow.add_operations(double_op, triple_op) results = workflow.execute(n=10).result() assert len(results) == 2 assert {"doubled": 20} in results assert {"tripled": 30} in results
def test_workflow(): from xicam.core.execution.workflow import Workflow from xicam.core.execution.daskexecutor import DaskExecutor from xicam.plugins.operationplugin import output_names executor = DaskExecutor() @operation @output_names("square") def square(a=3) -> int: return a**2 @operation @output_names("sum") def my_sum(a, b=3) -> int: return a + b wf = Workflow() square = square() my_sum = my_sum() wf.add_operation(square) wf.add_operation(my_sum) wf.add_link(square, my_sum, "square", "a") assert wf.execute_synchronous(executor=executor) == [{"sum": 12}]
def doReduceWorkflow(self, workflow: Workflow): multimode = self.reduceplot.toolbar.multiplot.isChecked() currentwidget = self.reducetabview.currentWidget() data = currentwidget.header.meta_array('primary') if not multimode: data = [data[currentwidget.timeIndex(currentwidget.timeLine)[0]]] ai = self.calibrationsettings.AI('pilatus2M') ai.detector = detectors.Pilatus2M() ai = [ai] * len(data) mask = [self.maskingworkflow.lastresult[0]['mask'].value if self.maskingworkflow.lastresult else None] * len( data) outputwidget = self.reduceplot outputwidget.clear() def showReduce(*results): outputwidget.appendResult(results) workflow.execute_all(None, data=data, ai=ai, mask=mask, callback_slot=showReduce, threadkey='reduce')
def test_default(self, op, old): w = Workflow(operations=[op]) result = w.execute_synchronous(executor=executor) corrected_images = result[0]['corrected_images'] gains = get_default(op, 'gains') assert np.array_equal(corrected_images, op.filled_values['images'] * gains[0]) if TEST_CSX_TOOLS: w.clear_operations() w.add_operation(old) result = w.execute_synchronous(executor=executor) assert np.array_equal(result[0]['corrected_images'], corrected_images)
def test_execute_all(self, sum_op, square_op, negative_op): workflow = Workflow() workflow.add_operations(sum_op, square_op, negative_op) workflow.add_link(sum_op, square_op, "sum", "n") workflow.add_link(square_op, negative_op, "square", "num") n1_values = [1, 3, 5] n2_values = [2, 4, 6] # TODO -- we are only getting one result, should get three (3 pairs of n1/n2). results = list( workflow.execute_all(n1=n1_values, n2=n2_values).result()) assert results == [{ "negative": (1 + 2)**2 * -1 }, { "negative": (3 + 4)**2 * -1 }, { "negative": (5 + 6)**2 * -1 }]