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
@@ -46,7 +46,9 @@ from ewokscore.task import Task as _EwoksTask
46
46
  from ewokscore.taskwithprogress import TaskWithProgress as _EwoksTaskWithProgress
47
47
  from silx.io.dictdump import dicttoh5, h5todict
48
48
  from silx.io.utils import h5py_read_dataset
49
+ from silx.io.utils import open as open_hdf5
49
50
  from tomoscan.io import HDF5File
51
+ from tomwer.core.utils.locker import FileLockerManager
50
52
 
51
53
 
52
54
  _logger = logging.getLogger(__name__)
@@ -173,19 +175,20 @@ class BaseProcessInfo:
173
175
  :type interpretations: Union[None, dict
174
176
  """
175
177
  assert process_file is not None
176
- try:
177
- self._register_process(
178
- process_file=process_file,
179
- entry=entry,
180
- process=self,
181
- configuration=configuration,
182
- results=results,
183
- process_index=process_index,
184
- interpretations=interpretations,
185
- overwrite=overwrite,
186
- )
187
- except IOError as e:
188
- _logger.error(e)
178
+ with FileLockerManager().get_lock(file_name=process_file):
179
+ try:
180
+ self._register_process(
181
+ process_file=process_file,
182
+ entry=entry,
183
+ process=self,
184
+ configuration=configuration,
185
+ results=results,
186
+ process_index=process_index,
187
+ interpretations=interpretations,
188
+ overwrite=overwrite,
189
+ )
190
+ except IOError as e:
191
+ _logger.error(e)
189
192
 
190
193
  @staticmethod
191
194
  def _register_process(
@@ -373,33 +376,38 @@ class BaseProcessInfo:
373
376
  :return: list of _process_desc(sequence_index, configuration, results)
374
377
  :rtype: list
375
378
  """
376
- processes = []
377
- with HDF5File(process_file, mode="r", swmr=True) as h5f:
379
+ # retrieve process to load
380
+ with open_hdf5(process_file) as h5f:
378
381
  if entry is None:
379
382
  if len(h5f.keys()) > 0:
380
383
  root = h5f[list(h5f.keys())[0]]
381
384
  else:
382
385
  _logger.warning("no process find")
386
+ return []
383
387
  else:
384
388
  root = h5f[entry]
389
+
390
+ processes_to_load = {}
385
391
  for process in root.keys():
386
392
  nx_process = root[process]
387
393
  if (
388
394
  h5py_read_dataset(nx_process["program"])
389
395
  == process_type.program_name()
390
396
  ):
391
- p_order = h5py_read_dataset(nx_process["sequence_index"])
392
- config = h5todict(
393
- process_file, "/".join((nx_process.name, "configuration"))
394
- )
395
- results = h5todict(
396
- process_file, "/".join((nx_process.name, "results"))
397
- )
398
- processes.append(
399
- _process_desc(
400
- process_order=p_order, configuration=config, results=results
401
- )
397
+ processes_to_load[nx_process.name] = h5py_read_dataset(
398
+ nx_process["sequence_index"]
402
399
  )
400
+
401
+ # load process
402
+ processes = []
403
+ for process_name, p_order in processes_to_load.items():
404
+ config = h5todict(process_file, "/".join((process_name, "configuration")))
405
+ results = h5todict(process_file, "/".join((process_name, "results")))
406
+ processes.append(
407
+ _process_desc(
408
+ process_order=p_order, configuration=config, results=results
409
+ )
410
+ )
403
411
  return processes
404
412
 
405
413
 
@@ -34,6 +34,7 @@ import tempfile
34
34
  import unittest
35
35
 
36
36
  import h5py
37
+ from tomoscan.io import get_swmr_mode
37
38
  import numpy
38
39
  from silx.io.utils import h5py_read_dataset
39
40
 
@@ -42,8 +43,8 @@ from tomwer.core.process.reconstruction.axis.params import AxisRP
42
43
  from tomwer.core.process.task import Task
43
44
  from tomwer.core.scan.edfscan import EDFTomoScan
44
45
  from tomwer.core.scan.scanbase import TomwerScanBase
45
- from tomwer.core.utils.scanutils import MockEDF, MockHDF5
46
- from tomwer.tests.utils import UtilsTest
46
+ from tomwer.core.utils.scanutils import MockEDF, MockNXtomo
47
+ from tomwer.tests.datasets import TomwerCIDatasets
47
48
 
48
49
  from ..reconstruction.axis.axis import AxisTask
49
50
 
@@ -93,9 +94,9 @@ class TestAxisIO(unittest.TestCase):
93
94
  )
94
95
 
95
96
  # patch the axis process
96
- axis_process._CALCULATIONS_METHODS[
97
- AxisMode.centered
98
- ] = TestAxisIO._random_calc
97
+ axis_process._CALCULATIONS_METHODS[AxisMode.centered] = (
98
+ TestAxisIO._random_calc
99
+ )
99
100
 
100
101
  axis_process.run()
101
102
  out = axis_process.outputs.data
@@ -131,7 +132,7 @@ class TestAxis(unittest.TestCase):
131
132
  axis_process.run()
132
133
  self.assertTrue(os.path.exists(scan.process_file))
133
134
 
134
- with h5py.File(scan.process_file, "r", swmr=True) as h5f:
135
+ with h5py.File(scan.process_file, "r", swmr=get_swmr_mode()) as h5f:
135
136
  self.assertTrue("entry" in h5f)
136
137
  self.assertTrue("tomwer_process_0" in h5f["entry"])
137
138
  group_axis = h5f["entry"]["tomwer_process_0"]
@@ -163,7 +164,7 @@ class TestAxis(unittest.TestCase):
163
164
  """
164
165
  self.tempdir = tempfile.mkdtemp()
165
166
  dim = 10
166
- mock = MockHDF5(
167
+ mock = MockNXtomo(
167
168
  scan_path=self.tempdir, n_proj=10, n_ini_proj=10, scan_range=180, dim=dim
168
169
  )
169
170
  mock.add_alignment_radio(index=10, angle=90)
@@ -183,7 +184,7 @@ class TestAxis(unittest.TestCase):
183
184
  # make sure center of position has been computed
184
185
  self.assertTrue(os.path.exists(scan.process_file))
185
186
 
186
- with h5py.File(scan.process_file, "r", swmr=True) as h5f:
187
+ with h5py.File(scan.process_file, "r", swmr=get_swmr_mode()) as h5f:
187
188
  self.assertTrue("entry" in h5f)
188
189
  self.assertTrue("tomwer_process_0" in h5f["entry"])
189
190
  group_axis = h5f["entry"]["tomwer_process_0"]
@@ -234,8 +235,8 @@ class TestAxisRP(unittest.TestCase):
234
235
  self.axis_rp.set_position_frm_par_file(existing_empty_file, force=False)
235
236
  self.assertEqual(self.axis_rp.value_ref_tomwer, old_value)
236
237
 
237
- valid_par_file = os.path.join(
238
- UtilsTest.getEDFDataset("scan_3_"), "scan_3_slice.par"
238
+ valid_par_file = TomwerCIDatasets.get_dataset(
239
+ "edf_datasets/scan_3_/scan_3_slice.par"
239
240
  )
240
241
  assert os.path.isfile(valid_par_file)
241
242
  self.axis_rp.set_position_frm_par_file(valid_par_file, force=False)
@@ -255,12 +256,12 @@ class TestSinogramAlgorithm(unittest.TestCase):
255
256
  def setUp(self) -> None:
256
257
  self.tmp_folder = tempfile.mkdtemp()
257
258
  dim = 512
258
- self.scan = MockHDF5(
259
+ self.scan = MockNXtomo(
259
260
  scan_path=self.tmp_folder,
260
261
  n_proj=10,
261
262
  n_ini_proj=10,
262
263
  create_ini_dark=True,
263
- create_final_ref=True,
264
+ create_final_flat=True,
264
265
  scan_range=360,
265
266
  dim=dim,
266
267
  ).scan
@@ -38,10 +38,10 @@ from tomoscan.esrf.scan.utils import get_data
38
38
 
39
39
  from tomwer.core.process.reconstruction.darkref.params import DKRFRP
40
40
  from tomwer.core.process.reconstruction.darkref.params import ReduceMethod as cMethod
41
- from tomwer.core.scan.hdf5scan import HDF5TomoScan
41
+ from tomwer.core.scan.nxtomoscan import NXtomoScan
42
42
  from tomwer.core.scan.scanbase import TomwerScanBase
43
- from tomwer.core.utils.scanutils import MockEDF, MockHDF5
44
- from tomwer.tests.utils import UtilsTest
43
+ from tomwer.core.utils.scanutils import MockEDF, MockNXtomo
44
+ from tomwer.tests.datasets import TomwerCIDatasets
45
45
 
46
46
  from ..reconstruction.darkref.darkrefs import DarkRefsTask
47
47
  from ..reconstruction.darkref.darkrefscopy import DarkRefsCopy
@@ -56,7 +56,9 @@ class TestDarkRefIO(unittest.TestCase):
56
56
  self.scan_edf = MockEDF.mockScan(
57
57
  scanID=self.scan_folder, nRadio=10, nRecons=1, nPagRecons=4, dim=10
58
58
  )
59
- self.mock_hdf5 = MockHDF5(scan_path=self.scan_folder, n_proj=10, n_pag_recons=0)
59
+ self.mock_hdf5 = MockNXtomo(
60
+ scan_path=self.scan_folder, n_proj=10, n_pag_recons=0
61
+ )
60
62
  self.scan_hdf5 = self.mock_hdf5.scan
61
63
 
62
64
  self.recons_params = DKRFRP()
@@ -102,7 +104,7 @@ class TestDarkRefCopyIO(unittest.TestCase):
102
104
  self.scan_edf = MockEDF.mockScan(
103
105
  scanID=self.scan_folder, nRadio=10, nRecons=1, nPagRecons=4, dim=10
104
106
  )
105
- self.scan_hdf5 = MockHDF5(
107
+ self.scan_hdf5 = MockNXtomo(
106
108
  scan_path=self.scan_folder, n_proj=10, n_pag_recons=0
107
109
  ).scan
108
110
  self.recons_params = DKRFRP()
@@ -256,9 +258,10 @@ class TestDarkRefNx(unittest.TestCase):
256
258
  self.scan_folder = tempfile.mkdtemp()
257
259
  self._file_path = os.path.join(self.scan_folder, dataset_name)
258
260
  shutil.copyfile(
259
- src=UtilsTest.getH5Dataset(folderID=dataset_name), dst=self._file_path
261
+ src=TomwerCIDatasets.get_dataset(f"h5_datasets/{dataset_name}"),
262
+ dst=self._file_path,
260
263
  )
261
- self.scan = HDF5TomoScan(scan=self._file_path, entry="entry0000")
264
+ self.scan = NXtomoScan(scan=self._file_path, entry="entry0000")
262
265
  self.assertFalse(os.path.exists(self.scan.process_file))
263
266
  self.recons_params = DKRFRP()
264
267
  self.recons_params.overwrite_dark = True
@@ -40,17 +40,19 @@ import numpy
40
40
  import pytest
41
41
  from nxtomomill.converter import from_h5_to_nx
42
42
  from nxtomomill.io.config.hdf5config import TomoHDF5Config
43
- from tomoscan.esrf.scan.hdf5scan import ImageKey
43
+ from nxtomo.nxobject.nxdetector import ImageKey
44
+
45
+ from silx.io.utils import open as open_hdf5
44
46
  from tomoscan.io import HDF5File
45
47
  from tomoscan.validator import is_valid_for_reconstruction
46
48
 
47
49
  from tomwer.core.process.control.datalistener import DataListener
48
50
  from tomwer.core.process.control.scantransfer import ScanTransferTask
49
- from tomwer.core.scan.hdf5scan import HDF5TomoScan
51
+ from tomwer.core.scan.nxtomoscan import NXtomoScan
50
52
  from tomwer.core.scan.scanbase import TomwerScanBase
51
53
  from tomwer.core.utils.scanutils import MockEDF
52
54
  from tomwer.synctools.rsyncmanager import RSyncManager
53
- from tomwer.tests.utils import UtilsTest
55
+ from tomwer.tests.datasets import TomwerCIDatasets
54
56
 
55
57
 
56
58
  class TestDataTransferIO(unittest.TestCase):
@@ -119,7 +121,7 @@ class TestBlissDataTransfer(unittest.TestCase):
119
121
  self.input_dir = tempfile.mkdtemp()
120
122
  self.output_dir = tempfile.mkdtemp()
121
123
  shutil.copytree(
122
- UtilsTest.getBlissDataset(folderID="sample"),
124
+ TomwerCIDatasets.get_dataset("bliss/sample"),
123
125
  os.path.join(self.input_dir, "sample"),
124
126
  )
125
127
 
@@ -204,7 +206,7 @@ class TestBlissDataTransfer(unittest.TestCase):
204
206
  configuration.file_extension = ".nx"
205
207
  from_h5_to_nx(configuration, input_callback=lambda *arg: 0)
206
208
  assert os.path.exists(output_file)
207
- assert is_valid_for_reconstruction(HDF5TomoScan(output_file, entry="entry0000"))
209
+ assert is_valid_for_reconstruction(NXtomoScan(output_file, entry="entry0000"))
208
210
 
209
211
 
210
212
  @pytest.mark.skipif(not RSyncManager().has_rsync(), reason="requires rsync")
@@ -319,7 +321,7 @@ class NXTomoDataTransferBase(unittest.TestCase):
319
321
  return super().tearDown()
320
322
 
321
323
  def _test(self):
322
- src_scan = HDF5TomoScan(self.nexus_file_path, entry="entry0000")
324
+ src_scan = NXtomoScan(self.nexus_file_path, entry="entry0000")
323
325
  assert is_valid_for_reconstruction(src_scan)
324
326
  process = ScanTransferTask(
325
327
  inputs={
@@ -334,9 +336,9 @@ class NXTomoDataTransferBase(unittest.TestCase):
334
336
  dst_scan = process.outputs.data
335
337
  assert dst_scan.master_file != self.nexus_file_path
336
338
  assert is_valid_for_reconstruction(dst_scan, check_values=True)
337
- with HDF5File(src_scan.master_file, mode="r") as h5s_src:
339
+ with open_hdf5(src_scan.master_file) as h5s_src:
338
340
  src_dataset = h5s_src["entry0000/detector/data"][...]
339
- with HDF5File(dst_scan.master_file, mode="r") as h5s_dst:
341
+ with open_hdf5(dst_scan.master_file) as h5s_dst:
340
342
  dst_dataset = h5s_dst["entry0000/detector/data"][...]
341
343
  assert numpy.array_equal(src_dataset, dst_dataset)
342
344
 
@@ -41,7 +41,7 @@ from tomwer.core.process.reconstruction.nabu.nabuslices import NabuSliceMode
41
41
  from tomwer.core.scan.edfscan import EDFTomoScan
42
42
  from tomwer.core.scan.scanfactory import ScanFactory
43
43
  from tomwer.core.utils.scanutils import MockEDF
44
- from tomwer.tests.utils import UtilsTest
44
+ from tomwer.tests.datasets import TomwerCIDatasets
45
45
 
46
46
 
47
47
  class TestNabuDescUtils(unittest.TestCase):
@@ -51,7 +51,7 @@ class TestNabuDescUtils(unittest.TestCase):
51
51
  unittest.TestCase.setUp(self)
52
52
  self.top_dir = tempfile.mkdtemp()
53
53
  self.dataset = "scan_3_"
54
- data_test_dir = UtilsTest.getEDFDataset(self.dataset)
54
+ data_test_dir = TomwerCIDatasets.get_dataset(f"edf_datasets/{self.dataset}")
55
55
  scan_folder = os.path.join(self.top_dir, self.dataset)
56
56
  shutil.copytree(data_test_dir, scan_folder)
57
57
  self.scan = ScanFactory.create_scan_object(scan_folder)
@@ -73,7 +73,7 @@ class TestNabuIni(unittest.TestCase):
73
73
  def setUp(self) -> None:
74
74
  self.dataset = "scan_3_"
75
75
  self._root_dir = tempfile.mkdtemp()
76
- data_test_dir = UtilsTest.getEDFDataset(self.dataset)
76
+ data_test_dir = TomwerCIDatasets.get_dataset("edf_datasets/scan_3_")
77
77
  scan_folder = os.path.join(self._root_dir, self.dataset)
78
78
  shutil.copytree(data_test_dir, scan_folder)
79
79
  self.scan = EDFTomoScan(scan_folder)
@@ -127,7 +127,7 @@ class TestNabuIni(unittest.TestCase):
127
127
  # check volume and slice .cfg files for nabu reconstruction are here
128
128
  for file_name in (
129
129
  os.path.basename(self.scan.path) + ".cfg",
130
- os.path.basename(self.scan.path) + "slice_000001" + ".cfg",
130
+ os.path.basename(self.scan.path) + "slice_000001_plane_XY" + ".cfg",
131
131
  ):
132
132
  with self.subTest(file_name=file_name):
133
133
  self.assertTrue(
@@ -151,7 +151,9 @@ class TestNabuIni(unittest.TestCase):
151
151
  # check volume and slice .cfg files for nabu reconstruction are here
152
152
  for file_name in (
153
153
  os.path.basename(self.scan.path) + "pag_db0100.cfg",
154
- os.path.basename(self.scan.path) + "slice_pag_000001_db0100" + ".cfg",
154
+ os.path.basename(self.scan.path)
155
+ + "slice_pag_000001_db0100_plane_XY"
156
+ + ".cfg",
155
157
  ):
156
158
  with self.subTest(file_name=file_name):
157
159
  self.assertTrue(
@@ -179,10 +181,13 @@ class TestNabuIni(unittest.TestCase):
179
181
  os.path.basename(self.scan.path)
180
182
  + "slice_"
181
183
  + str(slice_index).zfill(6)
184
+ + "_plane_XY"
182
185
  + ".cfg"
183
186
  )
184
187
 
188
+ print("nabu_files are", nabu_files)
185
189
  for ini_file in ini_files:
190
+ print("check ini_file", ini_file)
186
191
  with self.subTest(file_name=ini_file):
187
192
  self.assertTrue(
188
193
  os.path.join(self._nabu_cfg_folder, ini_file) in nabu_files
@@ -220,10 +225,12 @@ class TestNabuIni(unittest.TestCase):
220
225
  + ".cfg"
221
226
  )
222
227
  # add slice ini file
228
+ # note: plane is only specify for slice.
223
229
  ini_files.append(
224
230
  os.path.basename(self.scan.path)
225
231
  + "slice_pag_000001_db"
226
232
  + str(int(pag_db)).zfill(4)
233
+ + "_plane_XY"
227
234
  + ".cfg"
228
235
  )
229
236
 
@@ -268,6 +275,7 @@ class TestNabuIni(unittest.TestCase):
268
275
  + str(slice_index).zfill(6)
269
276
  + "_db"
270
277
  + str(pag_db).zfill(4)
278
+ + "_plane_XY"
271
279
  + ".cfg"
272
280
  )
273
281
 
@@ -356,7 +364,7 @@ class TestNabuAndAxis(unittest.TestCase):
356
364
  def setUp(self) -> None:
357
365
  self.dataset = "scan_3_"
358
366
  self._root_dir = tempfile.mkdtemp()
359
- data_test_dir = UtilsTest.getEDFDataset(self.dataset)
367
+ data_test_dir = TomwerCIDatasets.get_dataset("edf_datasets/scan_3_")
360
368
  scan_folder = os.path.join(self._root_dir, self.dataset)
361
369
  shutil.copytree(data_test_dir, scan_folder)
362
370
  self.scan = EDFTomoScan(scan_folder)
@@ -36,7 +36,7 @@ import numpy
36
36
  from tomoscan.normalization import Method
37
37
 
38
38
  from tomwer.core.process.reconstruction.normalization import normalization, params
39
- from tomwer.core.utils.scanutils import MockHDF5
39
+ from tomwer.core.utils.scanutils import MockNXtomo
40
40
 
41
41
 
42
42
  class TestNormalization(unittest.TestCase):
@@ -47,15 +47,15 @@ class TestNormalization(unittest.TestCase):
47
47
  def setUp(self) -> None:
48
48
  self.tempdir = tempfile.mkdtemp()
49
49
  dim = 100
50
- self.scan = MockHDF5(
50
+ self.scan = MockNXtomo(
51
51
  scan_path=self.tempdir,
52
52
  n_proj=2,
53
53
  n_ini_proj=2,
54
54
  scan_range=180,
55
55
  dim=dim,
56
56
  create_ini_dark=False,
57
- create_ini_ref=False,
58
- create_final_ref=False,
57
+ create_ini_flat=False,
58
+ create_final_flat=False,
59
59
  ).scan
60
60
 
61
61
  def tearDown(self) -> None:
@@ -54,7 +54,7 @@ import logging
54
54
  from typing import Optional
55
55
 
56
56
  from silx.io.utils import h5py_read_dataset
57
- from tomoscan.io import HDF5File
57
+ from silx.io.utils import open as open_hdf5
58
58
 
59
59
  _logger = logging.getLogger(__name__)
60
60
 
@@ -203,7 +203,7 @@ class BlissScan:
203
203
  return True
204
204
  return False
205
205
 
206
- with HDF5File(file_path, mode="r", swmr=True) as h5s:
206
+ with open_hdf5(file_path) as h5s:
207
207
  if not isinstance(h5s, h5py.Group) or not isinstance(
208
208
  h5s[entry], h5py.Group
209
209
  ):
@@ -227,7 +227,7 @@ class BlissScan:
227
227
  return tuple()
228
228
  else:
229
229
  res = []
230
- with HDF5File(file_path, mode="r", swmr=True) as h5s:
230
+ with open_hdf5(file_path) as h5s:
231
231
  for entry in h5s:
232
232
  if BlissScan.is_bliss_valid_entry(file_path=file_path, entry=entry):
233
233
  res.append(entry)
@@ -48,7 +48,7 @@ from tomoscan.esrf.identifier.edfidentifier import (
48
48
  )
49
49
  from tomoscan.esrf.scan.edfscan import EDFTomoScan as _tsEDFTomoScan
50
50
  from tomoscan.esrf.scan.utils import get_data
51
- from tomoscan.scanbase import FOV
51
+ from nxtomo.nxobject.nxdetector import FOV
52
52
 
53
53
  from tomwer.core.process.reconstruction.darkref.settings import (
54
54
  DARKHST_PREFIX,
@@ -85,6 +85,9 @@ class EDFTomoScanIdentifier(_EDFTomoScanIdentifier, DatasetIdentifier):
85
85
  """used for processview header tooltip for now"""
86
86
  return self.to_str()
87
87
 
88
+ def short_description(self) -> str:
89
+ return f"scan: {os.path.basename(os.path.basename(self.folder))} (EDF)"
90
+
88
91
 
89
92
  class EDFTomoScan(_tsEDFTomoScan, TomwerScanBase):
90
93
  """
@@ -278,9 +281,9 @@ class EDFTomoScan(_tsEDFTomoScan, TomwerScanBase):
278
281
  files.append(gfile)
279
282
  if f.endswith(".vol"):
280
283
  if withIndex is True:
281
- files[
282
- EDFTomoScan.get_index_reconstructed(f, scanID)
283
- ] = os.path.join(scanID, f)
284
+ files[EDFTomoScan.get_index_reconstructed(f, scanID)] = (
285
+ os.path.join(scanID, f)
286
+ )
284
287
  else:
285
288
  files.append(os.path.join(scanID, f))
286
289
  return files
@@ -549,12 +552,12 @@ class EDFTomoScan(_tsEDFTomoScan, TomwerScanBase):
549
552
  file_prefix=self.dataset_basename,
550
553
  metadata={
551
554
  "name": self.path,
552
- "creation_time": datetime.fromtimestamp(stat.st_ctime)
553
- if stat
554
- else None,
555
- "modification_time": datetime.fromtimestamp(stat.st_ctime)
556
- if stat
557
- else None,
555
+ "creation_time": (
556
+ datetime.fromtimestamp(stat.st_ctime) if stat else None
557
+ ),
558
+ "modification_time": (
559
+ datetime.fromtimestamp(stat.st_ctime) if stat else None
560
+ ),
558
561
  },
559
562
  )
560
563