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,273 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-19
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/schema/_encoding.py
4
+
5
+ """
6
+ Encoding Schema - Data to Visual Mapping for Scientific Rigor.
7
+
8
+ This module defines the encoding layer that maps data columns to visual channels.
9
+ This separation ensures scientific reproducibility by explicitly documenting
10
+ how data is transformed into visual representation.
11
+
12
+ Encoding (encoding.json) - Data→Visual Mapping:
13
+ - Column bindings (x, y, color, size, shape)
14
+ - Data transformations (log, normalize, bin)
15
+ - Scale types (linear, log, categorical)
16
+ - Missing value handling
17
+ """
18
+
19
+ import json
20
+ from dataclasses import dataclass, field
21
+ from typing import Any, Dict, List, Optional
22
+
23
+ ENCODING_VERSION = "1.0.0"
24
+
25
+
26
+ @dataclass
27
+ class ChannelBinding:
28
+ """
29
+ Binding from data column to visual channel.
30
+
31
+ Parameters
32
+ ----------
33
+ channel : str
34
+ Visual channel (x, y, color, size, shape, opacity, etc.)
35
+ column : str
36
+ Data column name
37
+ scale : str
38
+ Scale type (linear, log, sqrt, categorical, ordinal)
39
+ domain : list, optional
40
+ Data domain [min, max] or list of categories
41
+ range : list, optional
42
+ Visual range (e.g., [0, 1] for opacity, color palette for color)
43
+ transform : str, optional
44
+ Data transformation (log, sqrt, normalize, zscore, rank)
45
+ """
46
+
47
+ channel: str
48
+ column: str
49
+ scale: str = "linear"
50
+ domain: Optional[List[Any]] = None
51
+ range: Optional[List[Any]] = None
52
+ transform: Optional[str] = None
53
+ missing_value: Optional[str] = "drop" # "drop", "zero", "mean", "interpolate"
54
+
55
+ def to_dict(self) -> Dict[str, Any]:
56
+ result = {
57
+ "channel": self.channel,
58
+ "column": self.column,
59
+ "scale": self.scale,
60
+ }
61
+ if self.domain is not None:
62
+ result["domain"] = self.domain
63
+ if self.range is not None:
64
+ result["range"] = self.range
65
+ if self.transform:
66
+ result["transform"] = self.transform
67
+ if self.missing_value != "drop":
68
+ result["missing_value"] = self.missing_value
69
+ return result
70
+
71
+ @classmethod
72
+ def from_dict(cls, data: Dict[str, Any]) -> "ChannelBinding":
73
+ return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
74
+
75
+
76
+ @dataclass
77
+ class TraceEncoding:
78
+ """
79
+ Encoding specification for a single trace.
80
+
81
+ Parameters
82
+ ----------
83
+ trace_id : str
84
+ ID of the trace this encoding applies to
85
+ bindings : list of ChannelBinding
86
+ Column to channel bindings
87
+ aggregate : str, optional
88
+ Aggregation method (mean, median, sum, count, etc.)
89
+ group_by : list, optional
90
+ Columns to group by before aggregation
91
+ """
92
+
93
+ trace_id: str
94
+ bindings: List[ChannelBinding] = field(default_factory=list)
95
+ aggregate: Optional[str] = None
96
+ group_by: Optional[List[str]] = None
97
+
98
+ def to_dict(self) -> Dict[str, Any]:
99
+ result = {
100
+ "trace_id": self.trace_id,
101
+ "bindings": [b.to_dict() for b in self.bindings],
102
+ }
103
+ if self.aggregate:
104
+ result["aggregate"] = self.aggregate
105
+ if self.group_by:
106
+ result["group_by"] = self.group_by
107
+ return result
108
+
109
+ @classmethod
110
+ def from_dict(cls, data: Dict[str, Any]) -> "TraceEncoding":
111
+ bindings = [ChannelBinding.from_dict(b) for b in data.get("bindings", [])]
112
+ return cls(
113
+ trace_id=data.get("trace_id", ""),
114
+ bindings=bindings,
115
+ aggregate=data.get("aggregate"),
116
+ group_by=data.get("group_by"),
117
+ )
118
+
119
+
120
+ @dataclass
121
+ class PlotEncoding:
122
+ """
123
+ Complete encoding specification for a plot.
124
+
125
+ Stored in encoding.json. Documents how data maps to visual channels
126
+ for scientific reproducibility.
127
+
128
+ Parameters
129
+ ----------
130
+ traces : list of TraceEncoding
131
+ Per-trace encoding specifications
132
+ data_hash : str, optional
133
+ Hash of source data for integrity verification
134
+ """
135
+
136
+ traces: List[TraceEncoding] = field(default_factory=list)
137
+ data_hash: Optional[str] = None
138
+
139
+ # Schema metadata
140
+ scitex_schema: str = "scitex.plt.encoding"
141
+ scitex_schema_version: str = ENCODING_VERSION
142
+
143
+ def to_dict(self) -> Dict[str, Any]:
144
+ result = {
145
+ "schema": {
146
+ "name": self.scitex_schema,
147
+ "version": self.scitex_schema_version,
148
+ },
149
+ "traces": [t.to_dict() for t in self.traces],
150
+ }
151
+ if self.data_hash:
152
+ result["data_hash"] = self.data_hash
153
+ return result
154
+
155
+ def to_json(self, indent: int = 2) -> str:
156
+ return json.dumps(self.to_dict(), indent=indent)
157
+
158
+ @classmethod
159
+ def from_dict(cls, data: Dict[str, Any]) -> "PlotEncoding":
160
+ return cls(
161
+ traces=[TraceEncoding.from_dict(t) for t in data.get("traces", [])],
162
+ data_hash=data.get("data_hash"),
163
+ )
164
+
165
+ @classmethod
166
+ def from_json(cls, json_str: str) -> "PlotEncoding":
167
+ return cls.from_dict(json.loads(json_str))
168
+
169
+ @classmethod
170
+ def from_spec_and_style(cls, spec: dict, style: dict) -> "PlotEncoding":
171
+ """
172
+ Create encoding from spec and style for backward compatibility.
173
+
174
+ Extracts column bindings from PlotSpec traces and color/size mappings
175
+ from PlotStyle trace overrides.
176
+ """
177
+ traces = []
178
+ spec_traces = spec.get("traces", [])
179
+ style_traces = {t.get("trace_id"): t for t in style.get("traces", [])}
180
+
181
+ for trace in spec_traces:
182
+ trace_id = trace.get("id", "")
183
+ bindings = []
184
+
185
+ # X column
186
+ if trace.get("x_col"):
187
+ bindings.append(
188
+ ChannelBinding(
189
+ channel="x",
190
+ column=trace["x_col"],
191
+ )
192
+ )
193
+
194
+ # Y column
195
+ if trace.get("y_col"):
196
+ bindings.append(
197
+ ChannelBinding(
198
+ channel="y",
199
+ column=trace["y_col"],
200
+ )
201
+ )
202
+
203
+ # Data columns (for boxplot, etc.)
204
+ for i, col in enumerate(trace.get("data_cols", [])):
205
+ bindings.append(
206
+ ChannelBinding(
207
+ channel=f"data_{i}",
208
+ column=col,
209
+ )
210
+ )
211
+
212
+ # Value column (for heatmap)
213
+ if trace.get("value_col"):
214
+ bindings.append(
215
+ ChannelBinding(
216
+ channel="value",
217
+ column=trace["value_col"],
218
+ )
219
+ )
220
+
221
+ # Vector columns (for quiver)
222
+ if trace.get("u_col"):
223
+ bindings.append(
224
+ ChannelBinding(
225
+ channel="u",
226
+ column=trace["u_col"],
227
+ )
228
+ )
229
+ if trace.get("v_col"):
230
+ bindings.append(
231
+ ChannelBinding(
232
+ channel="v",
233
+ column=trace["v_col"],
234
+ )
235
+ )
236
+
237
+ # Style overrides that are data-driven
238
+ style_trace = style_traces.get(trace_id, {})
239
+ if style_trace.get("color_by"):
240
+ bindings.append(
241
+ ChannelBinding(
242
+ channel="color",
243
+ column=style_trace["color_by"],
244
+ )
245
+ )
246
+ if style_trace.get("size_by"):
247
+ bindings.append(
248
+ ChannelBinding(
249
+ channel="size",
250
+ column=style_trace["size_by"],
251
+ )
252
+ )
253
+
254
+ if bindings:
255
+ traces.append(
256
+ TraceEncoding(
257
+ trace_id=trace_id,
258
+ bindings=bindings,
259
+ )
260
+ )
261
+
262
+ return cls(traces=traces)
263
+
264
+
265
+ __all__ = [
266
+ "ENCODING_VERSION",
267
+ "ChannelBinding",
268
+ "TraceEncoding",
269
+ "PlotEncoding",
270
+ ]
271
+
272
+
273
+ # EOF
@@ -0,0 +1,406 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-19
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/schema/_figure_elements.py
4
+
5
+ """
6
+ Figure Elements Schema - Title, Caption, and Panel Labels.
7
+
8
+ This module defines figure-level text elements that appear in theme.json
9
+ for editability while maintaining consistent styling across the figure.
10
+
11
+ These elements enable auto-generation of publication-ready captions:
12
+ "Figure 1: Main Results. (A) Time-series analysis. (B) Frequency distribution."
13
+ """
14
+
15
+ from dataclasses import asdict, dataclass, field
16
+ from typing import Any, Dict, List, Optional
17
+
18
+ FIGURE_ELEMENTS_VERSION = "1.0.0"
19
+
20
+
21
+ @dataclass
22
+ class FigureTitle:
23
+ """
24
+ Figure title specification.
25
+
26
+ Parameters
27
+ ----------
28
+ text : str
29
+ The main title text (e.g., "Main Results")
30
+ prefix : str
31
+ Prefix before number (e.g., "Figure", "Fig.")
32
+ number : int, optional
33
+ Figure number (None for unnumbered)
34
+ font_size_pt : float
35
+ Title font size in points
36
+ font_weight : str
37
+ Font weight (normal, bold)
38
+ position : str
39
+ Position (top, bottom)
40
+ visible : bool
41
+ Whether to render the title
42
+ """
43
+
44
+ text: str = ""
45
+ prefix: str = "Figure"
46
+ number: Optional[int] = None
47
+ font_size_pt: float = 10.0
48
+ font_weight: str = "bold"
49
+ position: str = "top"
50
+ visible: bool = True
51
+
52
+ def format(self, include_number: bool = True) -> str:
53
+ """Format the title as displayed string."""
54
+ if include_number and self.number is not None:
55
+ return f"{self.prefix} {self.number}: {self.text}"
56
+ elif self.text:
57
+ return self.text
58
+ return ""
59
+
60
+ def to_dict(self) -> Dict[str, Any]:
61
+ result = {
62
+ "text": self.text,
63
+ "prefix": self.prefix,
64
+ "font_size_pt": self.font_size_pt,
65
+ "font_weight": self.font_weight,
66
+ "position": self.position,
67
+ "visible": self.visible,
68
+ }
69
+ if self.number is not None:
70
+ result["number"] = self.number
71
+ return result
72
+
73
+ @classmethod
74
+ def from_dict(cls, data: Dict[str, Any]) -> "FigureTitle":
75
+ return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
76
+
77
+
78
+ @dataclass
79
+ class Caption:
80
+ """
81
+ Figure caption specification.
82
+
83
+ Parameters
84
+ ----------
85
+ text : str
86
+ Manual caption text (overrides auto-generation if set)
87
+ auto_generate : bool
88
+ Whether to auto-generate from panel descriptions
89
+ font_size_pt : float
90
+ Caption font size in points
91
+ position : str
92
+ Position (top, bottom)
93
+ visible : bool
94
+ Whether to render the caption
95
+ """
96
+
97
+ text: str = ""
98
+ auto_generate: bool = True
99
+ font_size_pt: float = 8.0
100
+ position: str = "bottom"
101
+ visible: bool = True
102
+
103
+ def to_dict(self) -> Dict[str, Any]:
104
+ return asdict(self)
105
+
106
+ @classmethod
107
+ def from_dict(cls, data: Dict[str, Any]) -> "Caption":
108
+ return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
109
+
110
+
111
+ @dataclass
112
+ class PanelLabels:
113
+ """
114
+ Panel label styling for multi-panel figures.
115
+
116
+ Controls how panel letters (A, B, C, ...) are rendered on child plots.
117
+
118
+ Parameters
119
+ ----------
120
+ style : str
121
+ Letter style: "uppercase" (A, B, C), "lowercase" (a, b, c),
122
+ "roman" (i, ii, iii), "Roman" (I, II, III)
123
+ format : str
124
+ Format template with {letter} placeholder
125
+ font_size_pt : float
126
+ Label font size in points
127
+ font_weight : str
128
+ Font weight (normal, bold)
129
+ position : str
130
+ Position on panel (top-left, top-right, bottom-left, bottom-right)
131
+ offset_mm : dict
132
+ Offset from corner {x: mm, y: mm}
133
+ visible : bool
134
+ Whether to render panel labels
135
+ """
136
+
137
+ style: str = "uppercase"
138
+ format: str = "({letter})"
139
+ font_size_pt: float = 12.0
140
+ font_weight: str = "bold"
141
+ position: str = "top-left"
142
+ offset_mm: Dict[str, float] = field(default_factory=lambda: {"x": 2.0, "y": 2.0})
143
+ visible: bool = True
144
+
145
+ def format_letter(self, index: int) -> str:
146
+ """
147
+ Format panel letter for given index (0-based).
148
+
149
+ Parameters
150
+ ----------
151
+ index : int
152
+ Zero-based panel index
153
+
154
+ Returns
155
+ -------
156
+ str
157
+ Formatted label like "(A)", "(B)", etc.
158
+ """
159
+ if self.style == "uppercase":
160
+ letter = chr(ord("A") + index)
161
+ elif self.style == "lowercase":
162
+ letter = chr(ord("a") + index)
163
+ elif self.style == "roman":
164
+ letter = _to_roman(index + 1).lower()
165
+ elif self.style == "Roman":
166
+ letter = _to_roman(index + 1)
167
+ else:
168
+ letter = chr(ord("A") + index)
169
+
170
+ return self.format.replace("{letter}", letter)
171
+
172
+ def get_letter(self, index: int) -> str:
173
+ """Get just the letter without formatting."""
174
+ if self.style == "uppercase":
175
+ return chr(ord("A") + index)
176
+ elif self.style == "lowercase":
177
+ return chr(ord("a") + index)
178
+ elif self.style == "roman":
179
+ return _to_roman(index + 1).lower()
180
+ elif self.style == "Roman":
181
+ return _to_roman(index + 1)
182
+ return chr(ord("A") + index)
183
+
184
+ def to_dict(self) -> Dict[str, Any]:
185
+ return {
186
+ "style": self.style,
187
+ "format": self.format,
188
+ "font_size_pt": self.font_size_pt,
189
+ "font_weight": self.font_weight,
190
+ "position": self.position,
191
+ "offset_mm": self.offset_mm,
192
+ "visible": self.visible,
193
+ }
194
+
195
+ @classmethod
196
+ def from_dict(cls, data: Dict[str, Any]) -> "PanelLabels":
197
+ return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
198
+
199
+
200
+ def _to_roman(num: int) -> str:
201
+ """Convert integer to Roman numeral."""
202
+ values = [
203
+ (1000, "M"),
204
+ (900, "CM"),
205
+ (500, "D"),
206
+ (400, "CD"),
207
+ (100, "C"),
208
+ (90, "XC"),
209
+ (50, "L"),
210
+ (40, "XL"),
211
+ (10, "X"),
212
+ (9, "IX"),
213
+ (5, "V"),
214
+ (4, "IV"),
215
+ (1, "I"),
216
+ ]
217
+ result = ""
218
+ for value, numeral in values:
219
+ while num >= value:
220
+ result += numeral
221
+ num -= value
222
+ return result
223
+
224
+
225
+ @dataclass
226
+ class PanelInfo:
227
+ """
228
+ Information about a single panel in a multi-panel figure.
229
+
230
+ Used in spec.json to store per-panel metadata that combines with
231
+ theme-level PanelLabels to generate captions.
232
+
233
+ Parameters
234
+ ----------
235
+ panel_id : str
236
+ Unique identifier for the panel (matches child ID)
237
+ letter : str
238
+ Panel letter override (auto-assigned if empty)
239
+ description : str
240
+ Description for caption generation
241
+ order : int
242
+ Display order (0-based)
243
+ """
244
+
245
+ panel_id: str
246
+ letter: str = ""
247
+ description: str = ""
248
+ order: int = 0
249
+
250
+ def to_dict(self) -> Dict[str, Any]:
251
+ result = {"panel_id": self.panel_id, "order": self.order}
252
+ if self.letter:
253
+ result["letter"] = self.letter
254
+ if self.description:
255
+ result["description"] = self.description
256
+ return result
257
+
258
+ @classmethod
259
+ def from_dict(cls, data: Dict[str, Any]) -> "PanelInfo":
260
+ return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
261
+
262
+
263
+ def generate_caption(
264
+ title: FigureTitle,
265
+ caption: Caption,
266
+ panels: List[PanelInfo],
267
+ panel_labels: PanelLabels,
268
+ ) -> str:
269
+ """
270
+ Generate full figure caption from components.
271
+
272
+ Format: "Figure 1: Main Title. (A) Description A. (B) Description B."
273
+
274
+ Parameters
275
+ ----------
276
+ title : FigureTitle
277
+ Figure title specification
278
+ caption : Caption
279
+ Caption specification
280
+ panels : list of PanelInfo
281
+ Panel information with descriptions
282
+ panel_labels : PanelLabels
283
+ Panel label styling
284
+
285
+ Returns
286
+ -------
287
+ str
288
+ Complete formatted caption
289
+ """
290
+ # If manual caption is set and auto_generate is off, use it
291
+ if caption.text and not caption.auto_generate:
292
+ return caption.text
293
+
294
+ parts = []
295
+
296
+ # Add title
297
+ title_str = title.format()
298
+ if title_str:
299
+ parts.append(title_str)
300
+
301
+ # Sort panels by order
302
+ sorted_panels = sorted(panels, key=lambda p: p.order)
303
+
304
+ # Add panel descriptions
305
+ panel_parts = []
306
+ for idx, panel in enumerate(sorted_panels):
307
+ if panel.description:
308
+ letter = panel.letter or panel_labels.get_letter(idx)
309
+ formatted = panel_labels.format.replace("{letter}", letter)
310
+ panel_parts.append(f"{formatted} {panel.description}")
311
+
312
+ if panel_parts:
313
+ parts.append(" ".join(panel_parts))
314
+
315
+ return " ".join(parts) if parts else ""
316
+
317
+
318
+ def generate_caption_latex(
319
+ title: FigureTitle,
320
+ caption: Caption,
321
+ panels: List[PanelInfo],
322
+ panel_labels: PanelLabels,
323
+ ) -> str:
324
+ """
325
+ Generate LaTeX-formatted figure caption.
326
+
327
+ Format: "\\textbf{Figure 1: Main Title.} (A) Description A. (B) Description B."
328
+
329
+ Returns
330
+ -------
331
+ str
332
+ LaTeX-formatted caption
333
+ """
334
+ parts = []
335
+
336
+ # Add bold title
337
+ title_str = title.format()
338
+ if title_str:
339
+ parts.append(f"\\textbf{{{title_str}}}")
340
+
341
+ # Sort and add panel descriptions
342
+ sorted_panels = sorted(panels, key=lambda p: p.order)
343
+ panel_parts = []
344
+ for idx, panel in enumerate(sorted_panels):
345
+ if panel.description:
346
+ letter = panel.letter or panel_labels.get_letter(idx)
347
+ formatted = panel_labels.format.replace("{letter}", letter)
348
+ panel_parts.append(f"\\textbf{{{formatted}}} {panel.description}")
349
+
350
+ if panel_parts:
351
+ parts.append(" ".join(panel_parts))
352
+
353
+ return " ".join(parts) if parts else ""
354
+
355
+
356
+ def generate_caption_markdown(
357
+ title: FigureTitle,
358
+ caption: Caption,
359
+ panels: List[PanelInfo],
360
+ panel_labels: PanelLabels,
361
+ ) -> str:
362
+ """
363
+ Generate Markdown-formatted figure caption.
364
+
365
+ Format: "**Figure 1: Main Title.** (A) Description A. (B) Description B."
366
+
367
+ Returns
368
+ -------
369
+ str
370
+ Markdown-formatted caption
371
+ """
372
+ parts = []
373
+
374
+ # Add bold title
375
+ title_str = title.format()
376
+ if title_str:
377
+ parts.append(f"**{title_str}**")
378
+
379
+ # Sort and add panel descriptions
380
+ sorted_panels = sorted(panels, key=lambda p: p.order)
381
+ panel_parts = []
382
+ for idx, panel in enumerate(sorted_panels):
383
+ if panel.description:
384
+ letter = panel.letter or panel_labels.get_letter(idx)
385
+ formatted = panel_labels.format.replace("{letter}", letter)
386
+ panel_parts.append(f"**{formatted}** {panel.description}")
387
+
388
+ if panel_parts:
389
+ parts.append(" ".join(panel_parts))
390
+
391
+ return " ".join(parts) if parts else ""
392
+
393
+
394
+ __all__ = [
395
+ "FIGURE_ELEMENTS_VERSION",
396
+ "FigureTitle",
397
+ "Caption",
398
+ "PanelLabels",
399
+ "PanelInfo",
400
+ "generate_caption",
401
+ "generate_caption_latex",
402
+ "generate_caption_markdown",
403
+ ]
404
+
405
+
406
+ # EOF
scitex/schema/_plot.py CHANGED
File without changes