tomwer 1.2.0a2__py3-none-any.whl → 1.2.0a4__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.
Files changed (34) hide show
  1. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +0 -6
  2. orangecontrib/tomwer/widgets/reconstruction/DarkRefAndCopyOW.py +6 -2
  3. orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +169 -169
  4. tomwer/app/canvas_launcher/mainwindow.py +0 -3
  5. tomwer/app/imagekeyeditor.py +1 -0
  6. tomwer/app/imagekeyupgrader.py +2 -0
  7. tomwer/app/nxtomoeditor.py +2 -0
  8. tomwer/app/zstitching.py +1 -0
  9. tomwer/core/process/reconstruction/nabu/nabucommon.py +57 -12
  10. tomwer/core/process/reconstruction/nabu/nabuscores.py +3 -2
  11. tomwer/core/process/reconstruction/nabu/nabuslices.py +7 -9
  12. tomwer/core/process/reconstruction/nabu/nabuvolume.py +10 -9
  13. tomwer/core/process/reconstruction/nabu/utils.py +10 -36
  14. tomwer/core/process/test/test_nabu.py +5 -5
  15. tomwer/gui/visualization/reconstructionparameters.py +9 -1
  16. tomwer/gui/visualization/volumeviewer.py +2 -0
  17. tomwer/resources/gui/icons/esrf_1.svg +307 -0
  18. tomwer/resources/gui/icons/triangle.svg +80 -0
  19. tomwer/version.py +1 -1
  20. {tomwer-1.2.0a2.dist-info → tomwer-1.2.0a4.dist-info}/METADATA +1 -1
  21. {tomwer-1.2.0a2.dist-info → tomwer-1.2.0a4.dist-info}/RECORD +27 -32
  22. tomwer/third_party/__init__.py +0 -0
  23. tomwer/third_party/nabu/__init__.py +0 -0
  24. tomwer/third_party/nabu/preproc/__init__.py +0 -0
  25. tomwer/third_party/nabu/preproc/phase.py +0 -387
  26. tomwer/third_party/nabu/utils.py +0 -201
  27. tomwer/third_party/tango/__init__.py +0 -0
  28. tomwer/third_party/tango/device.py +0 -15
  29. /tomwer-1.2.0a2-py3.11-nspkg.pth → /tomwer-1.2.0a4-py3.11-nspkg.pth +0 -0
  30. {tomwer-1.2.0a2.dist-info → tomwer-1.2.0a4.dist-info}/LICENSE +0 -0
  31. {tomwer-1.2.0a2.dist-info → tomwer-1.2.0a4.dist-info}/WHEEL +0 -0
  32. {tomwer-1.2.0a2.dist-info → tomwer-1.2.0a4.dist-info}/entry_points.txt +0 -0
  33. {tomwer-1.2.0a2.dist-info → tomwer-1.2.0a4.dist-info}/namespace_packages.txt +0 -0
  34. {tomwer-1.2.0a2.dist-info → tomwer-1.2.0a4.dist-info}/top_level.txt +0 -0
@@ -139,9 +139,6 @@ class SingleTomoObjOW(OWBaseWidget, openclass=True):
139
139
  if isinstance(data, TomwerScanBase):
140
140
  if data.reduced_darks not in (None, {}):
141
141
  reduced_darks = data.reduced_darks
142
- reduced_darks[
143
- "reduce_frames_name"
144
- ] = f"darks from {data.get_identifier().short_description()}"
145
142
  self.Outputs.reduced_darks.send(reduced_darks)
146
143
 
147
144
  # we want to send those in relative position to have something generic. This is a convention for now
@@ -157,9 +154,6 @@ class SingleTomoObjOW(OWBaseWidget, openclass=True):
157
154
 
158
155
  if data.reduced_flats not in (None, {}):
159
156
  reduced_flats = data.reduced_flats
160
- reduced_flats[
161
- "reduce_frames_name"
162
- ] = f"flats from {data.get_identifier().short_description()}"
163
157
  self.Outputs.reduced_flats.send(reduced_flats)
164
158
 
165
159
  # we want to send those in relative position to have something generic. This is a convention for now
@@ -246,16 +246,20 @@ class DarkRefAndCopyOW(SuperviseOW, WidgetLongProcessing):
246
246
  self.sigScanReady.emit(scan)
247
247
  if scan.reduced_darks not in (None, {}):
248
248
  # we want to send those in relative position to have something generic. This is a convention for now
249
+ reduced_darks = scan.reduced_darks
250
+ reduced_darks.pop("reduce_frames_name", None)
249
251
  self.Outputs.reduced_darks.send(
250
252
  tomoscan.esrf.scan.utils.from_absolute_reduced_frames_to_relative(
251
- reduced_frames=scan.reduced_darks, scan=scan
253
+ reduced_frames=reduced_darks, scan=scan
252
254
  )
253
255
  )
254
256
  if scan.reduced_flats not in (None, {}):
255
257
  # we want to send those in relative position to have something generic. This is a convention for now
258
+ reduced_flats = scan.reduced_flats
259
+ reduced_flats.pop("reduce_frames_name", None)
256
260
  self.Outputs.reduced_flats.send(
257
261
  tomoscan.esrf.scan.utils.from_absolute_reduced_frames_to_relative(
258
- reduced_frames=scan.reduced_flats, scan=scan
262
+ reduced_frames=reduced_flats, scan=scan
259
263
  )
260
264
  )
261
265
  _logger.info(f"{scan} ended")
@@ -1,169 +1,169 @@
1
- import logging
2
-
3
- from orangewidget import gui
4
- from orangewidget.settings import Setting
5
- from orangewidget.widget import Input, Output
6
- from orangecontrib.tomwer.orange.managedprocess import TomwerWithStackStack
7
- from silx.gui import qt
8
- from typing import Optional
9
-
10
- from tomwer.core.futureobject import FutureTomwerObject
11
- from tomwer.core.scan.scanbase import TomwerScanBase
12
- from tomwer.gui.reconstruction.nabu.helical import HelicalPrepareWeightsDouble
13
- from tomwer.core.process.reconstruction.nabu.helical import (
14
- NabuHelicalPrepareWeightsDouble,
15
- )
16
- from tomwer.core.scan.hdf5scan import HDF5TomoScan
17
-
18
- _logger = logging.getLogger(__name__)
19
-
20
-
21
- class NabuHelicalPrepareWeightsDoubleOW(
22
- TomwerWithStackStack,
23
- ewokstaskclass=NabuHelicalPrepareWeightsDouble,
24
- ):
25
- """
26
- widget used to call the `nabu-helical-prepare-weights-double` application on a dedicated thread. It define weights map and double flat field.
27
-
28
- :param parent: the parent widget
29
- """
30
-
31
- # note of this widget should be the one registered on the documentation
32
- name = "helical prepate weights double"
33
- id = "orangecontrib.tomwer.widgets.reconstruction.NabuHelicalPrepareWeightsDoubleOW.NabuHelicalPrepareWeightsDoubleOW"
34
- description = "compute map of weights requested for nabu helical reconstruction"
35
- icon = "icons/nabu_prepare_weights_double.svg"
36
- priority = 199
37
- keywords = [
38
- "tomography",
39
- "nabu",
40
- "reconstruction",
41
- "nabu-helical",
42
- "helical",
43
- "weights",
44
- "prepare",
45
- ]
46
-
47
- want_main_area = True
48
- want_control_area = False
49
- resizing_enabled = True
50
-
51
- _ewoks_default_inputs = Setting(
52
- {
53
- "data": None,
54
- "transition_width": 50,
55
- "processes_file": "",
56
- }
57
- )
58
-
59
- sigScanReady = qt.Signal(TomwerScanBase)
60
- "Signal emitted when a scan is ended"
61
-
62
- TIMEOUT = 30
63
-
64
- class Inputs:
65
- data = Input(
66
- name="data",
67
- type=TomwerScanBase,
68
- doc="one scan to be process",
69
- default=True,
70
- multiple=False,
71
- )
72
-
73
- class Outputs:
74
- data = Output(name="data", type=TomwerScanBase, doc="one scan to be process")
75
- future_tomo_obj = Output(
76
- name="future_tomo_obj",
77
- type=FutureTomwerObject,
78
- doc="future object (process remotely)",
79
- )
80
-
81
- LOGGER = _logger
82
-
83
- def __init__(self, parent=None):
84
- super().__init__(parent)
85
- self.__scan = None
86
-
87
- # gui definition
88
- _layout = gui.vBox(self.mainArea, self.name).layout()
89
- self.widget = HelicalPrepareWeightsDouble(parent=self)
90
- _layout.addWidget(self.widget)
91
-
92
- ## connect signal / slot
93
- self.widget.sigConfigChanged.connect(self._updateSettings)
94
-
95
- self.task_output_changed_callbacks.add(self._notify_state)
96
-
97
- ## handle settings
98
- self._loadSettings()
99
- self.task_executor_queue.sigComputationStarted.connect(self._newTaskStarted)
100
-
101
- def _updateSettings(self):
102
- config = self.widget.getConfiguration()
103
- for key in ("transition_width", "processes_file"):
104
- self._ewoks_default_inputs[key] = config[key] # pylint: disable=E1137
105
-
106
- @property
107
- def request_input(self):
108
- return self.__request_input
109
-
110
- @request_input.setter
111
- def request_input(self, request):
112
- self.__request_input = request
113
-
114
- def get_task_inputs(self):
115
- assert self.__scan is not None
116
- return {
117
- "data": self.__scan,
118
- "transition_width": self.widget.getConfiguration()["transition_width"],
119
- "processes_file": self.widget.getConfiguration()["processes_file"],
120
- }
121
-
122
- def handleNewSignals(self) -> None:
123
- """Invoked by the workflow signal propagation manager after all
124
- signals handlers have been called.
125
- """
126
- # for now we want to avoid propagation any processing.
127
- # task will be executed only when the user validates the dialog
128
- data = super().get_task_inputs().get("data", None)
129
- if data is not None:
130
- if not isinstance(data, HDF5TomoScan):
131
- raise TypeError(
132
- f"data is expected to be an instance of HDF5TomoScan. {type(data)} are not handled"
133
- )
134
- self.add(data.path)
135
-
136
- def _loadSettings(self):
137
- self.widget.setConfiguration(self._ewoks_default_inputs)
138
-
139
- def _newTaskStarted(self):
140
- try:
141
- task_executor = self.sender()
142
- scan = task_executor.current_task.inputs.data
143
- self.notify_on_going(scan)
144
- except Exception:
145
- pass
146
-
147
- def _notify_state(self):
148
- try:
149
- task_executor = self.sender()
150
- task_suceeded = task_executor.succeeded
151
- scan = task_executor.current_task.outputs.data
152
- if task_suceeded:
153
- self.notify_succeed(scan=scan)
154
- else:
155
- self.notify_failed(scan=scan)
156
- except Exception as e:
157
- _logger.error(f"failed to handle task finished callback. Raiseon is {e}")
158
-
159
- @Inputs.data
160
- def process_data(self, scan: Optional[TomwerScanBase]):
161
- if scan is None:
162
- return
163
- else:
164
- self.__scan = scan
165
- self.notify_pending(scan=scan)
166
- self.execute_ewoks_task()
167
-
168
- def sizeHint(self) -> qt.QSize:
169
- return qt.QSize(650, 60)
1
+ # import logging
2
+
3
+ # from orangewidget import gui
4
+ # from orangewidget.settings import Setting
5
+ # from orangewidget.widget import Input, Output
6
+ # from orangecontrib.tomwer.orange.managedprocess import TomwerWithStackStack
7
+ # from silx.gui import qt
8
+ # from typing import Optional
9
+
10
+ # from tomwer.core.futureobject import FutureTomwerObject
11
+ # from tomwer.core.scan.scanbase import TomwerScanBase
12
+ # from tomwer.gui.reconstruction.nabu.helical import HelicalPrepareWeightsDouble
13
+ # from tomwer.core.process.reconstruction.nabu.helical import (
14
+ # NabuHelicalPrepareWeightsDouble,
15
+ # )
16
+ # from tomwer.core.scan.hdf5scan import HDF5TomoScan
17
+
18
+ # _logger = logging.getLogger(__name__)
19
+
20
+
21
+ # class NabuHelicalPrepareWeightsDoubleOW(
22
+ # TomwerWithStackStack,
23
+ # ewokstaskclass=NabuHelicalPrepareWeightsDouble,
24
+ # ):
25
+ # """
26
+ # widget used to call the `nabu-helical-prepare-weights-double` application on a dedicated thread. It define weights map and double flat field.
27
+
28
+ # :param parent: the parent widget
29
+ # """
30
+
31
+ # # note of this widget should be the one registered on the documentation
32
+ # name = "helical prerate weights double"
33
+ # id = "orangecontrib.tomwer.widgets.reconstruction.NabuHelicalPrepareWeightsDoubleOW.NabuHelicalPrepareWeightsDoubleOW"
34
+ # description = "compute map of weights requested for nabu helical reconstruction"
35
+ # icon = "icons/nabu_prepare_weights_double.svg"
36
+ # priority = 199
37
+ # keywords = [
38
+ # "tomography",
39
+ # "nabu",
40
+ # "reconstruction",
41
+ # "nabu-helical",
42
+ # "helical",
43
+ # "weights",
44
+ # "prepare",
45
+ # ]
46
+
47
+ # want_main_area = True
48
+ # want_control_area = False
49
+ # resizing_enabled = True
50
+
51
+ # _ewoks_default_inputs = Setting(
52
+ # {
53
+ # "data": None,
54
+ # "transition_width": 50,
55
+ # "processes_file": "",
56
+ # }
57
+ # )
58
+
59
+ # sigScanReady = qt.Signal(TomwerScanBase)
60
+ # "Signal emitted when a scan is ended"
61
+
62
+ # TIMEOUT = 30
63
+
64
+ # class Inputs:
65
+ # data = Input(
66
+ # name="data",
67
+ # type=TomwerScanBase,
68
+ # doc="one scan to be process",
69
+ # default=True,
70
+ # multiple=False,
71
+ # )
72
+
73
+ # class Outputs:
74
+ # data = Output(name="data", type=TomwerScanBase, doc="one scan to be process")
75
+ # future_tomo_obj = Output(
76
+ # name="future_tomo_obj",
77
+ # type=FutureTomwerObject,
78
+ # doc="future object (process remotely)",
79
+ # )
80
+
81
+ # LOGGER = _logger
82
+
83
+ # def __init__(self, parent=None):
84
+ # super().__init__(parent)
85
+ # self.__scan = None
86
+
87
+ # # gui definition
88
+ # _layout = gui.vBox(self.mainArea, self.name).layout()
89
+ # self.widget = HelicalPrepareWeightsDouble(parent=self)
90
+ # _layout.addWidget(self.widget)
91
+
92
+ # ## connect signal / slot
93
+ # self.widget.sigConfigChanged.connect(self._updateSettings)
94
+
95
+ # self.task_output_changed_callbacks.add(self._notify_state)
96
+
97
+ # ## handle settings
98
+ # self._loadSettings()
99
+ # self.task_executor_queue.sigComputationStarted.connect(self._newTaskStarted)
100
+
101
+ # def _updateSettings(self):
102
+ # config = self.widget.getConfiguration()
103
+ # for key in ("transition_width", "processes_file"):
104
+ # self._ewoks_default_inputs[key] = config[key] # pylint: disable=E1137
105
+
106
+ # @property
107
+ # def request_input(self):
108
+ # return self.__request_input
109
+
110
+ # @request_input.setter
111
+ # def request_input(self, request):
112
+ # self.__request_input = request
113
+
114
+ # def get_task_inputs(self):
115
+ # assert self.__scan is not None
116
+ # return {
117
+ # "data": self.__scan,
118
+ # "transition_width": self.widget.getConfiguration()["transition_width"],
119
+ # "processes_file": self.widget.getConfiguration()["processes_file"],
120
+ # }
121
+
122
+ # def handleNewSignals(self) -> None:
123
+ # """Invoked by the workflow signal propagation manager after all
124
+ # signals handlers have been called.
125
+ # """
126
+ # # for now we want to avoid propagation any processing.
127
+ # # task will be executed only when the user validates the dialog
128
+ # data = super().get_task_inputs().get("data", None)
129
+ # if data is not None:
130
+ # if not isinstance(data, HDF5TomoScan):
131
+ # raise TypeError(
132
+ # f"data is expected to be an instance of HDF5TomoScan. {type(data)} are not handled"
133
+ # )
134
+ # self.add(data.path)
135
+
136
+ # def _loadSettings(self):
137
+ # self.widget.setConfiguration(self._ewoks_default_inputs)
138
+
139
+ # def _newTaskStarted(self):
140
+ # try:
141
+ # task_executor = self.sender()
142
+ # scan = task_executor.current_task.inputs.data
143
+ # self.notify_on_going(scan)
144
+ # except Exception:
145
+ # pass
146
+
147
+ # def _notify_state(self):
148
+ # try:
149
+ # task_executor = self.sender()
150
+ # task_suceeded = task_executor.succeeded
151
+ # scan = task_executor.current_task.outputs.data
152
+ # if task_suceeded:
153
+ # self.notify_succeed(scan=scan)
154
+ # else:
155
+ # self.notify_failed(scan=scan)
156
+ # except Exception as e:
157
+ # _logger.error(f"failed to handle task finished callback. Raiseon is {e}")
158
+
159
+ # @Inputs.data
160
+ # def process_data(self, scan: Optional[TomwerScanBase]):
161
+ # if scan is None:
162
+ # return
163
+ # else:
164
+ # self.__scan = scan
165
+ # self.notify_pending(scan=scan)
166
+ # self.execute_ewoks_task()
167
+
168
+ # def sizeHint(self) -> qt.QSize:
169
+ # return qt.QSize(650, 60)
@@ -61,9 +61,7 @@ class MainWindow(_MainWindow):
61
61
  HELPDESK_URL = "https://requests.esrf.fr/plugins/servlet/desk/portal/41"
62
62
 
63
63
  def __init__(self, *args, **kwargs):
64
- print("create window")
65
64
  super().__init__(*args, **kwargs)
66
- print("end super")
67
65
  self.process_supervisor_dock = DockWidget(
68
66
  self.tr("object supervisor"),
69
67
  self,
@@ -71,7 +69,6 @@ class MainWindow(_MainWindow):
71
69
  allowedAreas=qt.Qt.BottomDockWidgetArea,
72
70
  visible=self.show_processes_manager_action.isChecked(),
73
71
  )
74
- print("continue")
75
72
 
76
73
  self.process_supervisor_dock.setWidget(ProcessManagerWindow(parent=None))
77
74
  self.process_supervisor_dock.visibilityChanged[bool].connect(
@@ -85,6 +85,7 @@ def main(argv):
85
85
  options.scan_path = options.scan_path.rstrip(os.path.sep)
86
86
 
87
87
  widget = ImageKeyDialog(parent=None)
88
+ widget.setWindowFlags(qt.Qt.Window)
88
89
  widget.setScan(scan)
89
90
  widget.setWindowTitle("Image key editor")
90
91
  widget.setWindowIcon(icons.getQIcon("tomwer"))
@@ -94,6 +94,8 @@ def main(argv):
94
94
  options.scan_path = options.scan_path.rstrip(os.path.sep)
95
95
 
96
96
  widget = ImageKeyUpgraderDialog(parent=None)
97
+ widget.setWindowFlags(qt.Qt.Window)
98
+
97
99
  widget.setScan(scan)
98
100
  widget.setWindowTitle("Image key upgrader")
99
101
  widget.setWindowIcon(icons.getQIcon("tomwer"))
@@ -83,6 +83,8 @@ def main(argv):
83
83
  return
84
84
 
85
85
  dialog = NXtomoEditorDialog()
86
+ dialog.setWindowFlags(qt.Qt.Window)
87
+
86
88
  dialog.setScan(scan)
87
89
  dialog.show()
88
90
  splash.finish(dialog)
tomwer/app/zstitching.py CHANGED
@@ -257,6 +257,7 @@ def main(argv):
257
257
  timer.timeout.connect(lambda: None)
258
258
 
259
259
  window = MainWindow()
260
+ window.setWindowFlags(qt.Qt.Window)
260
261
 
261
262
  window.show()
262
263
  from sluurp.utils import has_sbatch_available
@@ -327,6 +327,18 @@ class _NabuBaseReconstructor:
327
327
  else:
328
328
  raise ValueError(f"{self.target} is not recognized as a valid target")
329
329
 
330
+ @staticmethod
331
+ def _get_gpu_and_cpu_mem_fraction(config_to_dump: dict):
332
+ gpu_mem_fraction = config_to_dump.get("resources", {}).get(
333
+ "gpu_mem_fraction", config_to_dump.get("gpu_mem_fraction", 0.9)
334
+ )
335
+ assert gpu_mem_fraction <= 1
336
+ cpu_mem_fraction = config_to_dump.get("resources", {}).get(
337
+ "cpu_mem_fraction", config_to_dump.get("cpu_mem_fraction", 0.9)
338
+ )
339
+ assert cpu_mem_fraction <= 1
340
+ return gpu_mem_fraction, cpu_mem_fraction
341
+
330
342
  def _run_nabu_locally(
331
343
  self,
332
344
  conf_file: str,
@@ -346,8 +358,22 @@ class _NabuBaseReconstructor:
346
358
  """
347
359
  if not has_nabu:
348
360
  raise ImportError("Fail to import nabu")
361
+ assert isinstance(config_to_dump, dict)
362
+ gpu_mem_fraction, cpu_mem_fraction = self._get_gpu_and_cpu_mem_fraction(
363
+ config_to_dump
364
+ )
365
+
349
366
  command = " ".join(
350
- ("python3", "-m", settings.NABU_FULL_FIELD_APP_PATH, conf_file)
367
+ (
368
+ "python3",
369
+ "-m",
370
+ settings.NABU_FULL_FIELD_APP_PATH,
371
+ conf_file,
372
+ "--gpu_mem_fraction",
373
+ str(gpu_mem_fraction),
374
+ "--cpu_mem_fraction",
375
+ str(cpu_mem_fraction),
376
+ )
351
377
  )
352
378
  _logger.info(f'call nabu from "{command}"')
353
379
 
@@ -422,13 +448,21 @@ class _NabuBaseReconstructor:
422
448
  project_name = project_name.replace(" ", "_")
423
449
  cluster_config["job_name"] = project_name
424
450
 
451
+ # extract gpu_mem_fraction and cpu_mem_fraction
452
+ assert isinstance(config_to_dump, dict)
453
+ gpu_mem_fraction, cpu_mem_fraction = self._get_gpu_and_cpu_mem_fraction(
454
+ config_to_dump
455
+ )
456
+
425
457
  # submit job
426
458
  script_name = get_slurm_script_name(prefix="nabu")
427
459
  # for now force job name
428
460
  cluster_config["job_name"] = f"tomwer-nabu {conf_file}"
429
461
  job = SBatchScriptJob(
430
462
  slurm_config=cluster_config,
431
- script=(f"python3 -m {settings.NABU_FULL_FIELD_APP_PATH} {conf_file}",),
463
+ script=(
464
+ f"python3 -m {settings.NABU_FULL_FIELD_APP_PATH} {conf_file} --gpu_mem_fraction {gpu_mem_fraction} --cpu_mem_fraction {cpu_mem_fraction}",
465
+ ),
432
466
  script_path=os.path.join(self.scan.path, "slurm_scripts", script_name),
433
467
  clean_script=False,
434
468
  working_directory=self.scan.working_directory,
@@ -455,10 +489,12 @@ class _NabuBaseReconstructor:
455
489
  """Return a tuple a potential callback to be launch once the future is done"""
456
490
  return tuple()
457
491
 
458
- def _treateOutputSliceConfig(self, config):
492
+ def _treateOutputSliceConfig(self, config) -> tuple:
459
493
  """
460
494
  - add or overwrite some parameters of the dictionary
461
495
  - create the output directory if does not exist
496
+
497
+ :return: (config: dict, nabu_cfg_folder: str)
462
498
  """
463
499
  # handle phase
464
500
  pag = False
@@ -492,16 +528,26 @@ class _NabuBaseReconstructor:
492
528
  location = config["output"].get("location", None)
493
529
  if location not in ("", None):
494
530
  location = format_output_location(location, scan=self.scan)
495
- # if user specify the location
496
- if not os.path.isdir(config["output"]["location"]):
497
- os.makedirs(location)
531
+ location_cfg_files = location
498
532
  else:
499
533
  # otherwise default location will be the data root level
500
534
  location = self.scan.path
535
+ location_cfg_files = location
536
+ # TODO: if is a single file - append prefix
537
+ if config["output"].get("file_format") in (
538
+ NabuOutputFileFormat.EDF.value,
539
+ NabuOutputFileFormat.TIFF.value,
540
+ NabuOutputFileFormat.JP2K.value,
541
+ ): # if user specify the location
542
+ location = "/".join([location, _file_name])
543
+
501
544
  # add reconstruction path to the list. scan `reconstruction_paths` register all the existing path where
502
545
  # reconstruction are saved in order to be able to browse them all
503
546
  self.scan.add_reconstruction_path(location)
504
547
  config["output"]["location"] = location
548
+ else:
549
+ # don't think this could ever happen
550
+ location_cfg_files = self.scan.path
505
551
  # handle preproc
506
552
  if "preproc" not in config:
507
553
  config["preproc"] = {}
@@ -514,14 +560,14 @@ class _NabuBaseReconstructor:
514
560
 
515
561
  extra_infos = self.scan.intensity_normalization.get_extra_infos()
516
562
 
517
- nabu_cfg_folders = os.path.join(
518
- config["output"]["location"], settings.NABU_CFG_FILE_FOLDER
563
+ nabu_cfg_folder = os.path.join(
564
+ location_cfg_files, settings.NABU_CFG_FILE_FOLDER
519
565
  )
520
- os.makedirs(nabu_cfg_folders, exist_ok=True)
566
+ os.makedirs(nabu_cfg_folder, exist_ok=True)
521
567
 
522
568
  # configuration file and nabu_tomwer_serving_hatch must be in the same folder
523
569
  serving_hatch_file = os.path.join(
524
- nabu_cfg_folders, settings.NABU_TOMWER_SERVING_HATCH
570
+ nabu_cfg_folder, settings.NABU_TOMWER_SERVING_HATCH
525
571
  )
526
572
 
527
573
  source = extra_infos.get("source", INormSource.NONE)
@@ -576,8 +622,7 @@ class _NabuBaseReconstructor:
576
622
  )
577
623
  else:
578
624
  raise NotImplementedError(f"source type {source.value} is not handled")
579
-
580
- return config
625
+ return config, nabu_cfg_folder
581
626
 
582
627
  def _get_file_basename_reconstruction(self, pag, db, ctf):
583
628
  """return created file base name"""
@@ -223,6 +223,8 @@ class _Reconstructor(_NabuBaseReconstructor):
223
223
  if self._cancelled:
224
224
  break
225
225
  config, conf_file = self.preprocess_config(deepcopy(config), var_value)
226
+ print("conf file is", conf_file)
227
+ print("confif output is", config.get("output"))
226
228
 
227
229
  # add some tomwer metadata and save the configuration
228
230
  # note: for now the section is ignored by nabu but shouldn't stay that way
@@ -336,8 +338,7 @@ class _Reconstructor(_NabuBaseReconstructor):
336
338
  config["output"]["location"],
337
339
  nabu_settings.NABU_CFG_FILE_FOLDER,
338
340
  )
339
- if not os.path.exists(cfg_folder):
340
- os.makedirs(cfg_folder)
341
+ os.makedirs(cfg_folder, exist_ok=True)
341
342
 
342
343
  conf_file = os.path.join(
343
344
  cfg_folder, file_prefix + nabu_settings.NABU_CONFIG_FILE_EXTENSION
@@ -716,17 +716,15 @@ class SingleSliceRunner(_NabuBaseReconstructor):
716
716
  "steps_file": steps_file,
717
717
  }
718
718
 
719
- config = self._treateOutputSliceConfig(config)
719
+ config, cfg_folder = self._treateOutputSliceConfig(config)
720
720
  # the policy is to save nabu .cfg file at the same location as the
721
721
  # force overwrite results
722
722
  if self.slice_index is not None:
723
723
  config["reconstruction"]["start_z"] = self.slice_index
724
724
  config["reconstruction"]["end_z"] = self.slice_index
725
- cfg_folder = os.path.join(
726
- config["output"]["location"], nabu_settings.NABU_CFG_FILE_FOLDER
727
- )
728
- if not os.path.exists(cfg_folder):
729
- os.makedirs(cfg_folder)
725
+
726
+ if self.slice_index is not None:
727
+ os.makedirs(config["output"]["location"], exist_ok=True)
730
728
 
731
729
  name = (
732
730
  config["output"]["file_prefix"] + nabu_settings.NABU_CONFIG_FILE_EXTENSION
@@ -828,7 +826,7 @@ class SingleSliceRunner(_NabuBaseReconstructor):
828
826
  return "_".join(
829
827
  (
830
828
  basename + "slice_pag",
831
- str(slice_index).zfill(4),
829
+ str(slice_index).zfill(6),
832
830
  "db" + str(db).zfill(4),
833
831
  )
834
832
  )
@@ -836,12 +834,12 @@ class SingleSliceRunner(_NabuBaseReconstructor):
836
834
  return "_".join(
837
835
  (
838
836
  basename + "slice_ctf",
839
- str(slice_index).zfill(4),
837
+ str(slice_index).zfill(6),
840
838
  "db" + str(db).zfill(4),
841
839
  )
842
840
  )
843
841
  else:
844
- return "_".join((basename + "slice", str(slice_index).zfill(4)))
842
+ return "_".join((basename + "slice", str(slice_index).zfill(6)))
845
843
 
846
844
  @docstring(_NabuBaseReconstructor)
847
845
  def _get_file_basename_reconstruction(self, pag, db, ctf):