tomwer 1.2.1__py3-none-any.whl → 1.3.12__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 (334) hide show
  1. orangecontrib/tomwer/tutorials/icat_publication.ows +58 -0
  2. orangecontrib/tomwer/widgets/__init__.py +11 -11
  3. orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +2 -2
  4. orangecontrib/tomwer/widgets/control/DataListOW.py +9 -7
  5. orangecontrib/tomwer/widgets/control/DataListenerOW.py +6 -6
  6. orangecontrib/tomwer/widgets/control/DataSelectorOW.py +21 -10
  7. orangecontrib/tomwer/widgets/control/DataValidatorOW.py +6 -6
  8. orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +24 -7
  9. orangecontrib/tomwer/widgets/control/EmailOW.py +4 -4
  10. orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +3 -3
  11. orangecontrib/tomwer/widgets/control/NXTomomillOW.py +64 -23
  12. orangecontrib/tomwer/widgets/control/NXtomoConcatenate.py +20 -8
  13. orangecontrib/tomwer/widgets/control/NotifierOW.py +1 -0
  14. orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +6 -6
  15. orangecontrib/tomwer/widgets/control/VolumeSelector.py +7 -4
  16. orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +182 -182
  17. orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +5 -5
  18. orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +4 -4
  19. orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +3 -3
  20. orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +8 -1
  21. orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +3 -3
  22. orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +3 -3
  23. orangecontrib/tomwer/widgets/icat/PublishProcessedDataOW.py +115 -0
  24. orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +98 -0
  25. orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +129 -0
  26. orangecontrib/tomwer/widgets/icat/__init__.py +13 -0
  27. orangecontrib/tomwer/widgets/icat/icons/add_gallery.png +0 -0
  28. orangecontrib/tomwer/widgets/icat/icons/add_gallery.svg +82 -0
  29. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.png +0 -0
  30. orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.svg +95 -0
  31. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.png +0 -0
  32. orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.svg +143 -0
  33. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.png +0 -0
  34. orangecontrib/tomwer/widgets/icons/tomwer_data_portal.svg +76 -0
  35. orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +22 -20
  36. orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +19 -3
  37. orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +184 -169
  38. orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +23 -0
  39. orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +39 -5
  40. orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +18 -22
  41. orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +18 -26
  42. orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +15 -19
  43. orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +9 -9
  44. orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
  45. orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +1 -1
  46. orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +3 -3
  47. orangecontrib/tomwer/widgets/visualization/SinogramViewerOW.py +0 -1
  48. orangecontrib/tomwer/widgets/visualization/VolumeViewerOW.py +3 -29
  49. tomwer/__main__.py +7 -64
  50. tomwer/app/axis.py +3 -3
  51. tomwer/app/canvas.py +8 -0
  52. tomwer/app/canvas_launcher/config.py +16 -14
  53. tomwer/app/canvas_launcher/environ.py +1 -0
  54. tomwer/app/canvas_launcher/mainwindow.py +4 -1
  55. tomwer/app/darkref.py +1 -1
  56. tomwer/app/darkrefpatch.py +1 -1
  57. tomwer/app/diffframe.py +3 -3
  58. tomwer/app/imagekeyeditor.py +5 -5
  59. tomwer/app/imagekeyupgrader.py +5 -5
  60. tomwer/app/intensitynormalization.py +14 -13
  61. tomwer/app/{saaxis.py → multicor.py} +3 -3
  62. tomwer/app/{sadeltabeta.py → multipag.py} +3 -3
  63. tomwer/app/nabuapp.py +0 -11
  64. tomwer/app/radiostack.py +6 -4
  65. tomwer/app/samplemoved.py +3 -2
  66. tomwer/app/scanviewer.py +4 -2
  67. tomwer/app/sinogramviewer.py +3 -2
  68. tomwer/app/slicestack.py +3 -2
  69. tomwer/app/zstitching.py +88 -6
  70. tomwer/core/cluster/cluster.py +26 -0
  71. tomwer/core/log/logger.py +7 -5
  72. tomwer/core/process/conditions/filters.py +1 -1
  73. tomwer/core/process/control/datalistener/datalistener.py +19 -14
  74. tomwer/core/process/control/datawatcher/edfdwprocess.py +0 -9
  75. tomwer/core/process/control/nxtomoconcatenate.py +13 -13
  76. tomwer/core/process/control/nxtomomill.py +92 -34
  77. tomwer/core/process/control/scantransfer.py +20 -43
  78. tomwer/core/process/control/scanvalidator.py +3 -2
  79. tomwer/core/process/control/test/test_concatenate_nxtomos.py +9 -9
  80. tomwer/core/process/control/test/test_email.py +4 -4
  81. tomwer/core/process/control/test/test_h52nx_process.py +59 -7
  82. tomwer/core/process/control/test/test_volume_link.py +64 -64
  83. tomwer/core/process/control/timer.py +1 -1
  84. tomwer/core/process/control/volumesymlink.py +200 -200
  85. tomwer/core/process/edit/darkflatpatch.py +14 -15
  86. tomwer/core/process/edit/imagekeyeditor.py +41 -39
  87. tomwer/core/process/icat/__init__.py +0 -0
  88. tomwer/core/process/icat/createscreenshots.py +100 -0
  89. tomwer/core/process/icat/gallery.py +377 -0
  90. tomwer/core/process/icat/icatbase.py +36 -0
  91. tomwer/core/process/icat/publish.py +228 -0
  92. tomwer/core/process/icat/screenshots.py +27 -0
  93. tomwer/core/process/output.py +52 -0
  94. tomwer/core/process/reconstruction/axis/axis.py +280 -69
  95. tomwer/core/process/reconstruction/axis/mode.py +163 -48
  96. tomwer/core/process/reconstruction/axis/params.py +29 -21
  97. tomwer/core/process/reconstruction/darkref/darkrefs.py +41 -127
  98. tomwer/core/process/reconstruction/darkref/darkrefscopy.py +4 -3
  99. tomwer/core/process/reconstruction/darkref/params.py +1 -1
  100. tomwer/core/process/reconstruction/nabu/castvolume.py +4 -4
  101. tomwer/core/process/reconstruction/nabu/helical.py +9 -5
  102. tomwer/core/process/reconstruction/nabu/nabucommon.py +71 -78
  103. tomwer/core/process/reconstruction/nabu/nabuscores.py +425 -53
  104. tomwer/core/process/reconstruction/nabu/nabuslices.py +114 -93
  105. tomwer/core/process/reconstruction/nabu/nabuvolume.py +54 -27
  106. tomwer/core/process/reconstruction/nabu/plane.py +9 -0
  107. tomwer/core/process/reconstruction/nabu/settings.py +2 -2
  108. tomwer/core/process/reconstruction/nabu/utils.py +164 -26
  109. tomwer/core/process/reconstruction/output.py +108 -0
  110. tomwer/core/process/reconstruction/saaxis/saaxis.py +238 -264
  111. tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +151 -87
  112. tomwer/core/process/reconstruction/scores/params.py +7 -4
  113. tomwer/core/process/reconstruction/scores/scores.py +13 -0
  114. tomwer/core/process/reconstruction/test/test_axis_params.py +2 -2
  115. tomwer/core/process/reconstruction/test/test_darkref.py +3 -3
  116. tomwer/core/process/reconstruction/test/test_darkref_copy.py +7 -7
  117. tomwer/core/process/reconstruction/test/test_saaxis.py +3 -4
  118. tomwer/core/process/reconstruction/test/test_sadeltabeta.py +2 -2
  119. tomwer/core/process/stitching/nabustitcher.py +13 -12
  120. tomwer/core/process/task.py +34 -26
  121. tomwer/core/process/test/test_axis.py +13 -12
  122. tomwer/core/process/test/test_dark_and_flat.py +10 -7
  123. tomwer/core/process/test/test_data_transfer.py +10 -8
  124. tomwer/core/process/test/test_nabu.py +14 -6
  125. tomwer/core/process/test/test_normalization.py +4 -4
  126. tomwer/core/scan/blissscan.py +3 -3
  127. tomwer/core/scan/edfscan.py +13 -10
  128. tomwer/core/scan/hdf5scan.py +19 -530
  129. tomwer/core/scan/nxtomoscan.py +534 -0
  130. tomwer/core/scan/scanbase.py +72 -44
  131. tomwer/core/scan/scanfactory.py +13 -13
  132. tomwer/core/scan/test/test_edf.py +2 -2
  133. tomwer/core/scan/test/test_future_scan.py +3 -3
  134. tomwer/core/scan/test/test_h5.py +18 -16
  135. tomwer/core/scan/test/test_process_registration.py +4 -40
  136. tomwer/core/scan/test/test_scan.py +5 -78
  137. tomwer/core/settings.py +22 -2
  138. tomwer/core/test/test_scanutils.py +8 -7
  139. tomwer/core/test/test_utils.py +35 -28
  140. tomwer/core/tomwer_object.py +1 -1
  141. tomwer/core/utils/__init__.py +0 -466
  142. tomwer/core/utils/deprecation.py +1 -1
  143. tomwer/core/utils/dictutils.py +14 -0
  144. tomwer/core/utils/lbsram.py +35 -0
  145. tomwer/core/utils/nxtomoutils.py +1 -1
  146. tomwer/core/utils/scanutils.py +6 -6
  147. tomwer/core/utils/spec.py +263 -0
  148. tomwer/core/volume/edfvolume.py +6 -6
  149. tomwer/core/volume/hdf5volume.py +6 -6
  150. tomwer/core/volume/jp2kvolume.py +6 -6
  151. tomwer/core/volume/rawvolume.py +6 -6
  152. tomwer/core/volume/tiffvolume.py +12 -12
  153. tomwer/core/volume/volumefactory.py +2 -2
  154. tomwer/gui/cluster/slurm.py +274 -65
  155. tomwer/gui/cluster/supervisor.py +12 -0
  156. tomwer/gui/cluster/test/test_cluster.py +14 -2
  157. tomwer/gui/cluster/test/test_supervisor.py +3 -3
  158. tomwer/gui/configuration/__init__.py +0 -0
  159. tomwer/gui/{reconstruction/nabu → configuration}/action.py +1 -32
  160. tomwer/gui/configuration/level.py +22 -0
  161. tomwer/gui/control/actions.py +54 -0
  162. tomwer/gui/control/datalist.py +83 -16
  163. tomwer/gui/control/datalistener.py +4 -16
  164. tomwer/gui/control/datawatcher/controlwidget.py +2 -4
  165. tomwer/gui/control/datawatcher/datawatcher.py +1 -24
  166. tomwer/gui/control/{email.py → emailnotifier.py} +9 -18
  167. tomwer/gui/control/history.py +2 -2
  168. tomwer/gui/control/observations.py +2 -2
  169. tomwer/gui/control/reducedarkflatselector.py +9 -9
  170. tomwer/gui/control/selectorwidgetbase.py +36 -9
  171. tomwer/gui/control/serie/seriecreator.py +5 -22
  172. tomwer/gui/control/test/test_email.py +1 -1
  173. tomwer/gui/control/test/test_scanvalidator.py +6 -5
  174. tomwer/gui/control/test/test_single_tomo_obj.py +3 -3
  175. tomwer/gui/control/tomoobjdisplaymode.py +8 -0
  176. tomwer/gui/debugtools/datasetgenerator.py +3 -3
  177. tomwer/gui/edit/dkrfpatch.py +20 -26
  178. tomwer/gui/edit/imagekeyeditor.py +11 -12
  179. tomwer/gui/edit/nxtomoeditor.py +111 -44
  180. tomwer/gui/edit/nxtomowarmer.py +7 -6
  181. tomwer/gui/edit/test/test_dkrf_patch.py +13 -13
  182. tomwer/gui/edit/test/test_image_key_editor.py +3 -3
  183. tomwer/gui/edit/test/test_nx_editor.py +40 -16
  184. tomwer/gui/icat/__init__.py +0 -0
  185. tomwer/gui/icat/createscreenshots.py +80 -0
  186. tomwer/gui/icat/gallery.py +214 -0
  187. tomwer/gui/icat/publish.py +187 -0
  188. tomwer/gui/imagefromfile.py +2 -2
  189. tomwer/gui/qfolderdialog.py +24 -1
  190. tomwer/gui/reconstruction/axis/CompareImages.py +88 -168
  191. tomwer/gui/reconstruction/axis/axis.py +171 -57
  192. tomwer/gui/reconstruction/axis/radioaxis.py +122 -257
  193. tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +3 -2
  194. tomwer/gui/reconstruction/darkref/darkrefwidget.py +2 -1
  195. tomwer/gui/reconstruction/nabu/castvolume.py +14 -3
  196. tomwer/gui/reconstruction/nabu/check.py +9 -9
  197. tomwer/gui/reconstruction/nabu/helical.py +29 -12
  198. tomwer/gui/reconstruction/nabu/nabuconfig/base.py +2 -4
  199. tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +2 -1
  200. tomwer/gui/reconstruction/nabu/nabuconfig/output.py +126 -35
  201. tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +39 -32
  202. tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +222 -31
  203. tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +57 -27
  204. tomwer/gui/reconstruction/nabu/nabuflow.py +12 -20
  205. tomwer/gui/reconstruction/nabu/slices.py +10 -11
  206. tomwer/gui/reconstruction/nabu/volume.py +22 -10
  207. tomwer/gui/reconstruction/normalization/intensity.py +18 -48
  208. tomwer/gui/reconstruction/saaxis/corrangeselector.py +8 -24
  209. tomwer/gui/reconstruction/saaxis/dimensionwidget.py +1 -1
  210. tomwer/gui/reconstruction/saaxis/saaxis.py +9 -21
  211. tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +45 -12
  212. tomwer/gui/reconstruction/scores/control.py +2 -9
  213. tomwer/gui/reconstruction/scores/scoreplot.py +13 -11
  214. tomwer/gui/reconstruction/test/test_axis.py +41 -16
  215. tomwer/gui/reconstruction/test/test_nabu.py +31 -9
  216. tomwer/gui/reconstruction/test/test_saaxis.py +3 -3
  217. tomwer/gui/reconstruction/test/test_sadeltabeta.py +12 -2
  218. tomwer/gui/settings.py +5 -28
  219. tomwer/gui/stackplot.py +2 -5
  220. tomwer/gui/stitching/action.py +49 -0
  221. tomwer/gui/stitching/config/axisparams.py +7 -24
  222. tomwer/gui/stitching/config/output.py +10 -8
  223. tomwer/gui/stitching/config/positionoveraxis.py +22 -23
  224. tomwer/gui/stitching/normalization.py +117 -0
  225. tomwer/gui/stitching/stitchandbackground.py +4 -6
  226. tomwer/gui/stitching/stitching.py +267 -45
  227. tomwer/gui/stitching/stitching_preview.py +62 -55
  228. tomwer/gui/stitching/stitching_raw.py +13 -12
  229. tomwer/gui/stitching/z_stitching/fineestimation.py +0 -60
  230. tomwer/gui/utils/buttons.py +112 -29
  231. tomwer/gui/utils/inputwidget.py +43 -25
  232. tomwer/gui/utils/lineselector/lineselector.py +1 -1
  233. tomwer/gui/utils/scandescription.py +4 -0
  234. tomwer/gui/utils/step.py +144 -0
  235. tomwer/gui/utils/unitsystem.py +2 -5
  236. tomwer/gui/utils/vignettes.py +176 -15
  237. tomwer/gui/visualization/dataviewer.py +48 -35
  238. tomwer/gui/visualization/diffviewer/diffviewer.py +7 -16
  239. tomwer/gui/visualization/diffviewer/shiftwidget.py +2 -5
  240. tomwer/gui/visualization/scanoverview.py +1 -1
  241. tomwer/gui/visualization/sinogramviewer.py +20 -36
  242. tomwer/gui/visualization/test/test_diffviewer.py +3 -3
  243. tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +4 -4
  244. tomwer/gui/visualization/test/test_sinogramviewer.py +2 -2
  245. tomwer/gui/visualization/test/test_stacks.py +3 -3
  246. tomwer/gui/visualization/test/test_volumeviewer.py +65 -67
  247. tomwer/gui/visualization/volumeviewer.py +114 -113
  248. tomwer/io/utils/h5pyutils.py +3 -3
  249. tomwer/io/utils/raw_and_processed_data.py +84 -0
  250. tomwer/io/utils/tomoobj.py +4 -6
  251. tomwer/io/utils/utils.py +7 -7
  252. tomwer/resources/gui/icons/parameters.svg +1 -1
  253. tomwer/resources/gui/icons/ruler.png +0 -0
  254. tomwer/resources/gui/icons/ruler.svg +273 -0
  255. tomwer/resources/gui/icons/short_description.png +0 -0
  256. tomwer/resources/gui/icons/short_description.svg +58 -0
  257. tomwer/resources/gui/icons/url.png +0 -0
  258. tomwer/resources/gui/icons/url.svg +58 -0
  259. tomwer/resources/gui/illustrations/no_rot.svg +1 -1
  260. tomwer/synctools/stacks/edit/darkflatpatch.py +19 -14
  261. tomwer/synctools/stacks/edit/imagekeyeditor.py +2 -2
  262. tomwer/synctools/stacks/reconstruction/axis.py +4 -4
  263. tomwer/synctools/stacks/reconstruction/castvolume.py +22 -7
  264. tomwer/synctools/stacks/reconstruction/dkrefcopy.py +25 -20
  265. tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
  266. tomwer/synctools/stacks/reconstruction/normalization.py +2 -2
  267. tomwer/synctools/stacks/reconstruction/saaxis.py +2 -2
  268. tomwer/synctools/stacks/reconstruction/sadeltabeta.py +2 -2
  269. tomwer/synctools/test/test_darkRefs.py +7 -58
  270. tomwer/synctools/test/test_foldertransfer.py +6 -6
  271. tomwer/synctools/utils/scanstages.py +6 -6
  272. tomwer/tests/conftest.py +34 -0
  273. tomwer/tests/datasets.py +13 -0
  274. tomwer/tests/test_scripts.py +91 -41
  275. tomwer/tests/utils.py +5 -0
  276. tomwer/third_part/WaitingOverlay.py +110 -0
  277. tomwer/third_part/__init__.py +0 -0
  278. tomwer/version.py +2 -2
  279. tomwer-1.3.12-py3.11-nspkg.pth +1 -0
  280. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/METADATA +73 -58
  281. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/RECORD +287 -286
  282. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/WHEEL +1 -1
  283. orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +0 -197
  284. orangecontrib/tomwer/widgets/reconstruction/icons/XY_lamino.svg +0 -168
  285. orangecontrib/tomwer/widgets/reconstruction/icons/XZ_lamino.svg +0 -275
  286. orangecontrib/tomwer/widgets/reconstruction/icons/YZ_lamino.svg +0 -182
  287. tomwer/app/lamino.py +0 -143
  288. tomwer/core/process/reconstruction/lamino/__init__.py +0 -1
  289. tomwer/core/process/reconstruction/lamino/tofu.py +0 -1000
  290. tomwer/core/process/test/test_lamino.py +0 -76
  291. tomwer/core/test/test_lamino.py +0 -92
  292. tomwer/gui/reconstruction/lamino/__init__.py +0 -31
  293. tomwer/gui/reconstruction/lamino/tofu/TofuOptionLoader.py +0 -107
  294. tomwer/gui/reconstruction/lamino/tofu/__init__.py +0 -1
  295. tomwer/gui/reconstruction/lamino/tofu/misc.py +0 -148
  296. tomwer/gui/reconstruction/lamino/tofu/projections.py +0 -896
  297. tomwer/gui/reconstruction/lamino/tofu/settings.py +0 -75
  298. tomwer/gui/reconstruction/lamino/tofu/tofu.py +0 -432
  299. tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +0 -567
  300. tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +0 -760
  301. tomwer/gui/reconstruction/test/test_lamino.py +0 -189
  302. tomwer/resources/gui/icons/esrf_1.svg +0 -307
  303. tomwer/resources/gui/icons/lamino_parameters.svg +0 -70
  304. tomwer/resources/gui/icons/triangle.svg +0 -80
  305. tomwer/resources/gui/illustrations/lamino_angle.png +0 -0
  306. tomwer/resources/gui/illustrations/lamino_angle.svg +0 -509
  307. tomwer/resources/gui/illustrations/lamino_beta_angle.png +0 -0
  308. tomwer/resources/gui/illustrations/lamino_beta_angle.svg +0 -97
  309. tomwer/resources/gui/illustrations/lamino_theta_angle.png +0 -0
  310. tomwer/resources/gui/illustrations/lamino_theta_angle.svg +0 -368
  311. tomwer/resources/gui/illustrations/manual_slice.png +0 -0
  312. tomwer/resources/gui/illustrations/manual_slice.svg +0 -221
  313. tomwer/resources/gui/illustrations/psi_angle.png +0 -0
  314. tomwer/resources/gui/illustrations/psi_angle.svg +0 -479
  315. tomwer/resources/gui/illustrations/rotation_center.png +0 -0
  316. tomwer/resources/gui/illustrations/rotation_center.svg +0 -276
  317. tomwer/resources/gui/illustrations/slice_stack.png +0 -0
  318. tomwer/resources/gui/illustrations/slice_stack.svg +0 -266
  319. tomwer/resources/gui/illustrations/xy_slice.png +0 -0
  320. tomwer/resources/gui/illustrations/xy_slice.svg +0 -269
  321. tomwer/resources/gui/illustrations/xz_slice.png +0 -0
  322. tomwer/resources/gui/illustrations/xz_slice.svg +0 -270
  323. tomwer/resources/gui/illustrations/yz_slice.png +0 -0
  324. tomwer/resources/gui/illustrations/yz_slice.svg +0 -270
  325. tomwer/synctools/stacks/reconstruction/lamino.py +0 -233
  326. tomwer/synctools/test/test_scanstages.py +0 -162
  327. tomwer/tests/utils/__init__.py +0 -247
  328. tomwer/tests/utils/utilstest.py +0 -220
  329. tomwer-1.2.1-py3.11-nspkg.pth +0 -1
  330. /tomwer/core/process/control/{email.py → emailnotifier.py} +0 -0
  331. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/LICENSE +0 -0
  332. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/entry_points.txt +0 -0
  333. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/namespace_packages.txt +0 -0
  334. {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/top_level.txt +0 -0
@@ -28,16 +28,16 @@ __license__ = "MIT"
28
28
  __date__ = "26/10/2020"
29
29
 
30
30
 
31
+ import copy
31
32
  import logging
32
33
 
33
34
  import nxtomomill.version
34
35
  from nxtomomill.utils import change_image_key_control as _change_image_key_control
35
- from tomoscan.esrf.scan.hdf5scan import ImageKey
36
- from tomoscan.esrf.scan.hdf5scan import ImageKey as _ImageKey
37
- from silx.utils.deprecation import deprecated_warning, deprecated
36
+ from nxtomo.nxobject.nxdetector import ImageKey
37
+ from tomwer.core.utils.deprecation import deprecated_warning, deprecated
38
38
 
39
39
  from tomwer.core.process.task import Task
40
- from tomwer.core.scan.hdf5scan import HDF5TomoScan
40
+ from tomwer.core.scan.nxtomoscan import NXtomoScan
41
41
  from tomwer.core.scan.scanbase import TomwerScanBase
42
42
  from tomwer.core.utils.scanutils import data_identifier_to_scan
43
43
 
@@ -45,14 +45,14 @@ _logger = logging.getLogger(__name__)
45
45
 
46
46
 
47
47
  IMAGE_KEYS = {
48
- "projection": _ImageKey.PROJECTION,
49
- "invalid": _ImageKey.INVALID,
50
- "dark": _ImageKey.DARK_FIELD,
51
- "flat": _ImageKey.FLAT_FIELD,
48
+ "projection": ImageKey.PROJECTION,
49
+ "invalid": ImageKey.INVALID,
50
+ "dark": ImageKey.DARK_FIELD,
51
+ "flat": ImageKey.FLAT_FIELD,
52
52
  }
53
53
 
54
54
 
55
- def change_image_key_control(scan: HDF5TomoScan, config: dict) -> TomwerScanBase:
55
+ def change_image_key_control(scan: NXtomoScan, config: dict) -> TomwerScanBase:
56
56
  """
57
57
 
58
58
  :param scan:
@@ -63,9 +63,9 @@ def change_image_key_control(scan: HDF5TomoScan, config: dict) -> TomwerScanBase
63
63
  """
64
64
  if scan is None:
65
65
  return
66
- elif not isinstance(scan, HDF5TomoScan):
66
+ elif not isinstance(scan, NXtomoScan):
67
67
  raise ValueError(
68
- f"Image key control only handle HDF5TomoScan and not {type(scan)}"
68
+ f"Image key control only handle NXtomoScan and not {type(scan)}"
69
69
  )
70
70
 
71
71
  if "modifications" not in config:
@@ -84,7 +84,7 @@ def change_image_key_control(scan: HDF5TomoScan, config: dict) -> TomwerScanBase
84
84
  filter(lambda item: item[1] is image_key_type, modifications.items())
85
85
  )
86
86
  frame_indexes = tuple(frame_indexes_dict.keys())
87
- _logger.warning(f"will modify {frame_indexes} to {image_key_type}")
87
+ _logger.info(f"will modify {frame_indexes} to {image_key_type}")
88
88
  _change_image_key_control(
89
89
  file_path=scan.master_file,
90
90
  entry=scan.entry,
@@ -125,28 +125,31 @@ class ImageKeyEditorTask(
125
125
  raise TypeError(
126
126
  f"scan is expected to be a dict or an instance of TomwerScanBase. Not {type(scan)}"
127
127
  )
128
- if not isinstance(scan, HDF5TomoScan):
128
+ if not isinstance(scan, NXtomoScan):
129
129
  raise ValueError(f"input type of {scan}: {(type(scan))} is not managed")
130
130
 
131
131
  config = self.inputs.configuration
132
132
  if not isinstance(config, dict):
133
133
  raise TypeError
134
+ # apply configuration
134
135
  change_image_key_control(scan=scan, config=config)
135
136
  modif_keys = list(config.get("modifications", {}).keys())
137
+ # dump modifications to the tomwer processes file
138
+ # note: we need to cast image keys to str to save it
139
+ config = copy.deepcopy(config)
136
140
  new_modif = {}
137
141
  for key in modif_keys:
138
142
  value = config["modifications"][key]
139
143
  config["modifications"][str(key)] = value
140
144
  config["modifications"] = new_modif
141
- with scan.acquire_process_file_lock():
142
- self.register_process(
143
- process_file=scan.process_file,
144
- entry=scan.entry,
145
- configuration=config,
146
- results={},
147
- process_index=scan.pop_process_index(),
148
- overwrite=True,
149
- )
145
+ self.register_process(
146
+ process_file=scan.process_file,
147
+ entry=scan.entry,
148
+ configuration=config,
149
+ results={},
150
+ process_index=scan.pop_process_index(),
151
+ overwrite=True,
152
+ )
150
153
  if self.get_input_value("serialize_output_data", True):
151
154
  self.outputs.data = scan.to_dict()
152
155
  else:
@@ -178,8 +181,8 @@ class ImageKeyUpgraderTask(
178
181
 
179
182
  def run(self):
180
183
  scan = data_identifier_to_scan(self.inputs.data)
181
- if not isinstance(scan, HDF5TomoScan):
182
- raise TypeError(f"scan is expected to be an instance of {HDF5TomoScan}")
184
+ if not isinstance(scan, NXtomoScan):
185
+ raise TypeError(f"scan is expected to be an instance of {NXtomoScan}")
183
186
  operations = self.inputs.operations
184
187
  if not isinstance(operations, dict):
185
188
  raise TypeError("operations is expected to be an dict")
@@ -199,18 +202,17 @@ class ImageKeyUpgraderTask(
199
202
 
200
203
  # apply modification using tomoscan
201
204
  change_image_key_control(scan=scan, config=configuration)
202
- with scan.acquire_process_file_lock():
203
- self.register_process(
204
- process_file=scan.process_file,
205
- entry=scan.entry,
206
- configuration={
207
- _ImageKey.from_value(key).value: _ImageKey.from_value(value).value
208
- for key, value in operations.items()
209
- },
210
- results={},
211
- process_index=scan.pop_process_index(),
212
- overwrite=True,
213
- )
205
+ self.register_process(
206
+ process_file=scan.process_file,
207
+ entry=scan.entry,
208
+ configuration={
209
+ ImageKey.from_value(key).value: ImageKey.from_value(value).value
210
+ for key, value in operations.items()
211
+ },
212
+ results={},
213
+ process_index=scan.pop_process_index(),
214
+ overwrite=True,
215
+ )
214
216
  if self.get_input_value("serialize_output_data", True):
215
217
  self.outputs.data = scan.to_dict()
216
218
  else:
@@ -218,13 +220,13 @@ class ImageKeyUpgraderTask(
218
220
 
219
221
  @staticmethod
220
222
  def from_operation_to_config(
221
- scan: TomwerScanBase, from_image_key: _ImageKey, to_image_key: _ImageKey
223
+ scan: TomwerScanBase, from_image_key: ImageKey, to_image_key: ImageKey
222
224
  ):
223
225
  """
224
226
  retrieve frame indices to be updated
225
227
  """
226
- from_image_key = _ImageKey.from_value(from_image_key)
227
- to_image_key = _ImageKey.from_value(to_image_key)
228
+ from_image_key = ImageKey.from_value(from_image_key)
229
+ to_image_key = ImageKey.from_value(to_image_key)
228
230
 
229
231
  config = {}
230
232
  for i_frame, frame in enumerate(scan.frames):
File without changes
@@ -0,0 +1,100 @@
1
+ import numpy
2
+ from tomwer.core.process.task import Task
3
+ from tomwer.core.scan.scanbase import TomwerScanBase
4
+ from tomwer.core.utils.dictutils import concatenate_dict
5
+ from tomwer.core.process.icat.screenshots import IcatScreenshots
6
+ from tomwer.core.process.icat.gallery import deduce_dataset_gallery_location
7
+
8
+ from processview.core.manager import DatasetState, ProcessManager
9
+
10
+
11
+ def get_closest_projection(angle, angles_list):
12
+ idx_closest = numpy.argmin(numpy.abs(angles_list - angle))
13
+ return angles_list[idx_closest]
14
+
15
+
16
+ def select_angles(angles_list: tuple, each_angle: int) -> tuple:
17
+ angles_list = sorted(angles_list)
18
+ if len(angles_list) > 0:
19
+ start_angle = angles_list[0]
20
+ stop_angle = angles_list[-1]
21
+ picked_angle = numpy.arange(start_angle, stop_angle + 1, step=each_angle)
22
+ return tuple(
23
+ [get_closest_projection(angle, angles_list) for angle in picked_angle]
24
+ )
25
+ else:
26
+ return tuple()
27
+
28
+
29
+ class CreateRawDataScreenshotsTask(
30
+ Task,
31
+ input_names=("data",), # screenshots as instance of :class:Screenshots
32
+ optional_input_names=(
33
+ "__process__",
34
+ "raw_projections_required",
35
+ "raw_projections_each",
36
+ "raw_darks_required",
37
+ "raw_flats_required",
38
+ ),
39
+ output_names=("screenshots",),
40
+ ):
41
+ """
42
+ simple task to create screenshots from raw data
43
+ One raw projection will be picked each 'raw_projections_each' angle (expected in degree)
44
+ If 'with_flat' then will pick the first flat of each serie
45
+ If 'with_dark' then will pick the first dark of each serie
46
+ """
47
+
48
+ def run(self):
49
+ process = self.get_input_value("__process__", None)
50
+ scan = self.inputs.data
51
+ if not isinstance(scan, TomwerScanBase):
52
+ raise TypeError(
53
+ f"scan is expected to be an instance of {TomwerScanBase}. Get {type(scan)} instead"
54
+ )
55
+ if process is not None:
56
+ ProcessManager().notify_dataset_state(
57
+ dataset=scan,
58
+ process=process(),
59
+ state=DatasetState.ON_GOING,
60
+ )
61
+
62
+ raw_projections_required = self.get_input_value(
63
+ "raw_projections_required", True
64
+ )
65
+ proj_each = self.get_input_value("raw_projections_each", 90)
66
+ raw_flats_required = self.get_input_value("raw_flats_required", True)
67
+ raw_darks_required = self.get_input_value("raw_darks_required", True)
68
+
69
+ screenshots_urls = {}
70
+ # dict with screenshot name as key and DataUrl as value
71
+ if raw_darks_required and len(scan.darks) > 0:
72
+ screenshots_urls["dark"] = next(iter(scan.darks.values()))
73
+ if raw_flats_required and len(scan.flats) > 0:
74
+ screenshots_urls["flat"] = next(iter(scan.flats.values()))
75
+ if raw_projections_required:
76
+ projections = scan.projections_with_angle()
77
+ picked_angles = select_angles(
78
+ angles_list=sorted(projections.keys()),
79
+ each_angle=proj_each,
80
+ )
81
+ screenshots_urls = concatenate_dict(
82
+ screenshots_urls,
83
+ {
84
+ f"projection_{angle:.1f}": projections[angle]
85
+ for angle in picked_angles
86
+ },
87
+ )
88
+
89
+ self.outputs.screenshots = IcatScreenshots(
90
+ data_dir=deduce_dataset_gallery_location(scan),
91
+ screenshots=screenshots_urls,
92
+ scan=scan,
93
+ )
94
+
95
+ if process is not None:
96
+ ProcessManager().notify_dataset_state(
97
+ dataset=scan,
98
+ process=process(),
99
+ state=DatasetState.SUCCEED,
100
+ )
@@ -0,0 +1,377 @@
1
+ import logging
2
+ import os
3
+ from math import floor
4
+
5
+ import numpy
6
+ from nxtomomill.converter.hdf5.utils import PROCESSED_DATA_DIR_NAME, RAW_DATA_DIR_NAME
7
+ from PIL import Image
8
+ from silx.io.url import DataUrl
9
+ from silx.utils.enum import Enum as _Enum
10
+ from tomoscan.esrf.scan.utils import get_data
11
+ from tomoscan.esrf.volume.hdf5volume import HDF5Volume
12
+ from tomoscan.esrf.volume.singleframebase import VolumeSingleFrameBase
13
+
14
+ from tomwer.io.utils.raw_and_processed_data import to_processed_data_path
15
+ from tomwer.core.scan.edfscan import EDFTomoScan
16
+ from tomwer.core.process.task import Task
17
+ from tomwer.core.scan.scanbase import TomwerScanBase
18
+ from tomwer.core.volume.volumebase import TomwerVolumeBase
19
+ from tomwer.core.process.icat.screenshots import IcatScreenshots
20
+ from tomwer.core.process.icat.publish import (
21
+ PublishProcessedDataFolderTask,
22
+ from_bliss_original_file_to_raw,
23
+ )
24
+ from processview.core.manager import DatasetState, ProcessManager
25
+
26
+ _logger = logging.getLogger(__name__)
27
+
28
+ PROPOSAL_GALLERY_DIR_NAME = "GALLERY"
29
+ DATASET_GALLERY_DIR_NAME = "gallery"
30
+
31
+
32
+ class OutputFormat(_Enum):
33
+ """possible output format to save screenshots"""
34
+
35
+ PNG = "png"
36
+ JPEG = "jpg"
37
+
38
+
39
+ class Binning(_Enum):
40
+ ONE_BY_ONE = "1x1"
41
+ TWO_BY_TWO = "2x2"
42
+ FOUR_BY_FOUR = "4x4"
43
+ HEIGHT_BY_HEIGHT = "8x8"
44
+ SIXTEEN_BY_SIXTEEN = "16x16"
45
+ THIRTY_TWO_BY_THIRTY_TWO = "32x32"
46
+ SIXTY_FOUR_BY_SIXTY_FOUR = "64x64"
47
+ ONE_HUNDRED_TWENTY_HEIGHT_BY_ONE_HUNDRED_TWENTY_HEIGHT = "128x128"
48
+
49
+ @staticmethod
50
+ def _bin_data(data, binning):
51
+ if not isinstance(data, numpy.ndarray):
52
+ raise TypeError("data should be a numpy array")
53
+ if not data.ndim == 2:
54
+ raise ValueError("data is expected to be 2d")
55
+ binning = Binning.from_value(binning)
56
+ if binning is Binning.ONE_BY_ONE:
57
+ return data
58
+ elif binning is Binning.TWO_BY_TWO:
59
+ return data[::2, ::2]
60
+ elif binning is Binning.FOUR_BY_FOUR:
61
+ return data[::4, ::4]
62
+ elif binning is Binning.HEIGHT_BY_HEIGHT:
63
+ return data[::8, ::8]
64
+ elif binning is Binning.SIXTEEN_BY_SIXTEEN:
65
+ return data[::16, ::16]
66
+ elif binning is Binning.THIRTY_TWO_BY_THIRTY_TWO:
67
+ return data[::32, ::32]
68
+ elif binning is Binning.SIXTY_FOUR_BY_SIXTY_FOUR:
69
+ return data[::64, ::64]
70
+ else:
71
+ raise NotImplementedError
72
+
73
+
74
+ def deduce_dataset_gallery_location(scan_obj: TomwerScanBase) -> str:
75
+ """
76
+ From scan path deduce the 'dataset' path to the gallery.
77
+ Warning: dataset gallery is different then the 'proposal' gallery
78
+ """
79
+ if not isinstance(scan_obj, TomwerScanBase):
80
+ raise TypeError(f"'scan_obj' is expected to be an instance of {TomwerScanBase}")
81
+
82
+ file_path = os.path.abspath(scan_obj.path)
83
+
84
+ split_path = file_path.split(os.sep)
85
+ # reverse it to find the lower level value of 'PROCESSED_DATA_DIR_NAME' or 'RAW_DATA_DIR_NAME' if by any 'chance' has several in the path
86
+ # then we will replace the 'lower one' in the string. This is where the GALLERY will be added
87
+ split_path = split_path[::-1]
88
+ # check if already contain in a "PROCESSED_DATA" directory
89
+ try:
90
+ index_processed_data = split_path.index(PROCESSED_DATA_DIR_NAME)
91
+ except ValueError:
92
+ pass
93
+ index_processed_data = None
94
+ try:
95
+ index_raw_data = split_path.index(RAW_DATA_DIR_NAME)
96
+ except ValueError:
97
+ # if the value is not in the list
98
+ index_raw_data = None
99
+
100
+ if index_processed_data is None and index_raw_data is None:
101
+ # if not in any "PROCESSED_DATA" or "RAW_DATA" directory
102
+ return scan_obj.get_relative_file(
103
+ file_name=DATASET_GALLERY_DIR_NAME, with_dataset_prefix=False
104
+ )
105
+ elif index_processed_data is not None and index_raw_data is not None:
106
+ if index_raw_data > index_processed_data:
107
+ # if PROCESSED_DATA lower in the path than RAW_DATA
108
+ split_path[index_processed_data] = RAW_DATA_DIR_NAME
109
+ # reorder path to original
110
+ split_path = list(split_path[::-1])
111
+ split_path.append(DATASET_GALLERY_DIR_NAME)
112
+ # move it to PPROCESSED_DATA when possible
113
+ path = os.sep.join(split_path)
114
+ path = to_processed_data_path(path)
115
+ return path
116
+
117
+
118
+ def deduce_proposal_GALLERY_location(scan_obj: TomwerScanBase) -> str:
119
+ """
120
+ Policy: look if the scan_obj.path is in 'PROCESSED_DATA_DIR_NAME' or 'RAW_DATA_DIR_NAME' directories.
121
+ If find any (before any 'GALLERY_DIR_NAME' directory) replace it "GALLERY_DIR_NAME".
122
+ If none of those are found then create it at the same level as the scan
123
+
124
+ :param TomwerScanBase scan_obj: scan_obj for which we want the GALLERY directory
125
+ :return: gallery path (to save screeshots for example)
126
+ """
127
+ if not isinstance(scan_obj, TomwerScanBase):
128
+ raise TypeError(f"'scan_obj' is expected to be an instance of {TomwerScanBase}")
129
+
130
+ file_path = os.path.abspath(scan_obj.path)
131
+
132
+ split_path = file_path.split(os.sep)
133
+ # reverse it to find the lower level value of 'PROCESSED_DATA_DIR_NAME' or 'RAW_DATA_DIR_NAME' if by any 'chance' has several in the path
134
+ # then we will replace the 'lower one' in the string. This is where the GALLERY will be added
135
+ split_path = split_path[::-1]
136
+ # check if already contain in a "PROCESSED_DATA" directory
137
+ try:
138
+ index_processed_data = split_path.index(PROCESSED_DATA_DIR_NAME)
139
+ except ValueError:
140
+ pass
141
+ index_processed_data = None
142
+ try:
143
+ index_raw_data = split_path.index(RAW_DATA_DIR_NAME)
144
+ except ValueError:
145
+ # if the value is not in the list
146
+ index_raw_data = None
147
+
148
+ if index_processed_data is None and index_raw_data is None:
149
+ # if not in any "PROCESSED_DATA" or "RAW_DATA" directory
150
+ return scan_obj.get_relative_file(
151
+ file_name=PROPOSAL_GALLERY_DIR_NAME, with_dataset_prefix=False
152
+ )
153
+ elif index_processed_data is not None and index_raw_data is not None:
154
+ if index_raw_data > index_processed_data:
155
+ # if PROCESSED_DATA lower in the path than RAW_DATA
156
+ split_path[index_processed_data] = PROPOSAL_GALLERY_DIR_NAME
157
+ else:
158
+ # if RAW_DATA lower in the path than PROCESSED_DATA
159
+ split_path[index_raw_data] = PROPOSAL_GALLERY_DIR_NAME
160
+ elif index_raw_data is not None:
161
+ # if the path contains only PROCESSED_DATA or RAW_DATA (expected behavior for online acquistion)
162
+ split_path[index_raw_data] = PROPOSAL_GALLERY_DIR_NAME
163
+ else:
164
+ assert index_processed_data is not None, "index_processed_data is None"
165
+ split_path[index_processed_data] = PROPOSAL_GALLERY_DIR_NAME
166
+
167
+ # reorder path to original
168
+ split_path = split_path[::-1]
169
+ return os.sep.join(split_path)
170
+
171
+
172
+ def select_screenshot_from_volume(volume: TomwerVolumeBase) -> dict:
173
+ """
174
+ return a subset of url for a volume.
175
+ Warning: this function will be called each time a nabu slice or a nabu volume is executer from orangecontrig.
176
+ So it must stay 'low processing' function to avoid slowing down everything
177
+ """
178
+ if not isinstance(volume, TomwerVolumeBase):
179
+ raise TypeError(
180
+ f"volume is expected to be an instance of {TomwerVolumeBase}. Get {type(volume)}"
181
+ )
182
+
183
+ if isinstance(volume, VolumeSingleFrameBase):
184
+ volume_urls = tuple(volume.browse_data_urls())
185
+ # lets take the three equally spaced slices (3/6, 4/6 and 5/6)
186
+ n_slices = len(volume_urls)
187
+ # note: using a dict ensure to get a set of DataUrl in case an Url is used several time
188
+ # workaround: DataUrl is not hashable
189
+ screenshots = {
190
+ f"{os.path.splitext(os.path.basename(volume_urls[floor(i * n_slices / 6)].file_path()))}": volume_urls[
191
+ floor(i * n_slices / 6)
192
+ ]
193
+ for i in range(2, 5)
194
+ }
195
+ return screenshots
196
+ elif isinstance(volume, HDF5Volume):
197
+ volume_url = next(volume.browse_data_urls())
198
+ n_slices = volume.get_volume_shape()[0]
199
+ # note: using a dict ensure to get a set of DataUrl in case an Url is used several time
200
+ # workaround: DataUrl is not hashable
201
+ screenshots = {
202
+ f"{os.path.splitext(os.path.basename(volume_url.file_path()))[0]}_{floor(i / 6 * n_slices)}": DataUrl(
203
+ file_path=volume_url.file_path(),
204
+ data_path=volume_url.data_path(),
205
+ scheme="silx",
206
+ data_slice=floor(i / 6 * n_slices),
207
+ )
208
+ for i in range(2, 5)
209
+ }
210
+ return screenshots
211
+ else:
212
+ _logger.warning(
213
+ f"volume {type(volume)} does not allow to create screenshot for now"
214
+ )
215
+ return ()
216
+
217
+
218
+ class SaveScreenshotsToGalleryTask(
219
+ Task,
220
+ input_names=("screenshots",), # screenshots as instance of :class:Screenshots
221
+ optional_input_names=("format", "overwrite", "binning"),
222
+ ):
223
+ """simple task to do the binding between orange design and 'SaveScreenshotsTask'"""
224
+
225
+ def run(self):
226
+ if not isinstance(self.inputs.screenshots, IcatScreenshots):
227
+ raise TypeError(
228
+ f"'screenshots' is expected to be an instance of {IcatScreenshots}. get {type(self.inputs.screenshots)}"
229
+ )
230
+
231
+ inputs = self.inputs.screenshots.to_dict()
232
+ if self.get_input_value("overwrite", None) is not None:
233
+ inputs["overwrite"] = self.inputs.overwrite
234
+ if self.get_input_value("format", None) is not None:
235
+ inputs["format"] = self.inputs.format
236
+ if self.get_input_value("binning", None) is not None:
237
+ inputs["binning"] = self.inputs.binning
238
+ task = SaveScreenshotsTask(
239
+ inputs=inputs,
240
+ )
241
+ task.run()
242
+
243
+
244
+ class SaveScreenshotsTask(
245
+ Task,
246
+ input_names=("screenshots_as_dict", "output_dir"),
247
+ optional_input_names=("format", "overwrite", "binning"),
248
+ ):
249
+ """Task which save a set of screenshot to the output_dir under required format"""
250
+
251
+ def run(self):
252
+ format = OutputFormat.from_value(
253
+ self.get_input_value("format", OutputFormat.PNG)
254
+ )
255
+ overwrite = self.get_input_value("overwrite", False)
256
+ binning = self.get_input_value("binning", Binning.ONE_BY_ONE)
257
+
258
+ os.makedirs(self.inputs.output_dir, exist_ok=True)
259
+ for screenshot_name, data_url in self.inputs.screenshots_as_dict.items():
260
+ if not isinstance(data_url, DataUrl):
261
+ raise TypeError("screenshot values are expected to DataUrl")
262
+ data = get_data(data_url)
263
+ if not isinstance(data, numpy.ndarray) and data.ndim != 2:
264
+ raise TypeError("screenshot are expected to be 2D numpy arrays")
265
+ if not isinstance(screenshot_name, str):
266
+ raise TypeError(
267
+ f"screenshot keys are expected to be str. Get {type(screenshot_name)}"
268
+ )
269
+
270
+ if data.ndim == 3 and data.shape[0] == 1:
271
+ data = data.reshape(data.shape[1:])
272
+ elif data.ndim != 2:
273
+ raise ValueError(
274
+ f"only 2D grayscale image are handled. Get {data.shape}"
275
+ )
276
+ # if qt is available use it as this is simpler and more powerful
277
+ # clamp data in 0-256
278
+ data = data.astype(numpy.float32)
279
+ data = Binning._bin_data(data=data, binning=binning)
280
+ data *= 255.0 / data.max()
281
+
282
+ # do a rescale else use qt instead
283
+ img = Image.fromarray(data, mode=None)
284
+ img = img.convert("L")
285
+ output_file = os.path.join(
286
+ self.inputs.output_dir,
287
+ ".".join([screenshot_name, format.value]),
288
+ )
289
+ if not overwrite and os.path.exists(output_file):
290
+ raise OSError(f"File already exists ({output_file})")
291
+ img.save(output_file)
292
+
293
+
294
+ class SaveToGalleryAndPublishTask(
295
+ Task,
296
+ input_names=(
297
+ "screenshots",
298
+ "beamline",
299
+ "dataset",
300
+ "proposal",
301
+ "format",
302
+ ),
303
+ optional_input_names=(
304
+ "dry_run",
305
+ "__process__",
306
+ ),
307
+ ):
308
+ def __init__(
309
+ self, varinfo=None, inputs=None, node_id=None, node_attrs=None, execinfo=None
310
+ ):
311
+ super().__init__(varinfo, inputs, node_id, node_attrs, execinfo)
312
+ self._process = self.get_input_value("__process__", None)
313
+ self._task_save = SaveScreenshotsToGalleryTask(
314
+ inputs=inputs,
315
+ )
316
+
317
+ scan = self.inputs.screenshots.scan
318
+ if isinstance(scan, EDFTomoScan):
319
+ raise TypeError(
320
+ "scan is an EDFTomoScan. Not handled for publishing processed data to icat"
321
+ )
322
+ raw = from_bliss_original_file_to_raw(scan.get_bliss_orginal_files())
323
+
324
+ path = self.inputs.screenshots.data_dir
325
+ # path = os.path.dirname(self.inputs.screenshots.data_dir)
326
+
327
+ self._task_publish = PublishProcessedDataFolderTask(
328
+ inputs={
329
+ "beamline": self.inputs.beamline,
330
+ "dataset": self.inputs.dataset,
331
+ "proposal": self.inputs.proposal,
332
+ "path": path, # must be the dataset path
333
+ "raw": raw,
334
+ "dry_run": self.get_input_value("dry_run", False),
335
+ "metadata": self.inputs.screenshots.metadata,
336
+ },
337
+ )
338
+
339
+ def run(self):
340
+ if self._process is not None:
341
+ ProcessManager().notify_dataset_state(
342
+ dataset=self.inputs.screenshots.scan,
343
+ process=self._process(),
344
+ state=DatasetState.ON_GOING,
345
+ )
346
+ # save screenshots to gallery
347
+ try:
348
+ self._task_save.run()
349
+ except Exception as e:
350
+ if self._process is not None:
351
+ ProcessManager().notify_dataset_state(
352
+ dataset=self.inputs.screenshots.scan,
353
+ process=self._process(),
354
+ state=DatasetState.FAILED,
355
+ details=str(e),
356
+ )
357
+ raise e
358
+
359
+ # publication
360
+ try:
361
+ self._task_publish.run()
362
+ except Exception as e:
363
+ if self._process is not None:
364
+ ProcessManager().notify_dataset_state(
365
+ dataset=self.inputs.screenshots.scan,
366
+ process=self._process(),
367
+ state=DatasetState.FAILED,
368
+ details=str(e),
369
+ )
370
+ raise e
371
+ else:
372
+ if self._process is not None:
373
+ ProcessManager().notify_dataset_state(
374
+ dataset=self.inputs.screenshots.scan,
375
+ process=self._process(),
376
+ state=DatasetState.SUCCEED,
377
+ )
@@ -0,0 +1,36 @@
1
+ import weakref
2
+ from tomwer.core.scan.scanbase import TomwerScanBase
3
+
4
+
5
+ class IcatProcessedDataBase:
6
+ """
7
+ Container class to get all the information about processed data to be publish
8
+ """
9
+
10
+ def __init__(self, data_dir: str, scan: TomwerScanBase) -> None:
11
+ self._output_dir = data_dir
12
+ # output dir for the screenshots. The default is expected to be provided by the task creating the screenshot.
13
+ # it can be overwrite by the 'SaveScreenshotTask' if an output dir is provided
14
+ self._scan = weakref.ref(scan) if scan is not None else scan
15
+
16
+ @property
17
+ def data_dir(self):
18
+ return self._output_dir
19
+
20
+ @property
21
+ def scan(self):
22
+ return self._scan() if self._scan is not None else None
23
+
24
+ @property
25
+ def metadata(self) -> dict:
26
+ scan = self.scan
27
+ if scan is not None:
28
+ return scan.build_icat_metadata()
29
+ else:
30
+ return {}
31
+
32
+ def to_dict(self):
33
+ return {
34
+ "output_dir": self.data_dir,
35
+ "icat_metadata": self.metadata,
36
+ }