scitex 2.7.3__py3-none-any.whl → 2.10.0__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 (563) hide show
  1. scitex/__init__.py +15 -7
  2. scitex/__version__.py +1 -2
  3. scitex/_install_guide.py +250 -0
  4. scitex/_optional_deps.py +206 -39
  5. scitex/ai/_gen_ai/_Groq.py +2 -4
  6. scitex/ai/_gen_ai/_OpenAI.py +5 -2
  7. scitex/ai/_gen_ai/_Perplexity.py +20 -6
  8. scitex/audio/__init__.py +24 -15
  9. scitex/audio/_cross_process_lock.py +139 -0
  10. scitex/audio/_mcp_handlers.py +256 -0
  11. scitex/audio/_mcp_tool_schemas.py +203 -0
  12. scitex/audio/engines/elevenlabs_engine.py +5 -2
  13. scitex/audio/mcp_server.py +98 -457
  14. scitex/bridge/__init__.py +30 -19
  15. scitex/bridge/_figrecipe.py +245 -0
  16. scitex/bridge/_helpers.py +2 -1
  17. scitex/bridge/_plt_vis.py +23 -10
  18. scitex/bridge/_stats_plt.py +18 -5
  19. scitex/bridge/_stats_vis.py +16 -2
  20. scitex/browser/__init__.py +84 -44
  21. scitex/browser/automation/__init__.py +5 -1
  22. scitex/browser/core/BrowserMixin.py +17 -4
  23. scitex/browser/core/__init__.py +11 -2
  24. scitex/browser/remote/CaptchaHandler.py +1 -1
  25. scitex/browser/remote/ZenRowsAPIClient.py +1 -1
  26. scitex/capture/grid.py +487 -0
  27. scitex/capture/mcp_handlers.py +401 -0
  28. scitex/capture/mcp_tool_defs.py +192 -0
  29. scitex/capture/mcp_tools.py +241 -0
  30. scitex/capture/mcp_utils.py +30 -0
  31. scitex/cli/convert.py +421 -0
  32. scitex/cli/main.py +6 -4
  33. scitex/datetime/__init__.py +46 -0
  34. scitex/datetime/_linspace.py +100 -0
  35. scitex/datetime/_normalize_timestamp.py +306 -0
  36. scitex/db/_delete_duplicates.py +4 -4
  37. scitex/db/_sqlite3/_delete_duplicates.py +11 -2
  38. scitex/dev/plt/__init__.py +61 -62
  39. scitex/dev/plt/demo_plotters/__init__.py +0 -0
  40. scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
  41. scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
  42. scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
  43. scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
  44. scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
  45. scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
  46. scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
  47. scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
  48. scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
  49. scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
  50. scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
  51. scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
  52. scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
  53. scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
  54. scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
  55. scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
  56. scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
  57. scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
  58. scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
  59. scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
  60. scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
  61. scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
  62. scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
  63. scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
  64. scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
  65. scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
  66. scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
  67. scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
  68. scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
  69. scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
  70. scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
  71. scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
  72. scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
  73. scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
  74. scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
  75. scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
  76. scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
  77. scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
  78. scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
  79. scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
  80. scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
  81. scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
  82. scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
  83. scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
  84. scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
  85. scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
  86. scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
  87. scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
  88. scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
  89. scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
  90. scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
  91. scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
  92. scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
  93. scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
  94. scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
  95. scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
  96. scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
  97. scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
  98. scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
  99. scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
  100. scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
  101. scitex/dev/plt/mpl/get_dir_ax.py +46 -0
  102. scitex/dev/plt/mpl/get_signatures.py +176 -0
  103. scitex/dev/plt/mpl/get_signatures_details.py +522 -0
  104. scitex/dev/plt/plot_mpl_axhline.py +0 -0
  105. scitex/dev/plt/plot_mpl_axhspan.py +0 -0
  106. scitex/dev/plt/plot_mpl_axvline.py +0 -0
  107. scitex/dev/plt/plot_mpl_axvspan.py +0 -0
  108. scitex/dev/plt/plot_mpl_bar.py +0 -0
  109. scitex/dev/plt/plot_mpl_barh.py +0 -0
  110. scitex/dev/plt/plot_mpl_boxplot.py +0 -0
  111. scitex/dev/plt/plot_mpl_contour.py +0 -0
  112. scitex/dev/plt/plot_mpl_contourf.py +0 -0
  113. scitex/dev/plt/plot_mpl_errorbar.py +0 -0
  114. scitex/dev/plt/plot_mpl_eventplot.py +0 -0
  115. scitex/dev/plt/plot_mpl_fill.py +0 -0
  116. scitex/dev/plt/plot_mpl_fill_between.py +0 -0
  117. scitex/dev/plt/plot_mpl_hexbin.py +0 -0
  118. scitex/dev/plt/plot_mpl_hist.py +0 -0
  119. scitex/dev/plt/plot_mpl_hist2d.py +0 -0
  120. scitex/dev/plt/plot_mpl_imshow.py +0 -0
  121. scitex/dev/plt/plot_mpl_pcolormesh.py +0 -0
  122. scitex/dev/plt/plot_mpl_pie.py +0 -0
  123. scitex/dev/plt/plot_mpl_plot.py +0 -0
  124. scitex/dev/plt/plot_mpl_quiver.py +0 -0
  125. scitex/dev/plt/plot_mpl_scatter.py +0 -0
  126. scitex/dev/plt/plot_mpl_stackplot.py +0 -0
  127. scitex/dev/plt/plot_mpl_stem.py +0 -0
  128. scitex/dev/plt/plot_mpl_step.py +0 -0
  129. scitex/dev/plt/plot_mpl_violinplot.py +0 -0
  130. scitex/dev/plt/plot_sns_barplot.py +0 -0
  131. scitex/dev/plt/plot_sns_boxplot.py +0 -0
  132. scitex/dev/plt/plot_sns_heatmap.py +0 -0
  133. scitex/dev/plt/plot_sns_histplot.py +0 -0
  134. scitex/dev/plt/plot_sns_kdeplot.py +0 -0
  135. scitex/dev/plt/plot_sns_lineplot.py +0 -0
  136. scitex/dev/plt/plot_sns_scatterplot.py +0 -0
  137. scitex/dev/plt/plot_sns_stripplot.py +0 -0
  138. scitex/dev/plt/plot_sns_swarmplot.py +0 -0
  139. scitex/dev/plt/plot_sns_violinplot.py +0 -0
  140. scitex/dev/plt/plot_stx_bar.py +0 -0
  141. scitex/dev/plt/plot_stx_barh.py +0 -0
  142. scitex/dev/plt/plot_stx_box.py +0 -0
  143. scitex/dev/plt/plot_stx_boxplot.py +0 -0
  144. scitex/dev/plt/plot_stx_conf_mat.py +0 -0
  145. scitex/dev/plt/plot_stx_contour.py +0 -0
  146. scitex/dev/plt/plot_stx_ecdf.py +0 -0
  147. scitex/dev/plt/plot_stx_errorbar.py +0 -0
  148. scitex/dev/plt/plot_stx_fill_between.py +0 -0
  149. scitex/dev/plt/plot_stx_fillv.py +0 -0
  150. scitex/dev/plt/plot_stx_heatmap.py +0 -0
  151. scitex/dev/plt/plot_stx_image.py +0 -0
  152. scitex/dev/plt/plot_stx_imshow.py +0 -0
  153. scitex/dev/plt/plot_stx_joyplot.py +0 -0
  154. scitex/dev/plt/plot_stx_kde.py +0 -0
  155. scitex/dev/plt/plot_stx_line.py +0 -0
  156. scitex/dev/plt/plot_stx_mean_ci.py +0 -0
  157. scitex/dev/plt/plot_stx_mean_std.py +0 -0
  158. scitex/dev/plt/plot_stx_median_iqr.py +0 -0
  159. scitex/dev/plt/plot_stx_raster.py +0 -0
  160. scitex/dev/plt/plot_stx_rectangle.py +0 -0
  161. scitex/dev/plt/plot_stx_scatter.py +0 -0
  162. scitex/dev/plt/plot_stx_shaded_line.py +0 -0
  163. scitex/dev/plt/plot_stx_violin.py +0 -0
  164. scitex/dev/plt/plot_stx_violinplot.py +0 -0
  165. scitex/diagram/README.md +197 -0
  166. scitex/diagram/__init__.py +48 -0
  167. scitex/diagram/_compile.py +312 -0
  168. scitex/diagram/_diagram.py +355 -0
  169. scitex/diagram/_presets.py +173 -0
  170. scitex/diagram/_schema.py +182 -0
  171. scitex/diagram/_split.py +278 -0
  172. scitex/dict/_pop_keys.py +1 -7
  173. scitex/dsp/__init__.py +15 -10
  174. scitex/dsp/add_noise.py +5 -2
  175. scitex/dsp/example.py +35 -22
  176. scitex/dsp/filt.py +8 -3
  177. scitex/dsp/reference.py +3 -2
  178. scitex/dsp/utils/__init__.py +2 -1
  179. scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
  180. scitex/dt/__init__.py +39 -2
  181. scitex/errors.py +82 -521
  182. scitex/fig/__init__.py +4 -4
  183. scitex/fig/editor/__init__.py +5 -2
  184. scitex/fig/editor/_dearpygui_editor.py +1 -1
  185. scitex/fig/editor/_mpl_editor.py +1 -1
  186. scitex/fig/editor/_qt_editor.py +1 -1
  187. scitex/fig/editor/_tkinter_editor.py +1 -1
  188. scitex/fig/editor/edit/__init__.py +50 -0
  189. scitex/fig/editor/edit/backend_detector.py +109 -0
  190. scitex/fig/editor/edit/bundle_resolver.py +240 -0
  191. scitex/fig/editor/edit/editor_launcher.py +239 -0
  192. scitex/fig/editor/edit/manual_handler.py +53 -0
  193. scitex/fig/editor/edit/panel_loader.py +232 -0
  194. scitex/fig/editor/edit/path_resolver.py +67 -0
  195. scitex/fig/editor/flask_editor/_bbox.py +23 -0
  196. scitex/fig/editor/flask_editor/_core.py +908 -103
  197. scitex/fig/editor/flask_editor/_renderer.py +74 -0
  198. scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
  199. scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
  200. scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
  201. scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
  202. scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
  203. scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
  204. scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
  205. scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
  206. scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
  207. scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
  208. scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
  209. scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
  210. scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
  211. scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
  212. scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
  213. scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
  214. scitex/fig/editor/flask_editor/static/css/index.css +31 -0
  215. scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
  216. scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
  217. scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
  218. scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
  219. scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
  220. scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
  221. scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
  222. scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
  223. scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
  224. scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
  225. scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
  226. scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
  227. scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
  228. scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
  229. scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
  230. scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
  231. scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
  232. scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
  233. scitex/fig/editor/flask_editor/static/js/main.js +426 -0
  234. scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
  235. scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
  236. scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
  237. scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
  238. scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
  239. scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
  240. scitex/fig/editor/flask_editor/templates/__init__.py +95 -5
  241. scitex/fig/editor/flask_editor/templates/_html.py +27 -9
  242. scitex/fig/editor/flask_editor/templates/_scripts.py +1928 -131
  243. scitex/fig/editor/flask_editor/templates/_styles.py +363 -51
  244. scitex/fig/io/_bundle.py +104 -19
  245. scitex/fts/README.md +262 -0
  246. scitex/fts/TODO.md +66 -0
  247. scitex/fts/__init__.py +90 -0
  248. scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
  249. scitex/fts/_bundle/_FTS.py +657 -0
  250. scitex/fts/_bundle/__init__.py +38 -0
  251. scitex/fts/_bundle/_children.py +216 -0
  252. scitex/fts/_bundle/_conversion/__init__.py +15 -0
  253. scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
  254. scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
  255. scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
  256. scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
  257. scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
  258. scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
  259. scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
  260. scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
  261. scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
  262. scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
  263. scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
  264. scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
  265. scitex/fts/_bundle/_extractors/__init__.py +32 -0
  266. scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
  267. scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
  268. scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
  269. scitex/fts/_bundle/_loader.py +134 -0
  270. scitex/fts/_bundle/_mpl_helpers.py +389 -0
  271. scitex/fts/_bundle/_saver.py +269 -0
  272. scitex/fts/_bundle/_storage.py +200 -0
  273. scitex/fts/_bundle/_utils/__init__.py +55 -0
  274. scitex/fts/_bundle/_utils/_const.py +26 -0
  275. scitex/fts/_bundle/_utils/_errors.py +73 -0
  276. scitex/fts/_bundle/_utils/_generate.py +21 -0
  277. scitex/fts/_bundle/_utils/_types.py +76 -0
  278. scitex/fts/_bundle/_validation.py +434 -0
  279. scitex/fts/_bundle/_zipbundle.py +165 -0
  280. scitex/fts/_fig/__init__.py +22 -0
  281. scitex/fts/_fig/_backend/__init__.py +53 -0
  282. scitex/fts/_fig/_backend/_export.py +165 -0
  283. scitex/fts/_fig/_backend/_parser.py +188 -0
  284. scitex/fts/_fig/_backend/_render.py +538 -0
  285. scitex/fts/_fig/_composite.py +345 -0
  286. scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
  287. scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
  288. scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
  289. scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
  290. scitex/fts/_fig/_dataclasses/__init__.py +47 -0
  291. scitex/fts/_fig/_editor/__init__.py +14 -0
  292. scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
  293. scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
  294. scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
  295. scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
  296. scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
  297. scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
  298. scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
  299. scitex/fts/_fig/_editor/_defaults.py +300 -0
  300. scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
  301. scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
  302. scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
  303. scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
  304. scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
  305. scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
  306. scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
  307. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
  308. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
  309. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
  310. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
  311. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
  312. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
  313. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
  314. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
  315. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
  316. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
  317. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
  318. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
  319. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
  320. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
  321. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
  322. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
  323. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
  324. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
  325. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
  326. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
  327. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
  328. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
  329. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
  330. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
  331. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
  332. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
  333. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
  334. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
  335. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
  336. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
  337. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
  338. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
  339. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
  340. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
  341. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
  342. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
  343. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
  344. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
  345. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
  346. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
  347. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
  348. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
  349. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
  350. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
  351. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
  352. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
  353. scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
  354. scitex/fts/_fig/_models/_Annotations.py +115 -0
  355. scitex/fts/_fig/_models/_Axes.py +152 -0
  356. scitex/fts/_fig/_models/_Figure.py +138 -0
  357. scitex/fts/_fig/_models/_Guides.py +104 -0
  358. scitex/fts/_fig/_models/_Plot.py +123 -0
  359. scitex/fts/_fig/_models/_Styles.py +245 -0
  360. scitex/fts/_fig/_models/__init__.py +80 -0
  361. scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
  362. scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
  363. scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
  364. scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
  365. scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
  366. scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
  367. scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
  368. scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
  369. scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
  370. scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
  371. scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
  372. scitex/fts/_fig/_utils/__init__.py +129 -0
  373. scitex/fts/_fig/_utils/_auto_layout.py +127 -0
  374. scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
  375. scitex/fts/_fig/_utils/_const_sizes.py +48 -0
  376. scitex/fts/_fig/_utils/_convert_coords.py +77 -0
  377. scitex/fts/_fig/_utils/_get_template.py +178 -0
  378. scitex/fts/_fig/_utils/_normalize.py +73 -0
  379. scitex/fts/_fig/_utils/_plot_layout.py +397 -0
  380. scitex/fts/_fig/_utils/_validate.py +197 -0
  381. scitex/fts/_kinds/__init__.py +45 -0
  382. scitex/fts/_kinds/_figure/__init__.py +19 -0
  383. scitex/fts/_kinds/_figure/_composite.py +345 -0
  384. scitex/fts/_kinds/_plot/__init__.py +25 -0
  385. scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
  386. scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
  387. scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
  388. scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
  389. scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
  390. scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
  391. scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
  392. scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
  393. scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
  394. scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
  395. scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
  396. scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
  397. scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
  398. scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
  399. scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
  400. scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
  401. scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
  402. scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
  403. scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
  404. scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
  405. scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
  406. scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
  407. scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
  408. scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
  409. scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
  410. scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
  411. scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
  412. scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
  413. scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
  414. scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
  415. scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
  416. scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
  417. scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
  418. scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
  419. scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
  420. scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
  421. scitex/fts/_kinds/_shape/__init__.py +141 -0
  422. scitex/fts/_kinds/_stats/__init__.py +56 -0
  423. scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
  424. scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
  425. scitex/fts/_kinds/_table/__init__.py +72 -0
  426. scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
  427. scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
  428. scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
  429. scitex/fts/_kinds/_table/_latex/_export.py +279 -0
  430. scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
  431. scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
  432. scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
  433. scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
  434. scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
  435. scitex/fts/_kinds/_text/__init__.py +77 -0
  436. scitex/fts/_schemas/data_info.schema.json +75 -0
  437. scitex/fts/_schemas/encoding.schema.json +90 -0
  438. scitex/fts/_schemas/node.schema.json +145 -0
  439. scitex/fts/_schemas/render_manifest.schema.json +62 -0
  440. scitex/fts/_schemas/stats.schema.json +132 -0
  441. scitex/fts/_schemas/theme.schema.json +141 -0
  442. scitex/fts/_stats/__init__.py +48 -0
  443. scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
  444. scitex/fts/_stats/_dataclasses/__init__.py +48 -0
  445. scitex/fts/_tables/__init__.py +65 -0
  446. scitex/fts/_tables/_latex/__init__.py +93 -0
  447. scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
  448. scitex/fts/_tables/_latex/_editor/_app.py +725 -0
  449. scitex/fts/_tables/_latex/_export.py +279 -0
  450. scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
  451. scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
  452. scitex/fts/_tables/_latex/_table_exporter.py +362 -0
  453. scitex/fts/_tables/_latex/_utils.py +369 -0
  454. scitex/fts/_tables/_latex/_validator.py +445 -0
  455. scitex/gen/__init__.py +66 -25
  456. scitex/gen/misc.py +28 -0
  457. scitex/io/__init__.py +47 -20
  458. scitex/io/_load.py +87 -36
  459. scitex/io/_load_modules/__init__.py +10 -7
  460. scitex/io/_load_modules/_pandas.py +6 -1
  461. scitex/io/_save.py +299 -1556
  462. scitex/io/_save_modules/__init__.py +76 -19
  463. scitex/io/_save_modules/_figure_utils.py +90 -0
  464. scitex/io/_save_modules/_image_csv.py +497 -0
  465. scitex/io/_save_modules/_legends.py +91 -0
  466. scitex/io/_save_modules/_pltz_bundle.py +356 -0
  467. scitex/io/_save_modules/_pltz_stx.py +536 -0
  468. scitex/io/_save_modules/_stx_bundle.py +104 -0
  469. scitex/io/_save_modules/_symlink.py +96 -0
  470. scitex/io/_save_modules/_yaml.py +1 -1
  471. scitex/io/_save_modules/_zarr.py +64 -18
  472. scitex/io/bundle/README.md +212 -0
  473. scitex/io/bundle/__init__.py +110 -0
  474. scitex/io/{_bundle.py → bundle/_core.py} +219 -89
  475. scitex/io/bundle/_nested.py +713 -0
  476. scitex/io/bundle/_types.py +74 -0
  477. scitex/io/bundle/_zip.py +487 -0
  478. scitex/io/utils/h5_to_zarr.py +1 -1
  479. scitex/logging/__init__.py +108 -13
  480. scitex/logging/_errors.py +508 -0
  481. scitex/logging/_formatters.py +30 -6
  482. scitex/logging/_warnings.py +261 -0
  483. scitex/plt/__init__.py +4 -1
  484. scitex/plt/_figrecipe.py +236 -0
  485. scitex/plt/_subplots/_AxisWrapper.py +6 -0
  486. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +0 -0
  487. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +0 -0
  488. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +0 -0
  489. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +0 -0
  490. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +0 -0
  491. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +0 -0
  492. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +0 -0
  493. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +0 -0
  494. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +0 -0
  495. scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +0 -0
  496. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +0 -0
  497. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +0 -0
  498. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +0 -0
  499. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
  500. scitex/plt/_subplots/_FigWrapper.py +15 -0
  501. scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
  502. scitex/plt/_subplots/_export_as_csv.py +11 -0
  503. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
  504. scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
  505. scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
  506. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +0 -0
  507. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +0 -0
  508. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +0 -0
  509. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +0 -0
  510. scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
  511. scitex/plt/_subplots/_fonts.py +71 -0
  512. scitex/plt/_subplots/_mm_layout.py +282 -0
  513. scitex/plt/gallery/__init__.py +99 -2
  514. scitex/plt/io/_layered_bundle.py +0 -0
  515. scitex/plt/styles/_plot_postprocess.py +3 -1
  516. scitex/plt/utils/_configure_mpl.py +16 -19
  517. scitex/repro/_RandomStateManager.py +13 -8
  518. scitex/resource/__init__.py +19 -1
  519. scitex/resource/_utils/_get_env_info.py +13 -25
  520. scitex/schema/__init__.py +149 -160
  521. scitex/schema/_encoding.py +273 -0
  522. scitex/schema/_figure_elements.py +406 -0
  523. scitex/schema/_plot.py +0 -0
  524. scitex/schema/_theme.py +360 -0
  525. scitex/schema/_validation.py +0 -98
  526. scitex/scholar/__init__.py +56 -14
  527. scitex/scholar/auth/ScholarAuthManager.py +1 -1
  528. scitex/scholar/auth/__init__.py +11 -2
  529. scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
  530. scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
  531. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
  532. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
  533. scitex/scholar/config/ScholarConfig.py +1 -1
  534. scitex/scholar/core/Scholar.py +1 -1
  535. scitex/session/_decorator.py +18 -16
  536. scitex/session/_lifecycle.py +9 -11
  537. scitex/session/template.py +9 -8
  538. scitex/sh/test_sh.py +72 -0
  539. scitex/sh/test_sh_simple.py +61 -0
  540. scitex/stats/__init__.py +221 -97
  541. scitex/stats/_schema.py +21 -22
  542. scitex/stats/descriptive/_circular.py +212 -351
  543. scitex/stats/descriptive/_describe.py +81 -132
  544. scitex/stats/descriptive/_nan.py +205 -433
  545. scitex/stats/descriptive/_real.py +127 -141
  546. scitex/str/_format_plot_text.py +5 -5
  547. scitex/str/_latex.py +26 -84
  548. scitex/str/_latex_fallback.py +53 -47
  549. scitex/web/_search_pubmed.py +5 -4
  550. scitex/writer/tests/test_diff_between.py +451 -0
  551. scitex/writer/tests/test_document_section.py +311 -0
  552. scitex/writer/tests/test_document_workflow.py +393 -0
  553. scitex/writer/tests/test_writer.py +361 -0
  554. scitex/writer/tests/test_writer_integration.py +303 -0
  555. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
  556. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/RECORD +479 -108
  557. scitex/fig/editor/_edit.py +0 -751
  558. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
  559. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
  560. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
  561. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
  562. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
  563. {scitex-2.7.3.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,536 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-19
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_save_modules/_pltz_stx.py
4
+
5
+ """Save matplotlib figures as FTS bundles (ZIP or directory) with plot content type."""
6
+
7
+ import json
8
+ import tempfile
9
+ import warnings
10
+ from datetime import datetime
11
+ from pathlib import Path
12
+
13
+ import numpy as np
14
+
15
+ from ._figure_utils import get_figure_with_data
16
+
17
+
18
+ def _create_stx_spec(bundle_type, title, size):
19
+ """Create a spec dictionary for .stx bundle.
20
+
21
+ Parameters
22
+ ----------
23
+ bundle_type : str
24
+ Type of bundle ('plot', 'figure', 'stats')
25
+ title : str
26
+ Title/name of the bundle
27
+ size : dict
28
+ Size info with width_mm, height_mm, dpi
29
+
30
+ Returns
31
+ -------
32
+ dict
33
+ Spec dictionary
34
+ """
35
+ return {
36
+ "schema": {"name": f"scitex.{bundle_type}", "version": "1.0.0"},
37
+ "id": f"{bundle_type}_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}",
38
+ "type": bundle_type,
39
+ "title": title,
40
+ "size": size,
41
+ "created_at": datetime.utcnow().isoformat() + "Z",
42
+ }
43
+
44
+
45
+ def _extract_data_from_figure(fig):
46
+ """Extract plotted data from matplotlib figure lines.
47
+
48
+ Returns DataFrame with x/y columns for each trace, or None if no data.
49
+ """
50
+ import pandas as pd
51
+
52
+ extracted_data = {}
53
+ axes_list = list(fig.axes) if hasattr(fig.axes, "__iter__") else [fig.axes]
54
+
55
+ for ax_idx, ax in enumerate(axes_list):
56
+ for line_idx, line in enumerate(ax.get_lines()):
57
+ label = line.get_label()
58
+ if label is None or label.startswith("_"):
59
+ label = f"series_{line_idx}"
60
+
61
+ xdata, ydata = line.get_data()
62
+ if len(xdata) > 0:
63
+ x_col = f"ax{ax_idx}_line{line_idx}_x"
64
+ y_col = f"ax{ax_idx}_line{line_idx}_y"
65
+ extracted_data[x_col] = np.array(xdata, dtype=float)
66
+ extracted_data[y_col] = np.array(ydata, dtype=float)
67
+
68
+ if not extracted_data:
69
+ return None
70
+
71
+ # Pad arrays to same length
72
+ max_len = max(len(v) for v in extracted_data.values())
73
+ padded = {}
74
+ for k, v in extracted_data.items():
75
+ if len(v) < max_len:
76
+ padded[k] = np.pad(v, (0, max_len - len(v)), constant_values=np.nan)
77
+ else:
78
+ padded[k] = v
79
+
80
+ return pd.DataFrame(padded)
81
+
82
+
83
+ def _build_encoding_from_figure(fig, csv_df):
84
+ """Build encoding.json data from matplotlib figure.
85
+
86
+ Encoding captures data→visual mappings for scientific reproducibility.
87
+ """
88
+ from scitex.schema import ENCODING_VERSION
89
+
90
+ traces = []
91
+ axes_list = list(fig.axes) if hasattr(fig.axes, "__iter__") else [fig.axes]
92
+
93
+ for ax_idx, ax in enumerate(axes_list):
94
+ for line_idx, line in enumerate(ax.get_lines()):
95
+ label = line.get_label()
96
+ if label is None or label.startswith("_"):
97
+ label = f"line_{line_idx}"
98
+
99
+ trace = {
100
+ "trace_id": f"ax{ax_idx}_line{line_idx}",
101
+ "bindings": [],
102
+ }
103
+
104
+ # X binding
105
+ if csv_df is not None:
106
+ x_col = f"ax{ax_idx}_line{line_idx}_x"
107
+ y_col = f"ax{ax_idx}_line{line_idx}_y"
108
+ if x_col in csv_df.columns:
109
+ trace["bindings"].append(
110
+ {
111
+ "channel": "x",
112
+ "column": x_col,
113
+ "scale": "linear",
114
+ }
115
+ )
116
+ if y_col in csv_df.columns:
117
+ trace["bindings"].append(
118
+ {
119
+ "channel": "y",
120
+ "column": y_col,
121
+ "scale": "linear",
122
+ }
123
+ )
124
+
125
+ if trace["bindings"]:
126
+ traces.append(trace)
127
+
128
+ return {
129
+ "schema": {"name": "scitex.plt.encoding", "version": ENCODING_VERSION},
130
+ "traces": traces,
131
+ }
132
+
133
+
134
+ def _build_theme_from_figure(fig):
135
+ """Build theme.json data from matplotlib figure.
136
+
137
+ Theme captures pure aesthetics without affecting scientific meaning.
138
+ """
139
+ from scitex.schema import THEME_VERSION
140
+
141
+ # Extract colors from figure
142
+ fig_facecolor = fig.get_facecolor()
143
+ axes_list = list(fig.axes) if hasattr(fig.axes, "__iter__") else [fig.axes]
144
+
145
+ ax_facecolor = "white"
146
+ if axes_list:
147
+ try:
148
+ ax_facecolor = axes_list[0].get_facecolor()
149
+ if isinstance(ax_facecolor, (tuple, list)):
150
+ ax_facecolor = f"#{int(ax_facecolor[0] * 255):02x}{int(ax_facecolor[1] * 255):02x}{int(ax_facecolor[2] * 255):02x}"
151
+ except Exception:
152
+ pass
153
+
154
+ # Extract trace styles
155
+ traces = []
156
+ for ax_idx, ax in enumerate(axes_list):
157
+ for line_idx, line in enumerate(ax.get_lines()):
158
+ trace_style = {
159
+ "trace_id": f"ax{ax_idx}_line{line_idx}",
160
+ }
161
+ try:
162
+ color = line.get_color()
163
+ if color:
164
+ trace_style["color"] = color
165
+ lw = line.get_linewidth()
166
+ if lw:
167
+ trace_style["linewidth"] = float(lw)
168
+ ls = line.get_linestyle()
169
+ if ls:
170
+ trace_style["linestyle"] = ls
171
+ marker = line.get_marker()
172
+ if marker and marker != "None":
173
+ trace_style["marker"] = marker
174
+ ms = line.get_markersize()
175
+ if ms:
176
+ trace_style["markersize"] = float(ms)
177
+ alpha = line.get_alpha()
178
+ if alpha is not None:
179
+ trace_style["alpha"] = float(alpha)
180
+ except Exception:
181
+ pass
182
+
183
+ if len(trace_style) > 1: # More than just trace_id
184
+ traces.append(trace_style)
185
+
186
+ return {
187
+ "schema": {"name": "scitex.plt.theme", "version": THEME_VERSION},
188
+ "colors": {
189
+ "mode": "light",
190
+ "background": "transparent",
191
+ "axes_bg": ax_facecolor if isinstance(ax_facecolor, str) else "white",
192
+ },
193
+ "typography": {
194
+ "family": "sans-serif",
195
+ "size_pt": 7.0,
196
+ },
197
+ "traces": traces,
198
+ "grid": False,
199
+ }
200
+
201
+
202
+ def save_pltz_as_stx(obj, spath, as_zip=True, basename=None, **kwargs):
203
+ """Save a matplotlib figure as an FTS bundle (ZIP or directory).
204
+
205
+ Bundle structure:
206
+ plot_name/ # or plot_name.zip (with plot_name/ inside)
207
+ spec.json # Bundle specification (WHAT to plot)
208
+ encoding.json # Data→visual mapping (scientific rigor)
209
+ theme.json # Pure aesthetics (colors, fonts)
210
+ style.json # Backward compat (encoding + theme merged)
211
+ data/
212
+ data.csv # Plotted data (tidy format)
213
+ data_info.json # Column meanings, units, dtypes
214
+ stats/
215
+ stats.json # Statistical test results (if any)
216
+ stats.csv # Tabular statistics
217
+ cache/
218
+ geometry_px.json # Hit areas for GUI editing
219
+ render_manifest.json # Render metadata
220
+ hitmap.png # Hit testing image
221
+ hitmap.svg # Vector hit testing
222
+ exports/
223
+ plot.svg # Vector export
224
+ plot.png # Raster export
225
+ plot.pdf # Publication export
226
+ """
227
+ import matplotlib.figure
228
+
229
+ p = Path(spath)
230
+
231
+ if basename is None:
232
+ basename = p.stem
233
+
234
+ # Extract figure
235
+ fig = obj
236
+ if hasattr(obj, "figure"):
237
+ fig = obj.figure
238
+ elif hasattr(obj, "fig"):
239
+ fig = obj.fig
240
+
241
+ if not isinstance(fig, matplotlib.figure.Figure):
242
+ raise TypeError(f"Expected matplotlib Figure, got {type(obj).__name__}")
243
+
244
+ dpi = kwargs.pop("dpi", 300)
245
+ data = kwargs.pop("data", None)
246
+
247
+ # Get CSV data from figure if not provided
248
+ csv_df = data
249
+ if csv_df is None:
250
+ # Try SciTeX wrapped objects first
251
+ csv_source = get_figure_with_data(obj)
252
+ if csv_source is not None and hasattr(csv_source, "export_as_csv"):
253
+ try:
254
+ csv_df = csv_source.export_as_csv()
255
+ except Exception:
256
+ pass
257
+ # Fall back to extracting from matplotlib lines
258
+ if csv_df is None:
259
+ csv_df = _extract_data_from_figure(fig)
260
+
261
+ # Create spec for .stx format
262
+ fig_width_inch, fig_height_inch = fig.get_size_inches()
263
+
264
+ spec = _create_stx_spec(
265
+ bundle_type="plot",
266
+ title=basename,
267
+ size={
268
+ "width_mm": round(fig_width_inch * 25.4, 2),
269
+ "height_mm": round(fig_height_inch * 25.4, 2),
270
+ "dpi": dpi,
271
+ },
272
+ )
273
+
274
+ # Determine paths
275
+ if as_zip:
276
+ zip_path = p
277
+ temp_dir = Path(tempfile.mkdtemp())
278
+ bundle_dir = temp_dir / basename
279
+ else:
280
+ bundle_dir = p
281
+ temp_dir = None
282
+
283
+ bundle_dir.mkdir(parents=True, exist_ok=True)
284
+
285
+ # Save spec.json
286
+ with open(bundle_dir / "spec.json", "w") as f:
287
+ json.dump(spec, f, indent=2, default=str)
288
+
289
+ # Save style.json (empty default for backward compatibility)
290
+ style_data = {}
291
+ with open(bundle_dir / "style.json", "w") as f:
292
+ json.dump(style_data, f, indent=2)
293
+
294
+ # Save encoding.json (data→visual mapping for scientific rigor)
295
+ encoding_data = _build_encoding_from_figure(fig, csv_df)
296
+ with open(bundle_dir / "encoding.json", "w") as f:
297
+ json.dump(encoding_data, f, indent=2)
298
+
299
+ # Save theme.json (pure aesthetics)
300
+ theme_data = _build_theme_from_figure(fig)
301
+ with open(bundle_dir / "theme.json", "w") as f:
302
+ json.dump(theme_data, f, indent=2)
303
+
304
+ # Create directory structure
305
+ data_dir = bundle_dir / "data"
306
+ stats_dir = bundle_dir / "stats"
307
+ cache_dir = bundle_dir / "cache"
308
+ exports_dir = bundle_dir / "exports"
309
+
310
+ data_dir.mkdir(exist_ok=True)
311
+ stats_dir.mkdir(exist_ok=True)
312
+ cache_dir.mkdir(exist_ok=True)
313
+ exports_dir.mkdir(exist_ok=True)
314
+
315
+ with warnings.catch_warnings():
316
+ warnings.filterwarnings("ignore", message=".*tight_layout.*")
317
+
318
+ # Save exports with simple names (bundle dir provides context)
319
+ fig.savefig(
320
+ exports_dir / "plot.png",
321
+ dpi=dpi,
322
+ bbox_inches="tight",
323
+ format="png",
324
+ transparent=True,
325
+ )
326
+
327
+ fig.savefig(
328
+ exports_dir / "plot.svg",
329
+ bbox_inches="tight",
330
+ format="svg",
331
+ )
332
+
333
+ fig.savefig(
334
+ exports_dir / "plot.pdf",
335
+ bbox_inches="tight",
336
+ format="pdf",
337
+ )
338
+
339
+ # Save data/data.csv and data/meta.json if available
340
+ if csv_df is not None:
341
+ csv_df.to_csv(data_dir / "data.csv", index=False)
342
+
343
+ # Generate metadata for the data
344
+ meta = {
345
+ "columns": list(csv_df.columns),
346
+ "dtypes": {col: str(dtype) for col, dtype in csv_df.dtypes.items()},
347
+ "shape": list(csv_df.shape),
348
+ "description": "Plotted data (tidy format)",
349
+ }
350
+ with open(data_dir / "data_info.json", "w") as f:
351
+ json.dump(meta, f, indent=2)
352
+
353
+ # Save cache/geometry_px.json and hitmap images for GUI hit areas
354
+ try:
355
+ from scitex.plt.utils._hitmap import (
356
+ HITMAP_AXES_COLOR,
357
+ HITMAP_BACKGROUND_COLOR,
358
+ apply_hitmap_colors,
359
+ extract_path_data,
360
+ extract_selectable_regions,
361
+ restore_original_colors,
362
+ )
363
+
364
+ geometry = {
365
+ "path_data": extract_path_data(fig),
366
+ "selectable_regions": extract_selectable_regions(fig),
367
+ }
368
+ with open(cache_dir / "geometry_px.json", "w") as f:
369
+ json.dump(geometry, f, indent=2)
370
+
371
+ # Generate hitmap images
372
+ axes_list = list(fig.axes) if hasattr(fig.axes, "__iter__") else [fig.axes]
373
+ original_props, color_map, groups = apply_hitmap_colors(fig)
374
+
375
+ # Store and set hitmap colors
376
+ saved_fig_facecolor = fig.patch.get_facecolor()
377
+ saved_ax_facecolors = []
378
+ for ax in axes_list:
379
+ saved_ax_facecolors.append(ax.get_facecolor())
380
+ ax.set_facecolor(HITMAP_BACKGROUND_COLOR)
381
+ for spine in ax.spines.values():
382
+ spine.set_color(HITMAP_AXES_COLOR)
383
+ fig.patch.set_facecolor(HITMAP_BACKGROUND_COLOR)
384
+
385
+ # Save hitmap PNG
386
+ fig.savefig(
387
+ cache_dir / "hitmap.png",
388
+ dpi=dpi,
389
+ format="png",
390
+ facecolor=HITMAP_BACKGROUND_COLOR,
391
+ )
392
+
393
+ # Save hitmap SVG
394
+ fig.savefig(
395
+ cache_dir / "hitmap.svg",
396
+ format="svg",
397
+ facecolor=HITMAP_BACKGROUND_COLOR,
398
+ )
399
+
400
+ # Restore colors
401
+ restore_original_colors(original_props)
402
+ fig.patch.set_facecolor(saved_fig_facecolor)
403
+ for i, ax in enumerate(axes_list):
404
+ ax.set_facecolor(saved_ax_facecolors[i])
405
+
406
+ except Exception:
407
+ pass # Skip if hitmap extraction fails
408
+
409
+ # Save cache/render_manifest.json
410
+ render_manifest = {
411
+ "dpi": dpi,
412
+ "format": ["png", "svg", "pdf"],
413
+ "bbox_inches": "tight",
414
+ "size_mm": {
415
+ "width": round(fig_width_inch * 25.4, 2),
416
+ "height": round(fig_height_inch * 25.4, 2),
417
+ },
418
+ "hitmap_png": "cache/hitmap.png",
419
+ "hitmap_svg": "cache/hitmap.svg",
420
+ }
421
+ with open(cache_dir / "render_manifest.json", "w") as f:
422
+ json.dump(render_manifest, f, indent=2)
423
+
424
+ # Save stats/stats.json and stats.csv placeholder (empty by default)
425
+ stats_data = {"comparisons": [], "tests": []}
426
+ with open(stats_dir / "stats.json", "w") as f:
427
+ json.dump(stats_data, f, indent=2)
428
+
429
+ # Create stats.csv with header for tabular export
430
+ import pandas as pd
431
+
432
+ stats_df = pd.DataFrame(
433
+ columns=[
434
+ "test_type",
435
+ "group1",
436
+ "group2",
437
+ "statistic",
438
+ "p_value",
439
+ "effect_size",
440
+ "significant",
441
+ ]
442
+ )
443
+ stats_df.to_csv(stats_dir / "stats.csv", index=False)
444
+
445
+ # Generate README.md for self-documentation
446
+ _generate_readme(bundle_dir, basename, spec, csv_df)
447
+
448
+ # Pack to ZIP if requested
449
+ if as_zip:
450
+ import shutil
451
+ import zipfile
452
+
453
+ with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
454
+ for file_path in bundle_dir.rglob("*"):
455
+ if file_path.is_file():
456
+ arcname = file_path.relative_to(bundle_dir.parent)
457
+ zf.write(file_path, arcname)
458
+ shutil.rmtree(temp_dir)
459
+
460
+
461
+ def _generate_readme(bundle_dir, basename, spec, csv_df):
462
+ """Generate README.md for self-documentation."""
463
+ from datetime import datetime
464
+
465
+ readme_lines = [
466
+ f"# {basename} FTS Bundle",
467
+ "",
468
+ "## Overview",
469
+ "",
470
+ f"- **Type**: {spec.get('type', 'plot')}",
471
+ f"- **Created**: {datetime.now().isoformat()}",
472
+ f"- **Bundle ID**: {spec.get('bundle_id', 'N/A')}",
473
+ "",
474
+ "## Structure",
475
+ "",
476
+ "```",
477
+ f"{basename}/",
478
+ "├── spec.json # What to plot (data mapping)",
479
+ "├── encoding.json # Data→visual mapping (scientific rigor)",
480
+ "├── theme.json # Pure aesthetics (colors, fonts)",
481
+ "├── style.json # Backward compat (= encoding + theme)",
482
+ "├── data/",
483
+ "│ ├── data.csv # Raw data",
484
+ "│ └── data_info.json # Column metadata",
485
+ "├── stats/",
486
+ "│ ├── stats.json # Statistical results",
487
+ "│ └── stats.csv # Tabular statistics",
488
+ "├── cache/",
489
+ "│ ├── geometry_px.json # Hit areas (regenerable)",
490
+ "│ ├── render_manifest.json",
491
+ "│ ├── hitmap.png # Hit testing image",
492
+ "│ └── hitmap.svg # Vector hit testing",
493
+ "└── exports/",
494
+ " ├── plot.png # Raster export",
495
+ " ├── plot.svg # Vector export",
496
+ " └── plot.pdf # Publication export",
497
+ "```",
498
+ "",
499
+ ]
500
+
501
+ if csv_df is not None:
502
+ readme_lines.extend(
503
+ [
504
+ "## Data Columns",
505
+ "",
506
+ "| Column | Type | Description |",
507
+ "|--------|------|-------------|",
508
+ ]
509
+ )
510
+ for col in csv_df.columns:
511
+ dtype = str(csv_df[col].dtype)
512
+ readme_lines.append(f"| {col} | {dtype} | - |")
513
+ readme_lines.append("")
514
+
515
+ readme_lines.extend(
516
+ [
517
+ "## Usage",
518
+ "",
519
+ "```python",
520
+ "from scitex.fts import FTS",
521
+ "",
522
+ f'bundle = FTS("{basename}.zip") # or "{basename}/" directory',
523
+ "bundle.show() # Display",
524
+ 'bundle.export("output.png") # Export',
525
+ "```",
526
+ "",
527
+ "---",
528
+ "*Generated by SciTeX*",
529
+ ]
530
+ )
531
+
532
+ with open(bundle_dir / "README.md", "w") as f:
533
+ f.write("\n".join(readme_lines))
534
+
535
+
536
+ # EOF
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-19
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_save_modules/_stx_bundle.py
4
+
5
+ """Save functions for FTS bundle format (.zip or directory)."""
6
+
7
+ from pathlib import Path
8
+
9
+
10
+ def save_stx_bundle(obj, spath, as_zip=True, bundle_type=None, basename=None, **kwargs):
11
+ """Save an object as an FTS bundle (.zip or directory).
12
+
13
+ FTS (Figure-Table-Statistics) is the unified bundle format that supports:
14
+ - figure: Publication figures with multiple panels
15
+ - plot: Single matplotlib plots
16
+ - stats: Statistical results
17
+
18
+ The content type is auto-detected from the object:
19
+ - FTS instance -> delegates to FTS.save()
20
+ - matplotlib.figure.Figure -> plot
21
+ - dict with 'panels' or 'elements' -> figure
22
+ - dict with 'comparisons' -> stats
23
+
24
+ Bundle structure:
25
+ output/ # or output.zip
26
+ node.json # Bundle metadata
27
+ encoding.json # Data-to-visual mappings
28
+ theme.json # Visual styling
29
+ data/ # Raw data files
30
+ exports/ # PNG, SVG, PDF exports
31
+
32
+ Parameters
33
+ ----------
34
+ obj : Any
35
+ Object to save (FTS, Figure, dict, etc.)
36
+ spath : str or Path
37
+ Output path (e.g., "output.zip" or "output/")
38
+ as_zip : bool
39
+ If True (default), save as ZIP archive. Use False for directory.
40
+ bundle_type : str, optional
41
+ Force bundle type: 'figure', 'plot', or 'stats'. Auto-detected if None.
42
+ **kwargs
43
+ Additional arguments passed to format-specific savers.
44
+ """
45
+ from scitex.fts import FTS
46
+
47
+ if isinstance(obj, FTS):
48
+ # Delegate to FTS.save()
49
+ obj.save()
50
+ return
51
+
52
+ p = Path(spath)
53
+
54
+ # Extract basename from path if not provided
55
+ if basename is None:
56
+ basename = p.stem
57
+
58
+ # Auto-detect content type from object
59
+ content_type = bundle_type
60
+ if content_type is None:
61
+ import matplotlib.figure
62
+
63
+ if isinstance(obj, matplotlib.figure.Figure):
64
+ content_type = "plot"
65
+ elif hasattr(obj, "figure"):
66
+ content_type = "plot"
67
+ obj = obj.figure
68
+ elif isinstance(obj, dict):
69
+ if "panels" in obj or "elements" in obj:
70
+ content_type = "figure"
71
+ elif "comparisons" in obj:
72
+ content_type = "stats"
73
+ else:
74
+ content_type = "figure" # Default for dicts
75
+ else:
76
+ raise ValueError(
77
+ f"Cannot auto-detect bundle type for {type(obj).__name__}. "
78
+ "Please specify bundle_type='figure', 'plot', or 'stats'."
79
+ )
80
+
81
+ # Route to appropriate handler based on content type
82
+ if content_type == "plot":
83
+ from ._pltz_stx import save_pltz_as_stx
84
+
85
+ save_pltz_as_stx(obj, spath, as_zip=as_zip, basename=basename, **kwargs)
86
+ elif content_type == "figure":
87
+ from scitex.fts import FTS
88
+
89
+ bundle = FTS(spath, create=True, node_type="figure")
90
+ if isinstance(obj, dict):
91
+ if "title" in obj:
92
+ bundle.node.title = obj["title"]
93
+ if "description" in obj:
94
+ bundle.node.description = obj["description"]
95
+ bundle.save()
96
+ elif content_type == "stats":
97
+ import scitex.stats as sstats
98
+
99
+ sstats.save_statsz(obj, spath, as_zip=as_zip, **kwargs)
100
+ else:
101
+ raise ValueError(f"Unknown bundle type: {content_type}")
102
+
103
+
104
+ # EOF