scitex 2.7.0__py3-none-any.whl → 2.8.1__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 (355) hide show
  1. scitex/__init__.py +6 -2
  2. scitex/__version__.py +1 -1
  3. scitex/audio/README.md +52 -0
  4. scitex/audio/__init__.py +384 -0
  5. scitex/audio/__main__.py +129 -0
  6. scitex/audio/_tts.py +334 -0
  7. scitex/audio/engines/__init__.py +44 -0
  8. scitex/audio/engines/base.py +275 -0
  9. scitex/audio/engines/elevenlabs_engine.py +143 -0
  10. scitex/audio/engines/gtts_engine.py +162 -0
  11. scitex/audio/engines/pyttsx3_engine.py +131 -0
  12. scitex/audio/mcp_server.py +757 -0
  13. scitex/bridge/_helpers.py +1 -1
  14. scitex/bridge/_plt_vis.py +1 -1
  15. scitex/bridge/_stats_vis.py +1 -1
  16. scitex/dev/plt/__init__.py +272 -0
  17. scitex/dev/plt/plot_mpl_axhline.py +28 -0
  18. scitex/dev/plt/plot_mpl_axhspan.py +28 -0
  19. scitex/dev/plt/plot_mpl_axvline.py +28 -0
  20. scitex/dev/plt/plot_mpl_axvspan.py +28 -0
  21. scitex/dev/plt/plot_mpl_bar.py +29 -0
  22. scitex/dev/plt/plot_mpl_barh.py +29 -0
  23. scitex/dev/plt/plot_mpl_boxplot.py +28 -0
  24. scitex/dev/plt/plot_mpl_contour.py +31 -0
  25. scitex/dev/plt/plot_mpl_contourf.py +31 -0
  26. scitex/dev/plt/plot_mpl_errorbar.py +30 -0
  27. scitex/dev/plt/plot_mpl_eventplot.py +28 -0
  28. scitex/dev/plt/plot_mpl_fill.py +30 -0
  29. scitex/dev/plt/plot_mpl_fill_between.py +31 -0
  30. scitex/dev/plt/plot_mpl_hexbin.py +28 -0
  31. scitex/dev/plt/plot_mpl_hist.py +28 -0
  32. scitex/dev/plt/plot_mpl_hist2d.py +28 -0
  33. scitex/dev/plt/plot_mpl_imshow.py +29 -0
  34. scitex/dev/plt/plot_mpl_pcolormesh.py +31 -0
  35. scitex/dev/plt/plot_mpl_pie.py +29 -0
  36. scitex/dev/plt/plot_mpl_plot.py +29 -0
  37. scitex/dev/plt/plot_mpl_quiver.py +31 -0
  38. scitex/dev/plt/plot_mpl_scatter.py +28 -0
  39. scitex/dev/plt/plot_mpl_stackplot.py +31 -0
  40. scitex/dev/plt/plot_mpl_stem.py +29 -0
  41. scitex/dev/plt/plot_mpl_step.py +29 -0
  42. scitex/dev/plt/plot_mpl_violinplot.py +28 -0
  43. scitex/dev/plt/plot_sns_barplot.py +29 -0
  44. scitex/dev/plt/plot_sns_boxplot.py +29 -0
  45. scitex/dev/plt/plot_sns_heatmap.py +28 -0
  46. scitex/dev/plt/plot_sns_histplot.py +29 -0
  47. scitex/dev/plt/plot_sns_kdeplot.py +29 -0
  48. scitex/dev/plt/plot_sns_lineplot.py +31 -0
  49. scitex/dev/plt/plot_sns_scatterplot.py +29 -0
  50. scitex/dev/plt/plot_sns_stripplot.py +29 -0
  51. scitex/dev/plt/plot_sns_swarmplot.py +29 -0
  52. scitex/dev/plt/plot_sns_violinplot.py +29 -0
  53. scitex/dev/plt/plot_stx_bar.py +29 -0
  54. scitex/dev/plt/plot_stx_barh.py +29 -0
  55. scitex/dev/plt/plot_stx_box.py +28 -0
  56. scitex/dev/plt/plot_stx_boxplot.py +28 -0
  57. scitex/dev/plt/plot_stx_conf_mat.py +28 -0
  58. scitex/dev/plt/plot_stx_contour.py +31 -0
  59. scitex/dev/plt/plot_stx_ecdf.py +28 -0
  60. scitex/dev/plt/plot_stx_errorbar.py +30 -0
  61. scitex/dev/plt/plot_stx_fill_between.py +31 -0
  62. scitex/dev/plt/plot_stx_fillv.py +28 -0
  63. scitex/dev/plt/plot_stx_heatmap.py +28 -0
  64. scitex/dev/plt/plot_stx_image.py +28 -0
  65. scitex/dev/plt/plot_stx_imshow.py +28 -0
  66. scitex/dev/plt/plot_stx_joyplot.py +28 -0
  67. scitex/dev/plt/plot_stx_kde.py +28 -0
  68. scitex/dev/plt/plot_stx_line.py +28 -0
  69. scitex/dev/plt/plot_stx_mean_ci.py +28 -0
  70. scitex/dev/plt/plot_stx_mean_std.py +28 -0
  71. scitex/dev/plt/plot_stx_median_iqr.py +28 -0
  72. scitex/dev/plt/plot_stx_raster.py +28 -0
  73. scitex/dev/plt/plot_stx_rectangle.py +28 -0
  74. scitex/dev/plt/plot_stx_scatter.py +29 -0
  75. scitex/dev/plt/plot_stx_shaded_line.py +29 -0
  76. scitex/dev/plt/plot_stx_violin.py +28 -0
  77. scitex/dev/plt/plot_stx_violinplot.py +28 -0
  78. scitex/diagram/README.md +197 -0
  79. scitex/diagram/__init__.py +48 -0
  80. scitex/diagram/_compile.py +312 -0
  81. scitex/diagram/_diagram.py +355 -0
  82. scitex/diagram/_presets.py +173 -0
  83. scitex/diagram/_schema.py +182 -0
  84. scitex/diagram/_split.py +278 -0
  85. scitex/fig/__init__.py +352 -0
  86. scitex/{vis → fig}/backend/_parser.py +1 -1
  87. scitex/{vis → fig}/canvas.py +1 -1
  88. scitex/{vis → fig}/editor/__init__.py +5 -2
  89. scitex/{vis → fig}/editor/_dearpygui_editor.py +1 -1
  90. scitex/{vis → fig}/editor/_defaults.py +70 -5
  91. scitex/{vis → fig}/editor/_mpl_editor.py +1 -1
  92. scitex/{vis → fig}/editor/_qt_editor.py +182 -2
  93. scitex/{vis → fig}/editor/_tkinter_editor.py +1 -1
  94. scitex/fig/editor/edit/__init__.py +50 -0
  95. scitex/fig/editor/edit/backend_detector.py +109 -0
  96. scitex/fig/editor/edit/bundle_resolver.py +240 -0
  97. scitex/fig/editor/edit/editor_launcher.py +239 -0
  98. scitex/fig/editor/edit/manual_handler.py +53 -0
  99. scitex/fig/editor/edit/panel_loader.py +232 -0
  100. scitex/fig/editor/edit/path_resolver.py +67 -0
  101. scitex/fig/editor/flask_editor/_bbox.py +1299 -0
  102. scitex/fig/editor/flask_editor/_core.py +1429 -0
  103. scitex/{vis → fig}/editor/flask_editor/_plotter.py +38 -4
  104. scitex/fig/editor/flask_editor/_renderer.py +813 -0
  105. scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
  106. scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
  107. scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
  108. scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
  109. scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
  110. scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
  111. scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
  112. scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
  113. scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
  114. scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
  115. scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
  116. scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
  117. scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
  118. scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
  119. scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
  120. scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
  121. scitex/fig/editor/flask_editor/static/css/index.css +31 -0
  122. scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
  123. scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
  124. scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
  125. scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
  126. scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
  127. scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
  128. scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
  129. scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
  130. scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
  131. scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
  132. scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
  133. scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
  134. scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
  135. scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
  136. scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
  137. scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
  138. scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
  139. scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
  140. scitex/fig/editor/flask_editor/static/js/main.js +426 -0
  141. scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
  142. scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
  143. scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
  144. scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
  145. scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
  146. scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
  147. scitex/fig/editor/flask_editor/templates/__init__.py +123 -0
  148. scitex/fig/editor/flask_editor/templates/_html.py +852 -0
  149. scitex/fig/editor/flask_editor/templates/_scripts.py +4933 -0
  150. scitex/fig/editor/flask_editor/templates/_styles.py +1658 -0
  151. scitex/{vis → fig}/io/__init__.py +13 -1
  152. scitex/fig/io/_bundle.py +1058 -0
  153. scitex/{vis → fig}/io/_canvas.py +1 -1
  154. scitex/{vis → fig}/io/_data.py +1 -1
  155. scitex/{vis → fig}/io/_export.py +1 -1
  156. scitex/{vis → fig}/io/_load.py +1 -1
  157. scitex/{vis → fig}/io/_panel.py +1 -1
  158. scitex/{vis → fig}/io/_save.py +1 -1
  159. scitex/{vis → fig}/model/__init__.py +1 -1
  160. scitex/{vis → fig}/model/_annotations.py +1 -1
  161. scitex/{vis → fig}/model/_axes.py +1 -1
  162. scitex/{vis → fig}/model/_figure.py +1 -1
  163. scitex/{vis → fig}/model/_guides.py +1 -1
  164. scitex/{vis → fig}/model/_plot.py +1 -1
  165. scitex/{vis → fig}/model/_styles.py +1 -1
  166. scitex/{vis → fig}/utils/__init__.py +1 -1
  167. scitex/io/__init__.py +22 -26
  168. scitex/io/_bundle.py +493 -0
  169. scitex/io/_flush.py +5 -2
  170. scitex/io/_load.py +98 -0
  171. scitex/io/_load_modules/_H5Explorer.py +5 -2
  172. scitex/io/_load_modules/_canvas.py +2 -2
  173. scitex/io/_load_modules/_image.py +3 -4
  174. scitex/io/_load_modules/_txt.py +4 -2
  175. scitex/io/_metadata.py +34 -324
  176. scitex/io/_metadata_modules/__init__.py +46 -0
  177. scitex/io/_metadata_modules/_embed.py +70 -0
  178. scitex/io/_metadata_modules/_read.py +64 -0
  179. scitex/io/_metadata_modules/_utils.py +79 -0
  180. scitex/io/_metadata_modules/embed_metadata_jpeg.py +74 -0
  181. scitex/io/_metadata_modules/embed_metadata_pdf.py +53 -0
  182. scitex/io/_metadata_modules/embed_metadata_png.py +26 -0
  183. scitex/io/_metadata_modules/embed_metadata_svg.py +62 -0
  184. scitex/io/_metadata_modules/read_metadata_jpeg.py +57 -0
  185. scitex/io/_metadata_modules/read_metadata_pdf.py +51 -0
  186. scitex/io/_metadata_modules/read_metadata_png.py +39 -0
  187. scitex/io/_metadata_modules/read_metadata_svg.py +44 -0
  188. scitex/io/_qr_utils.py +5 -3
  189. scitex/io/_save.py +548 -30
  190. scitex/io/_save_modules/_canvas.py +3 -3
  191. scitex/io/_save_modules/_image.py +5 -9
  192. scitex/io/_save_modules/_tex.py +7 -4
  193. scitex/io/_zip_bundle.py +439 -0
  194. scitex/io/utils/h5_to_zarr.py +11 -9
  195. scitex/msword/__init__.py +255 -0
  196. scitex/msword/profiles.py +357 -0
  197. scitex/msword/reader.py +753 -0
  198. scitex/msword/utils.py +289 -0
  199. scitex/msword/writer.py +362 -0
  200. scitex/plt/__init__.py +5 -2
  201. scitex/plt/_subplots/_AxesWrapper.py +6 -6
  202. scitex/plt/_subplots/_AxisWrapper.py +15 -9
  203. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +36 -0
  204. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +264 -0
  205. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +213 -0
  206. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +128 -0
  207. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +59 -0
  208. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +34 -0
  209. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +593 -0
  210. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +654 -0
  211. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +527 -0
  212. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +321 -0
  213. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +33 -0
  214. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +152 -0
  215. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +600 -0
  216. scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +79 -5
  217. scitex/plt/_subplots/_FigWrapper.py +6 -6
  218. scitex/plt/_subplots/_SubplotsWrapper.py +28 -18
  219. scitex/plt/_subplots/_export_as_csv.py +35 -5
  220. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +8 -0
  221. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +10 -21
  222. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +18 -7
  223. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +28 -12
  224. scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +10 -4
  225. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +13 -1
  226. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +12 -2
  227. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +10 -3
  228. scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -4
  229. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +18 -3
  230. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +44 -36
  231. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +14 -2
  232. scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +11 -5
  233. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +84 -0
  234. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +85 -0
  235. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +14 -3
  236. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_contour.py +54 -0
  237. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +14 -2
  238. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +120 -0
  239. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +16 -6
  240. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +29 -19
  241. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_imshow.py +63 -0
  242. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +22 -5
  243. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +18 -14
  244. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +18 -14
  245. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +18 -14
  246. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +10 -2
  247. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +51 -0
  248. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +18 -9
  249. scitex/plt/ax/_plot/_stx_ecdf.py +4 -2
  250. scitex/plt/gallery/_generate.py +421 -14
  251. scitex/plt/io/__init__.py +53 -0
  252. scitex/plt/io/_bundle.py +490 -0
  253. scitex/plt/io/_layered_bundle.py +1343 -0
  254. scitex/plt/styles/SCITEX_STYLE.yaml +26 -0
  255. scitex/plt/styles/__init__.py +14 -0
  256. scitex/plt/styles/presets.py +78 -0
  257. scitex/plt/utils/__init__.py +13 -1
  258. scitex/plt/utils/_collect_figure_metadata.py +10 -14
  259. scitex/plt/utils/_configure_mpl.py +6 -18
  260. scitex/plt/utils/_crop.py +32 -14
  261. scitex/plt/utils/_csv_column_naming.py +54 -0
  262. scitex/plt/utils/_figure_mm.py +116 -1
  263. scitex/plt/utils/_hitmap.py +1643 -0
  264. scitex/plt/utils/metadata/__init__.py +25 -0
  265. scitex/plt/utils/metadata/_core.py +9 -10
  266. scitex/plt/utils/metadata/_dimensions.py +6 -3
  267. scitex/plt/utils/metadata/_editable_export.py +405 -0
  268. scitex/plt/utils/metadata/_geometry_extraction.py +570 -0
  269. scitex/schema/__init__.py +109 -16
  270. scitex/schema/_canvas.py +1 -1
  271. scitex/schema/_plot.py +1015 -0
  272. scitex/schema/_stats.py +2 -2
  273. scitex/stats/__init__.py +117 -0
  274. scitex/stats/io/__init__.py +29 -0
  275. scitex/stats/io/_bundle.py +156 -0
  276. scitex/tex/__init__.py +4 -0
  277. scitex/tex/_export.py +890 -0
  278. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/METADATA +11 -1
  279. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/RECORD +294 -170
  280. scitex/io/memo.md +0 -2827
  281. scitex/plt/REQUESTS.md +0 -191
  282. scitex/plt/_subplots/TODO.md +0 -53
  283. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +0 -559
  284. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +0 -1609
  285. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +0 -447
  286. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +0 -110
  287. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +0 -88
  288. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +0 -103
  289. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +0 -106
  290. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +0 -92
  291. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +0 -92
  292. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +0 -92
  293. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +0 -84
  294. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +0 -84
  295. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +0 -83
  296. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +0 -93
  297. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +0 -91
  298. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +0 -91
  299. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +0 -91
  300. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +0 -97
  301. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +0 -98
  302. scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +0 -84
  303. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +0 -101
  304. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +0 -96
  305. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +0 -95
  306. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +0 -95
  307. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +0 -93
  308. scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +0 -95
  309. scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +0 -95
  310. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +0 -83
  311. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +0 -92
  312. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +0 -121
  313. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +0 -84
  314. scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +0 -110
  315. scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +0 -92
  316. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +0 -95
  317. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +0 -96
  318. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +0 -95
  319. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +0 -95
  320. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +0 -92
  321. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +0 -84
  322. scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +0 -94
  323. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +0 -109
  324. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +0 -108
  325. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +0 -93
  326. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +0 -84
  327. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +0 -96
  328. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +0 -96
  329. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +0 -96
  330. scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +0 -99
  331. scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +0 -100
  332. scitex/vis/__init__.py +0 -177
  333. scitex/vis/editor/_edit.py +0 -390
  334. scitex/vis/editor/flask_editor/_bbox.py +0 -529
  335. scitex/vis/editor/flask_editor/_core.py +0 -168
  336. scitex/vis/editor/flask_editor/_renderer.py +0 -393
  337. scitex/vis/editor/flask_editor/templates/__init__.py +0 -33
  338. scitex/vis/editor/flask_editor/templates/_html.py +0 -513
  339. scitex/vis/editor/flask_editor/templates/_scripts.py +0 -1261
  340. scitex/vis/editor/flask_editor/templates/_styles.py +0 -739
  341. /scitex/{vis → fig}/README.md +0 -0
  342. /scitex/{vis → fig}/backend/__init__.py +0 -0
  343. /scitex/{vis → fig}/backend/_export.py +0 -0
  344. /scitex/{vis → fig}/backend/_render.py +0 -0
  345. /scitex/{vis → fig}/docs/CANVAS_ARCHITECTURE.md +0 -0
  346. /scitex/{vis → fig}/editor/_flask_editor.py +0 -0
  347. /scitex/{vis → fig}/editor/flask_editor/__init__.py +0 -0
  348. /scitex/{vis → fig}/editor/flask_editor/_utils.py +0 -0
  349. /scitex/{vis → fig}/io/_directory.py +0 -0
  350. /scitex/{vis → fig}/model/_plot_types.py +0 -0
  351. /scitex/{vis → fig}/utils/_defaults.py +0 -0
  352. /scitex/{vis → fig}/utils/_validate.py +0 -0
  353. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/WHEEL +0 -0
  354. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/entry_points.txt +0 -0
  355. {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,239 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-14 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fig/editor/edit/editor_launcher.py
5
+
6
+ """Main edit function for launching visual editor."""
7
+
8
+ from pathlib import Path
9
+ from typing import Union, Literal
10
+
11
+ from .backend_detector import print_available_backends, detect_best_backend
12
+ from .path_resolver import resolve_figure_paths
13
+ from .bundle_resolver import resolve_pltz_bundle, resolve_figz_bundle
14
+
15
+ __all__ = ["edit"]
16
+
17
+
18
+ def edit(
19
+ path: Union[str, Path],
20
+ backend: Literal["auto", "flask", "dearpygui", "qt", "tkinter", "mpl"] = "auto",
21
+ apply_manual: bool = True,
22
+ port: int = 5050,
23
+ ) -> None:
24
+ """
25
+ Launch interactive editor for figure style/annotation editing.
26
+
27
+ Parameters
28
+ ----------
29
+ path : str or Path
30
+ Path to figure file. Can be:
31
+ - .pltz.d directory bundle (recommended for hitmap selection)
32
+ - .pltz ZIP bundle
33
+ - .figz or .figz.d multi-panel bundle
34
+ - JSON file (figure.json or figure.manual.json)
35
+ - CSV file (figure.csv) - for data-only start
36
+ - PNG file (figure.png)
37
+ backend : str, optional
38
+ GUI backend to use (default: "auto"):
39
+ - "auto": Pick best available (flask -> dearpygui -> qt -> tkinter -> mpl)
40
+ - "flask": Browser-based editor (Flask, modern UI)
41
+ - "dearpygui": GPU-accelerated modern GUI
42
+ - "qt": Rich desktop editor (requires PyQt5/6 or PySide2/6)
43
+ - "tkinter": Built-in Python GUI
44
+ - "mpl": Minimal matplotlib interactive mode
45
+ apply_manual : bool, optional
46
+ If True, load .manual.json overrides if exists (default: True)
47
+ port : int, optional
48
+ Port number for web-based editors. Default: 5050.
49
+
50
+ Returns
51
+ -------
52
+ None
53
+ Editor runs in GUI event loop. Changes saved to .manual.json.
54
+ """
55
+ path = Path(path)
56
+ spath = str(path)
57
+ parent_str = str(path.parent) if path.is_file() else ""
58
+
59
+ # Panel info for multi-panel figures
60
+ panel_info = None
61
+
62
+ # Resolve paths based on input type
63
+ json_path, csv_path, png_path, hitmap_path, bundle_spec, panel_info = _resolve_paths(
64
+ path, spath, parent_str
65
+ )
66
+
67
+ if not json_path.exists():
68
+ raise FileNotFoundError(f"JSON file not found: {json_path}")
69
+
70
+ # Load data
71
+ import scitex as stx
72
+
73
+ metadata = bundle_spec if bundle_spec else stx.io.load(json_path)
74
+ csv_data = None
75
+ if csv_path and csv_path.exists():
76
+ csv_data = stx.io.load(csv_path)
77
+
78
+ # Load manual overrides if exists
79
+ manual_path = json_path.with_suffix(".manual.json")
80
+ manual_overrides = None
81
+ if apply_manual and manual_path.exists():
82
+ manual_data = stx.io.load(manual_path)
83
+ manual_overrides = manual_data.get("overrides", {})
84
+
85
+ # Resolve backend if "auto"
86
+ if backend == "auto":
87
+ backend = detect_best_backend()
88
+
89
+ # Print status
90
+ print_available_backends()
91
+ print(f"Launching {backend} editor for: {json_path}")
92
+
93
+ # Launch appropriate backend
94
+ _launch_backend(
95
+ backend=backend,
96
+ json_path=json_path,
97
+ metadata=metadata,
98
+ csv_data=csv_data,
99
+ png_path=png_path,
100
+ hitmap_path=hitmap_path,
101
+ manual_overrides=manual_overrides,
102
+ bundle_spec=bundle_spec,
103
+ panel_info=panel_info,
104
+ port=port,
105
+ )
106
+
107
+
108
+ def _resolve_paths(path: Path, spath: str, parent_str: str) -> tuple:
109
+ """Resolve paths based on input type."""
110
+ panel_info = None
111
+ hitmap_path = None
112
+ bundle_spec = None
113
+
114
+ # Check if this is a .figz bundle (multi-panel figure)
115
+ if spath.endswith('.figz.d') or spath.endswith('.figz'):
116
+ json_path, csv_path, png_path, hitmap_path, bundle_spec, panel_info = resolve_figz_bundle(path)
117
+ # Check if this is a .pltz bundle
118
+ elif spath.endswith('.pltz.d') or spath.endswith('.pltz') or parent_str.endswith('.pltz.d'):
119
+ bundle_path = path.parent if parent_str.endswith('.pltz.d') else path
120
+ json_path, csv_path, png_path, hitmap_path, bundle_spec = resolve_pltz_bundle(bundle_path)
121
+ # Check if file is inside a .figz.d
122
+ elif parent_str.endswith('.figz.d') or (path.parent.parent and str(path.parent.parent).endswith('.figz.d')):
123
+ figz_path = path.parent if parent_str.endswith('.figz.d') else path.parent.parent
124
+ json_path, csv_path, png_path, hitmap_path, bundle_spec, panel_info = resolve_figz_bundle(figz_path)
125
+ else:
126
+ # Standard file paths
127
+ json_path, csv_path, png_path = resolve_figure_paths(path)
128
+
129
+ return json_path, csv_path, png_path, hitmap_path, bundle_spec, panel_info
130
+
131
+
132
+ def _launch_backend(
133
+ backend: str,
134
+ json_path: Path,
135
+ metadata: dict,
136
+ csv_data,
137
+ png_path,
138
+ hitmap_path,
139
+ manual_overrides,
140
+ bundle_spec,
141
+ panel_info,
142
+ port: int,
143
+ ) -> None:
144
+ """Launch the specified editor backend."""
145
+ if backend == "flask":
146
+ _launch_flask(json_path, metadata, csv_data, png_path, hitmap_path, manual_overrides, port, panel_info)
147
+ elif backend == "dearpygui":
148
+ _launch_dearpygui(json_path, metadata, csv_data, png_path, manual_overrides)
149
+ elif backend == "qt":
150
+ _launch_qt(json_path, metadata, csv_data, png_path, manual_overrides, hitmap_path, bundle_spec)
151
+ elif backend == "tkinter":
152
+ _launch_tkinter(json_path, metadata, csv_data, manual_overrides)
153
+ elif backend == "mpl":
154
+ _launch_mpl(json_path, metadata, csv_data, manual_overrides)
155
+ else:
156
+ raise ValueError(
157
+ f"Unknown backend: {backend}. "
158
+ "Use 'auto', 'flask', 'dearpygui', 'qt', 'tkinter', or 'mpl'."
159
+ )
160
+
161
+
162
+ def _launch_flask(json_path, metadata, csv_data, png_path, hitmap_path, manual_overrides, port, panel_info):
163
+ """Launch Flask web editor."""
164
+ try:
165
+ from .._flask_editor import WebEditor
166
+ editor = WebEditor(
167
+ json_path=json_path,
168
+ metadata=metadata,
169
+ csv_data=csv_data,
170
+ png_path=png_path,
171
+ hitmap_path=hitmap_path,
172
+ manual_overrides=manual_overrides,
173
+ port=port,
174
+ panel_info=panel_info,
175
+ )
176
+ editor.run()
177
+ except ImportError as e:
178
+ raise ImportError("Flask backend requires Flask. Install with: pip install flask") from e
179
+
180
+
181
+ def _launch_dearpygui(json_path, metadata, csv_data, png_path, manual_overrides):
182
+ """Launch DearPyGui editor."""
183
+ try:
184
+ from .._dearpygui_editor import DearPyGuiEditor
185
+ editor = DearPyGuiEditor(
186
+ json_path=json_path,
187
+ metadata=metadata,
188
+ csv_data=csv_data,
189
+ png_path=png_path,
190
+ manual_overrides=manual_overrides,
191
+ )
192
+ editor.run()
193
+ except ImportError as e:
194
+ raise ImportError("DearPyGui backend requires dearpygui. Install with: pip install dearpygui") from e
195
+
196
+
197
+ def _launch_qt(json_path, metadata, csv_data, png_path, manual_overrides, hitmap_path, bundle_spec):
198
+ """Launch Qt editor."""
199
+ try:
200
+ from .._qt_editor import QtEditor
201
+ editor = QtEditor(
202
+ json_path=json_path,
203
+ metadata=metadata,
204
+ csv_data=csv_data,
205
+ png_path=png_path,
206
+ manual_overrides=manual_overrides,
207
+ hitmap_path=hitmap_path,
208
+ bundle_spec=bundle_spec,
209
+ )
210
+ editor.run()
211
+ except ImportError as e:
212
+ raise ImportError("Qt backend requires PyQt5/PyQt6 or PySide2/PySide6. Install with: pip install PyQt6") from e
213
+
214
+
215
+ def _launch_tkinter(json_path, metadata, csv_data, manual_overrides):
216
+ """Launch Tkinter editor."""
217
+ from .._tkinter_editor import TkinterEditor
218
+ editor = TkinterEditor(
219
+ json_path=json_path,
220
+ metadata=metadata,
221
+ csv_data=csv_data,
222
+ manual_overrides=manual_overrides,
223
+ )
224
+ editor.run()
225
+
226
+
227
+ def _launch_mpl(json_path, metadata, csv_data, manual_overrides):
228
+ """Launch matplotlib editor."""
229
+ from .._mpl_editor import MplEditor
230
+ editor = MplEditor(
231
+ json_path=json_path,
232
+ metadata=metadata,
233
+ csv_data=csv_data,
234
+ manual_overrides=manual_overrides,
235
+ )
236
+ editor.run()
237
+
238
+
239
+ # EOF
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-14 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fig/editor/edit/manual_handler.py
5
+
6
+ """Manual override handling for figure editor."""
7
+
8
+ import hashlib
9
+ from pathlib import Path
10
+
11
+ __all__ = ["compute_file_hash", "save_manual_overrides"]
12
+
13
+
14
+ def compute_file_hash(path: Path) -> str:
15
+ """Compute SHA256 hash of file contents."""
16
+ with open(path, "rb") as f:
17
+ return hashlib.sha256(f.read()).hexdigest()
18
+
19
+
20
+ def save_manual_overrides(json_path: Path, overrides: dict) -> Path:
21
+ """
22
+ Save manual overrides to .manual.json file.
23
+
24
+ Parameters
25
+ ----------
26
+ json_path : Path
27
+ Path to base JSON file
28
+ overrides : dict
29
+ Override settings (styles, annotations, etc.)
30
+
31
+ Returns
32
+ -------
33
+ Path
34
+ Path to saved manual.json file
35
+ """
36
+ import scitex as stx
37
+
38
+ manual_path = json_path.with_suffix(".manual.json")
39
+
40
+ # Compute hash of base JSON for staleness detection
41
+ base_hash = compute_file_hash(json_path)
42
+
43
+ manual_data = {
44
+ "base_file": json_path.name,
45
+ "base_hash": base_hash,
46
+ "overrides": overrides,
47
+ }
48
+
49
+ stx.io.save(manual_data, manual_path)
50
+ return manual_path
51
+
52
+
53
+ # EOF
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-14 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fig/editor/edit/panel_loader.py
5
+
6
+ """Panel data loading for figure editor."""
7
+
8
+ import io
9
+ import json as json_module
10
+ from pathlib import Path
11
+ from typing import Optional, Union, Dict, Any
12
+
13
+ __all__ = ["load_panel_data"]
14
+
15
+
16
+ def load_panel_data(panel_path: Union[Path, str], is_zip: bool = None) -> Optional[Dict[str, Any]]:
17
+ """
18
+ Load panel data from either a .pltz.d directory or a .pltz zip file.
19
+
20
+ Handles both formats transparently using in-memory reading for zips.
21
+
22
+ Parameters
23
+ ----------
24
+ panel_path : Path or str
25
+ Path to .pltz.d directory or .pltz zip file
26
+ is_zip : bool, optional
27
+ If True, treat as zip file. If False, treat as directory.
28
+ If None, auto-detect based on path suffix and existence.
29
+
30
+ Returns
31
+ -------
32
+ dict or None
33
+ Dictionary with keys: metadata, csv_data, png_bytes, hitmap_bytes, img_size
34
+ For directories, also includes: json_path, png_path, hitmap_path
35
+ Returns None if panel cannot be loaded
36
+ """
37
+ from PIL import Image
38
+
39
+ panel_path = Path(panel_path)
40
+
41
+ # Auto-detect if not specified
42
+ if is_zip is None:
43
+ spath = str(panel_path)
44
+ if spath.endswith('.pltz') and not spath.endswith('.pltz.d'):
45
+ is_zip = panel_path.is_file()
46
+ else:
47
+ is_zip = False
48
+
49
+ if is_zip:
50
+ return _load_from_zip(panel_path)
51
+ else:
52
+ return _load_from_directory(panel_path)
53
+
54
+
55
+ def _load_from_zip(panel_path: Path) -> Optional[Dict[str, Any]]:
56
+ """Load panel data from a .pltz zip file."""
57
+ from PIL import Image
58
+ from scitex.io._zip_bundle import ZipBundle
59
+
60
+ if not panel_path.exists():
61
+ return None
62
+
63
+ try:
64
+ with ZipBundle(panel_path, mode="r") as zb:
65
+ # Load spec.json for metadata
66
+ try:
67
+ metadata = zb.read_json("spec.json")
68
+ except FileNotFoundError:
69
+ metadata = {}
70
+
71
+ # Load style.json if exists
72
+ try:
73
+ style = zb.read_json("style.json")
74
+ metadata["style"] = style
75
+ except FileNotFoundError:
76
+ pass
77
+
78
+ # Find and read PNG
79
+ png_bytes = None
80
+ for name in zb.namelist():
81
+ if name.endswith('.png') and '_hitmap' not in name and '_overview' not in name:
82
+ if 'exports/' in name:
83
+ png_bytes = zb.read_bytes(name)
84
+ break
85
+
86
+ # If no PNG in exports/, try root level
87
+ if not png_bytes:
88
+ for name in zb.namelist():
89
+ if name.endswith('.png') and '_hitmap' not in name and '_overview' not in name:
90
+ png_bytes = zb.read_bytes(name)
91
+ break
92
+
93
+ # Get image size
94
+ img_size = None
95
+ if png_bytes:
96
+ img = Image.open(io.BytesIO(png_bytes))
97
+ img_size = {"width": img.size[0], "height": img.size[1]}
98
+ img.close()
99
+
100
+ # Find and read hitmap
101
+ hitmap_bytes = None
102
+ for name in zb.namelist():
103
+ if '_hitmap.png' in name:
104
+ hitmap_bytes = zb.read_bytes(name)
105
+ break
106
+
107
+ # Load geometry_px.json if available
108
+ geometry_data = None
109
+ try:
110
+ geometry_data = zb.read_json("cache/geometry_px.json")
111
+ except FileNotFoundError:
112
+ pass
113
+
114
+ return {
115
+ "metadata": metadata,
116
+ "png_bytes": png_bytes,
117
+ "hitmap_bytes": hitmap_bytes,
118
+ "img_size": img_size,
119
+ "geometry_data": geometry_data,
120
+ "is_zip": True,
121
+ }
122
+ except Exception as e:
123
+ print(f"Error loading panel zip {panel_path}: {e}")
124
+ return None
125
+
126
+
127
+ def _load_from_directory(panel_path: Path) -> Optional[Dict[str, Any]]:
128
+ """Load panel data from a .pltz.d directory."""
129
+ import scitex as stx
130
+
131
+ panel_dir = panel_path
132
+ if not panel_dir.exists():
133
+ return None
134
+
135
+ # Check for layered vs legacy format
136
+ spec_path = panel_dir / "spec.json"
137
+ if spec_path.exists():
138
+ return _load_layered_directory(panel_dir)
139
+ else:
140
+ return _load_legacy_directory(panel_dir)
141
+
142
+
143
+ def _load_layered_directory(panel_dir: Path) -> Dict[str, Any]:
144
+ """Load panel data from layered format directory."""
145
+ import scitex as stx
146
+ from scitex.plt.io import load_layered_pltz_bundle
147
+
148
+ bundle_data = load_layered_pltz_bundle(panel_dir)
149
+ metadata = bundle_data.get("merged", {})
150
+
151
+ # Find CSV
152
+ csv_data = None
153
+ for f in panel_dir.glob("*.csv"):
154
+ csv_data = stx.io.load(f)
155
+ break
156
+
157
+ # Find exports - prefer PNG over SVG (PIL can't open SVG)
158
+ png_path = None
159
+ svg_path = None
160
+ hitmap_path = None
161
+ exports_dir = panel_dir / "exports"
162
+ if exports_dir.exists():
163
+ for f in exports_dir.iterdir():
164
+ name = f.name
165
+ if name.endswith('_hitmap.png'):
166
+ hitmap_path = f
167
+ elif name.endswith('.png') and '_hitmap' not in name and '_overview' not in name:
168
+ png_path = f
169
+ elif name.endswith('.svg') and '_hitmap' not in name and svg_path is None:
170
+ svg_path = f
171
+
172
+ if png_path is None:
173
+ png_path = svg_path
174
+
175
+ # Load geometry_px.json if available
176
+ geometry_data = None
177
+ geometry_path = panel_dir / "cache" / "geometry_px.json"
178
+ if geometry_path.exists():
179
+ with open(geometry_path) as f:
180
+ geometry_data = json_module.load(f)
181
+
182
+ return {
183
+ "json_path": panel_dir / "spec.json",
184
+ "metadata": metadata,
185
+ "csv_data": csv_data,
186
+ "png_path": png_path,
187
+ "hitmap_path": hitmap_path,
188
+ "geometry_data": geometry_data,
189
+ "is_zip": False,
190
+ }
191
+
192
+
193
+ def _load_legacy_directory(panel_dir: Path) -> Optional[Dict[str, Any]]:
194
+ """Load panel data from legacy format directory."""
195
+ import scitex as stx
196
+
197
+ json_path = None
198
+ csv_data = None
199
+ png_path = None
200
+ hitmap_path = None
201
+
202
+ for f in panel_dir.iterdir():
203
+ name = f.name
204
+ if name.endswith('.json') and not name.endswith('.manual.json'):
205
+ json_path = f
206
+ elif name.endswith('.csv'):
207
+ csv_data = stx.io.load(f)
208
+ elif name.endswith('_hitmap.png'):
209
+ hitmap_path = f
210
+ elif name.endswith('.svg') and '_hitmap' not in name:
211
+ png_path = f
212
+ elif name.endswith('.png') and '_hitmap' not in name and '_overview' not in name:
213
+ if png_path is None:
214
+ png_path = f
215
+
216
+ if json_path is None:
217
+ return None
218
+
219
+ with open(json_path, 'r') as f:
220
+ metadata = json_module.load(f)
221
+
222
+ return {
223
+ "json_path": json_path,
224
+ "metadata": metadata,
225
+ "csv_data": csv_data,
226
+ "png_path": png_path,
227
+ "hitmap_path": hitmap_path,
228
+ "is_zip": False,
229
+ }
230
+
231
+
232
+ # EOF
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-14 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fig/editor/edit/path_resolver.py
5
+
6
+ """Basic path resolution for figure files."""
7
+
8
+ from pathlib import Path
9
+ from typing import Optional, Tuple
10
+
11
+ __all__ = ["resolve_figure_paths"]
12
+
13
+
14
+ def resolve_figure_paths(path: Path) -> Tuple[Path, Optional[Path], Optional[Path]]:
15
+ """
16
+ Resolve JSON, CSV, and PNG paths from any input file path.
17
+
18
+ Handles two patterns:
19
+ 1. Flat (sibling): path/to/figure.{json,csv,png}
20
+ 2. Organized (subdirs): path/to/{json,csv,png}/figure.{ext}
21
+
22
+ Parameters
23
+ ----------
24
+ path : Path
25
+ Input path (can be JSON, CSV, or PNG)
26
+
27
+ Returns
28
+ -------
29
+ tuple
30
+ (json_path, csv_path, png_path) - csv_path/png_path may be None if not found
31
+ """
32
+ path = Path(path)
33
+ stem = path.stem
34
+ parent = path.parent
35
+
36
+ # Check if this is organized pattern (parent is json/, csv/, png/)
37
+ if parent.name in ("json", "csv", "png"):
38
+ base_dir = parent.parent
39
+ json_path = base_dir / "json" / f"{stem}.json"
40
+ csv_path = base_dir / "csv" / f"{stem}.csv"
41
+ png_path = base_dir / "png" / f"{stem}.png"
42
+ else:
43
+ # Flat pattern - sibling files
44
+ json_path = parent / f"{stem}.json"
45
+ csv_path = parent / f"{stem}.csv"
46
+ png_path = parent / f"{stem}.png"
47
+
48
+ # If input was .manual.json, get base json
49
+ if stem.endswith(".manual"):
50
+ base_stem = stem[:-7] # Remove '.manual'
51
+ if parent.name == "json":
52
+ json_path = parent / f"{base_stem}.json"
53
+ csv_path = parent.parent / "csv" / f"{base_stem}.csv"
54
+ png_path = parent.parent / "png" / f"{base_stem}.png"
55
+ else:
56
+ json_path = parent / f"{base_stem}.json"
57
+ csv_path = parent / f"{base_stem}.csv"
58
+ png_path = parent / f"{base_stem}.png"
59
+
60
+ return (
61
+ json_path,
62
+ csv_path if csv_path.exists() else None,
63
+ png_path if png_path.exists() else None,
64
+ )
65
+
66
+
67
+ # EOF