scitex 2.8.1__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 (415) 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/dict/_pop_keys.py +1 -7
  105. scitex/dsp/__init__.py +15 -10
  106. scitex/dsp/add_noise.py +5 -2
  107. scitex/dsp/example.py +35 -22
  108. scitex/dsp/filt.py +8 -3
  109. scitex/dsp/reference.py +3 -2
  110. scitex/dsp/utils/__init__.py +2 -1
  111. scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
  112. scitex/dt/__init__.py +39 -2
  113. scitex/errors.py +82 -521
  114. scitex/fig/__init__.py +4 -4
  115. scitex/fig/editor/edit/panel_loader.py +1 -1
  116. scitex/fig/io/_bundle.py +7 -7
  117. scitex/fts/README.md +262 -0
  118. scitex/fts/TODO.md +66 -0
  119. scitex/fts/__init__.py +90 -0
  120. scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
  121. scitex/fts/_bundle/_FTS.py +657 -0
  122. scitex/fts/_bundle/__init__.py +38 -0
  123. scitex/fts/_bundle/_children.py +216 -0
  124. scitex/fts/_bundle/_conversion/__init__.py +15 -0
  125. scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
  126. scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
  127. scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
  128. scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
  129. scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
  130. scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
  131. scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
  132. scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
  133. scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
  134. scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
  135. scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
  136. scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
  137. scitex/fts/_bundle/_extractors/__init__.py +32 -0
  138. scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
  139. scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
  140. scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
  141. scitex/fts/_bundle/_loader.py +134 -0
  142. scitex/fts/_bundle/_mpl_helpers.py +389 -0
  143. scitex/fts/_bundle/_saver.py +269 -0
  144. scitex/fts/_bundle/_storage.py +200 -0
  145. scitex/fts/_bundle/_utils/__init__.py +55 -0
  146. scitex/fts/_bundle/_utils/_const.py +26 -0
  147. scitex/fts/_bundle/_utils/_errors.py +73 -0
  148. scitex/fts/_bundle/_utils/_generate.py +21 -0
  149. scitex/fts/_bundle/_utils/_types.py +76 -0
  150. scitex/fts/_bundle/_validation.py +434 -0
  151. scitex/fts/_bundle/_zipbundle.py +165 -0
  152. scitex/fts/_fig/__init__.py +22 -0
  153. scitex/fts/_fig/_backend/__init__.py +53 -0
  154. scitex/fts/_fig/_backend/_export.py +165 -0
  155. scitex/fts/_fig/_backend/_parser.py +188 -0
  156. scitex/fts/_fig/_backend/_render.py +538 -0
  157. scitex/fts/_fig/_composite.py +345 -0
  158. scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
  159. scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
  160. scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
  161. scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
  162. scitex/fts/_fig/_dataclasses/__init__.py +47 -0
  163. scitex/fts/_fig/_editor/__init__.py +14 -0
  164. scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
  165. scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
  166. scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
  167. scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
  168. scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
  169. scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
  170. scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
  171. scitex/fts/_fig/_editor/_defaults.py +300 -0
  172. scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
  173. scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
  174. scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
  175. scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
  176. scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
  177. scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
  178. scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
  179. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
  180. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
  181. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
  182. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
  183. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
  184. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
  185. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
  186. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
  187. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
  188. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
  189. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
  190. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
  191. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
  192. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
  193. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
  194. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
  195. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
  196. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
  197. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
  198. scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
  199. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
  200. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
  201. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
  202. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
  203. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
  204. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
  205. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
  206. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
  207. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
  208. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
  209. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
  210. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
  211. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
  212. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
  213. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
  214. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
  215. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
  216. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
  217. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
  218. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
  219. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
  220. scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
  221. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
  222. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
  223. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
  224. scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
  225. scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
  226. scitex/fts/_fig/_models/_Annotations.py +115 -0
  227. scitex/fts/_fig/_models/_Axes.py +152 -0
  228. scitex/fts/_fig/_models/_Figure.py +138 -0
  229. scitex/fts/_fig/_models/_Guides.py +104 -0
  230. scitex/fts/_fig/_models/_Plot.py +123 -0
  231. scitex/fts/_fig/_models/_Styles.py +245 -0
  232. scitex/fts/_fig/_models/__init__.py +80 -0
  233. scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
  234. scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
  235. scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
  236. scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
  237. scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
  238. scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
  239. scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
  240. scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
  241. scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
  242. scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
  243. scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
  244. scitex/fts/_fig/_utils/__init__.py +129 -0
  245. scitex/fts/_fig/_utils/_auto_layout.py +127 -0
  246. scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
  247. scitex/fts/_fig/_utils/_const_sizes.py +48 -0
  248. scitex/fts/_fig/_utils/_convert_coords.py +77 -0
  249. scitex/fts/_fig/_utils/_get_template.py +178 -0
  250. scitex/fts/_fig/_utils/_normalize.py +73 -0
  251. scitex/fts/_fig/_utils/_plot_layout.py +397 -0
  252. scitex/fts/_fig/_utils/_validate.py +197 -0
  253. scitex/fts/_kinds/__init__.py +45 -0
  254. scitex/fts/_kinds/_figure/__init__.py +19 -0
  255. scitex/fts/_kinds/_figure/_composite.py +345 -0
  256. scitex/fts/_kinds/_plot/__init__.py +25 -0
  257. scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
  258. scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
  259. scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
  260. scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
  261. scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
  262. scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
  263. scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
  264. scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
  265. scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
  266. scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
  267. scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
  268. scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
  269. scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
  270. scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
  271. scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
  272. scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
  273. scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
  274. scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
  275. scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
  276. scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
  277. scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
  278. scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
  279. scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
  280. scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
  281. scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
  282. scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
  283. scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
  284. scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
  285. scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
  286. scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
  287. scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
  288. scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
  289. scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
  290. scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
  291. scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
  292. scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
  293. scitex/fts/_kinds/_shape/__init__.py +141 -0
  294. scitex/fts/_kinds/_stats/__init__.py +56 -0
  295. scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
  296. scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
  297. scitex/fts/_kinds/_table/__init__.py +72 -0
  298. scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
  299. scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
  300. scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
  301. scitex/fts/_kinds/_table/_latex/_export.py +279 -0
  302. scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
  303. scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
  304. scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
  305. scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
  306. scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
  307. scitex/fts/_kinds/_text/__init__.py +77 -0
  308. scitex/fts/_schemas/data_info.schema.json +75 -0
  309. scitex/fts/_schemas/encoding.schema.json +90 -0
  310. scitex/fts/_schemas/node.schema.json +145 -0
  311. scitex/fts/_schemas/render_manifest.schema.json +62 -0
  312. scitex/fts/_schemas/stats.schema.json +132 -0
  313. scitex/fts/_schemas/theme.schema.json +141 -0
  314. scitex/fts/_stats/__init__.py +48 -0
  315. scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
  316. scitex/fts/_stats/_dataclasses/__init__.py +48 -0
  317. scitex/fts/_tables/__init__.py +65 -0
  318. scitex/fts/_tables/_latex/__init__.py +93 -0
  319. scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
  320. scitex/fts/_tables/_latex/_editor/_app.py +725 -0
  321. scitex/fts/_tables/_latex/_export.py +279 -0
  322. scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
  323. scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
  324. scitex/fts/_tables/_latex/_table_exporter.py +362 -0
  325. scitex/fts/_tables/_latex/_utils.py +369 -0
  326. scitex/fts/_tables/_latex/_validator.py +445 -0
  327. scitex/gen/__init__.py +66 -25
  328. scitex/gen/misc.py +28 -0
  329. scitex/io/__init__.py +47 -32
  330. scitex/io/_load.py +87 -36
  331. scitex/io/_load_modules/__init__.py +10 -7
  332. scitex/io/_load_modules/_pandas.py +6 -1
  333. scitex/io/_save.py +299 -1556
  334. scitex/io/_save_modules/__init__.py +76 -19
  335. scitex/io/_save_modules/_figure_utils.py +90 -0
  336. scitex/io/_save_modules/_image_csv.py +497 -0
  337. scitex/io/_save_modules/_legends.py +91 -0
  338. scitex/io/_save_modules/_pltz_bundle.py +356 -0
  339. scitex/io/_save_modules/_pltz_stx.py +536 -0
  340. scitex/io/_save_modules/_stx_bundle.py +104 -0
  341. scitex/io/_save_modules/_symlink.py +96 -0
  342. scitex/io/_save_modules/_yaml.py +1 -1
  343. scitex/io/_save_modules/_zarr.py +64 -18
  344. scitex/io/bundle/README.md +212 -0
  345. scitex/io/bundle/__init__.py +110 -0
  346. scitex/io/{_bundle.py → bundle/_core.py} +168 -97
  347. scitex/io/bundle/_nested.py +713 -0
  348. scitex/io/bundle/_types.py +74 -0
  349. scitex/io/{_zip_bundle.py → bundle/_zip.py} +93 -45
  350. scitex/io/utils/h5_to_zarr.py +1 -1
  351. scitex/logging/__init__.py +108 -13
  352. scitex/logging/_errors.py +508 -0
  353. scitex/logging/_formatters.py +30 -6
  354. scitex/logging/_warnings.py +261 -0
  355. scitex/plt/__init__.py +4 -1
  356. scitex/plt/_figrecipe.py +236 -0
  357. scitex/plt/_subplots/_AxisWrapper.py +6 -0
  358. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
  359. scitex/plt/_subplots/_FigWrapper.py +15 -0
  360. scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
  361. scitex/plt/_subplots/_export_as_csv.py +11 -0
  362. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
  363. scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
  364. scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
  365. scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
  366. scitex/plt/_subplots/_fonts.py +71 -0
  367. scitex/plt/_subplots/_mm_layout.py +282 -0
  368. scitex/plt/gallery/__init__.py +99 -2
  369. scitex/plt/styles/_plot_postprocess.py +3 -1
  370. scitex/plt/utils/_configure_mpl.py +16 -19
  371. scitex/repro/_RandomStateManager.py +13 -8
  372. scitex/resource/__init__.py +19 -1
  373. scitex/resource/_utils/_get_env_info.py +13 -25
  374. scitex/schema/__init__.py +149 -160
  375. scitex/schema/_encoding.py +273 -0
  376. scitex/schema/_figure_elements.py +406 -0
  377. scitex/schema/_theme.py +360 -0
  378. scitex/schema/_validation.py +0 -98
  379. scitex/scholar/__init__.py +56 -14
  380. scitex/scholar/auth/ScholarAuthManager.py +1 -1
  381. scitex/scholar/auth/__init__.py +11 -2
  382. scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
  383. scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
  384. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
  385. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
  386. scitex/scholar/config/ScholarConfig.py +1 -1
  387. scitex/scholar/core/Scholar.py +1 -1
  388. scitex/session/_decorator.py +18 -16
  389. scitex/session/_lifecycle.py +9 -11
  390. scitex/session/template.py +9 -8
  391. scitex/sh/test_sh.py +72 -0
  392. scitex/sh/test_sh_simple.py +61 -0
  393. scitex/stats/__init__.py +221 -97
  394. scitex/stats/_schema.py +21 -22
  395. scitex/stats/descriptive/_circular.py +212 -351
  396. scitex/stats/descriptive/_describe.py +81 -132
  397. scitex/stats/descriptive/_nan.py +205 -433
  398. scitex/stats/descriptive/_real.py +127 -141
  399. scitex/str/_format_plot_text.py +5 -5
  400. scitex/str/_latex.py +26 -84
  401. scitex/str/_latex_fallback.py +53 -47
  402. scitex/web/_search_pubmed.py +5 -4
  403. scitex/writer/tests/test_diff_between.py +451 -0
  404. scitex/writer/tests/test_document_section.py +311 -0
  405. scitex/writer/tests/test_document_workflow.py +393 -0
  406. scitex/writer/tests/test_writer.py +361 -0
  407. scitex/writer/tests/test_writer_integration.py +303 -0
  408. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
  409. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/RECORD +412 -97
  410. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
  411. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
  412. scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
  413. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
  414. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
  415. {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -76,6 +76,8 @@ from ._export_as_csv_formatters import (
76
76
  _format_eventplot,
77
77
  _format_fill,
78
78
  _format_fill_between,
79
+ _format_stackplot,
80
+ _format_pcolormesh,
79
81
  _format_hexbin,
80
82
  _format_hist,
81
83
  _format_hist2d,
@@ -147,6 +149,9 @@ _FORMATTER_REGISTRY = {
147
149
  "eventplot": _format_eventplot,
148
150
  "fill": _format_fill,
149
151
  "fill_between": _format_fill_between,
152
+ "stackplot": _format_stackplot,
153
+ "pcolormesh": _format_pcolormesh,
154
+ "pcolor": _format_pcolormesh,
150
155
  "hexbin": _format_hexbin,
151
156
  "hist": _format_hist,
152
157
  "hist2d": _format_hist2d,
@@ -337,6 +342,12 @@ def format_record(record, record_index=0):
337
342
  return _format_fill(id, tracked_dict, kwargs)
338
343
  elif method == "fill_between":
339
344
  return _format_fill_between(id, tracked_dict, kwargs)
345
+ elif method == "stackplot":
346
+ return _format_stackplot(id, tracked_dict, kwargs)
347
+ elif method == "pcolormesh":
348
+ return _format_pcolormesh(id, tracked_dict, kwargs)
349
+ elif method == "pcolor":
350
+ return _format_pcolormesh(id, tracked_dict, kwargs)
340
351
  elif method == "hexbin":
341
352
  return _format_hexbin(id, tracked_dict, kwargs)
342
353
  elif method == "hist2d":
@@ -24,6 +24,8 @@ from ._format_errorbar import _format_errorbar
24
24
  from ._format_eventplot import _format_eventplot
25
25
  from ._format_fill import _format_fill
26
26
  from ._format_fill_between import _format_fill_between
27
+ from ._format_stackplot import _format_stackplot
28
+ from ._format_pcolormesh import _format_pcolormesh
27
29
  from ._format_hexbin import _format_hexbin
28
30
  from ._format_hist2d import _format_hist2d
29
31
  from ._format_imshow import _format_imshow
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-21 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py
5
+
6
+ import numpy as np
7
+ import pandas as pd
8
+
9
+ from scitex.plt.utils._csv_column_naming import get_csv_column_name
10
+ from ._format_plot import _parse_tracking_id
11
+
12
+
13
+ def _format_pcolormesh(id, tracked_dict, kwargs):
14
+ """Format data from a pcolormesh call.
15
+
16
+ Args:
17
+ id (str): Identifier for the plot
18
+ tracked_dict (dict): Dictionary containing tracked data
19
+ kwargs (dict): Keyword arguments passed to pcolormesh
20
+
21
+ Returns:
22
+ pd.DataFrame: Formatted data from pcolormesh (x, y, value columns)
23
+ """
24
+ if not tracked_dict or not isinstance(tracked_dict, dict):
25
+ return pd.DataFrame()
26
+
27
+ # Parse tracking ID to get axes position and trace ID
28
+ ax_row, ax_col, trace_id = _parse_tracking_id(id)
29
+
30
+ args = tracked_dict.get("args", ())
31
+
32
+ if len(args) == 0:
33
+ return pd.DataFrame()
34
+
35
+ # pcolormesh can be called as:
36
+ # pcolormesh(C) - just color values
37
+ # pcolormesh(X, Y, C) - with coordinates
38
+ if len(args) == 1:
39
+ # Just C provided
40
+ C = np.asarray(args[0])
41
+ rows, cols = C.shape
42
+ Y, X = np.meshgrid(range(rows), range(cols), indexing="ij")
43
+ elif len(args) >= 3:
44
+ # X, Y, C provided
45
+ X = np.asarray(args[0])
46
+ Y = np.asarray(args[1])
47
+ C = np.asarray(args[2])
48
+ else:
49
+ return pd.DataFrame()
50
+
51
+ # Get column names
52
+ col_x = get_csv_column_name("x", ax_row, ax_col, trace_id=trace_id)
53
+ col_y = get_csv_column_name("y", ax_row, ax_col, trace_id=trace_id)
54
+ col_value = get_csv_column_name("value", ax_row, ax_col, trace_id=trace_id)
55
+
56
+ # Flatten for CSV format
57
+ df = pd.DataFrame({
58
+ col_x: X.flatten(),
59
+ col_y: Y.flatten(),
60
+ col_value: C.flatten(),
61
+ })
62
+
63
+ return df
64
+
65
+
66
+ # EOF
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-12-21 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py
5
+
6
+ import numpy as np
7
+ import pandas as pd
8
+
9
+ from scitex.plt.utils._csv_column_naming import get_csv_column_name
10
+ from ._format_plot import _parse_tracking_id
11
+
12
+
13
+ def _format_stackplot(id, tracked_dict, kwargs):
14
+ """Format data from a stackplot call.
15
+
16
+ Args:
17
+ id (str): Identifier for the plot
18
+ tracked_dict (dict): Dictionary containing tracked data
19
+ kwargs (dict): Keyword arguments passed to stackplot
20
+
21
+ Returns:
22
+ pd.DataFrame: Formatted data from stackplot (x and multiple y columns)
23
+ """
24
+ if not tracked_dict or not isinstance(tracked_dict, dict):
25
+ return pd.DataFrame()
26
+
27
+ # Parse tracking ID to get axes position and trace ID
28
+ ax_row, ax_col, trace_id = _parse_tracking_id(id)
29
+
30
+ args = tracked_dict.get("args", ())
31
+
32
+ # stackplot(x, y1, y2, y3, ...) or stackplot(x, [y1, y2, y3], ...)
33
+ if len(args) < 2:
34
+ return pd.DataFrame()
35
+
36
+ x = np.asarray(args[0])
37
+ col_x = get_csv_column_name("x", ax_row, ax_col, trace_id=trace_id)
38
+ data = {col_x: x}
39
+
40
+ # Get labels from kwargs if available
41
+ labels = kwargs.get("labels", [])
42
+
43
+ # Handle remaining args as y arrays
44
+ y_arrays = args[1:]
45
+
46
+ # If first y arg is a 2D array, treat rows as separate series
47
+ if len(y_arrays) == 1 and hasattr(y_arrays[0], "ndim"):
48
+ y_data = np.asarray(y_arrays[0])
49
+ if y_data.ndim == 2:
50
+ y_arrays = [y_data[i] for i in range(y_data.shape[0])]
51
+
52
+ for i, y in enumerate(y_arrays):
53
+ y = np.asarray(y)
54
+ # Use label if available, otherwise use index
55
+ label = labels[i] if i < len(labels) else f"y{i:02d}"
56
+ col_y = get_csv_column_name(label, ax_row, ax_col, trace_id=trace_id)
57
+ data[col_y] = y
58
+
59
+ return pd.DataFrame(data)
60
+
61
+
62
+ # EOF
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-05-18 22:05:10 (ywatanabe)"
4
+ # File: /data/gpfs/projects/punim2354/ywatanabe/scitex_repo/src/scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py
5
+ # ----------------------------------------
6
+ import os
7
+ __FILE__ = __file__
8
+ __DIR__ = os.path.dirname(__FILE__)
9
+ # ----------------------------------------
10
+
11
+ import unittest
12
+ import numpy as np
13
+ import pandas as pd
14
+
15
+ # Import formatters directly
16
+ from ._format_plot import _format_plot
17
+ from ._format_plot_kde import _format_plot_kde
18
+ from ._format_plot_ecdf import _format_plot_ecdf
19
+ from ._format_plot_heatmap import _format_plot_heatmap
20
+ from ._format_plot_violin import _format_plot_violin
21
+ from ._format_plot_shaded_line import _format_plot_shaded_line
22
+ from ._format_plot_scatter_hist import _format_plot_scatter_hist
23
+
24
+
25
+ class FormattersTest(unittest.TestCase):
26
+ """Test the formatter functions."""
27
+
28
+ def test_format_plot_kde(self):
29
+ """Test _format_plot_kde function."""
30
+ # Test case 1: Normal input
31
+ tracked_dict = {
32
+ 'x': np.linspace(-3, 3, 100),
33
+ 'kde': np.exp(-np.linspace(-3, 3, 100)**2/2),
34
+ 'n': 500
35
+ }
36
+ id = 'test_kde'
37
+ df = _format_plot_kde(id, tracked_dict, {})
38
+
39
+ # Verify columns
40
+ self.assertIn(f"{id}_kde_x", df.columns)
41
+ self.assertIn(f"{id}_kde_density", df.columns)
42
+ self.assertIn(f"{id}_kde_n", df.columns)
43
+
44
+ # Test case 2: Empty tracked_dict
45
+ df = _format_plot_kde(id, {}, {})
46
+ self.assertTrue(df.empty)
47
+
48
+ # Test case 3: Missing 'x' key
49
+ tracked_dict = {'kde': np.exp(-np.linspace(-3, 3, 100)**2/2)}
50
+ df = _format_plot_kde(id, tracked_dict, {})
51
+ self.assertTrue(df.empty)
52
+
53
+ def test_format_plot(self):
54
+ """Test _format_plot function."""
55
+ # Test case 1: Normal input
56
+ tracked_dict = {
57
+ 'plot_df': pd.DataFrame({
58
+ 'x': np.linspace(0, 10, 100),
59
+ 'y': np.sin(np.linspace(0, 10, 100))
60
+ })
61
+ }
62
+ id = 'test_plot'
63
+ df = _format_plot(id, tracked_dict, {})
64
+
65
+ # Verify it returned the DataFrame with added prefix
66
+ self.assertFalse(df.empty)
67
+
68
+ # Test case 2: Empty tracked_dict
69
+ df = _format_plot(id, {}, {})
70
+ self.assertTrue(df.empty)
71
+
72
+ def test_format_plot_ecdf(self):
73
+ """Test _format_plot_ecdf function."""
74
+ # Test case 1: Normal input
75
+ tracked_dict = {
76
+ 'ecdf_df': pd.DataFrame({
77
+ 'x': np.linspace(-3, 3, 100),
78
+ 'ecdf': np.linspace(0, 1, 100)
79
+ })
80
+ }
81
+ id = 'test_ecdf'
82
+ df = _format_plot_ecdf(id, tracked_dict, {})
83
+
84
+ # Verify it returned the DataFrame
85
+ self.assertFalse(df.empty)
86
+
87
+ # Test case 2: Empty tracked_dict
88
+ df = _format_plot_ecdf(id, {}, {})
89
+ self.assertTrue(df.empty)
90
+
91
+ def test_format_plot_heatmap(self):
92
+ """Test _format_plot_heatmap function."""
93
+ # Test case 1: Normal input with labels
94
+ data = np.random.rand(3, 4)
95
+ x_labels = ['A', 'B', 'C']
96
+ y_labels = ['W', 'X', 'Y', 'Z']
97
+
98
+ tracked_dict = {
99
+ 'data': data,
100
+ 'x_labels': x_labels,
101
+ 'y_labels': y_labels
102
+ }
103
+ id = 'test_heatmap'
104
+ df = _format_plot_heatmap(id, tracked_dict, {})
105
+
106
+ # Verify it returned the DataFrame with the expected shape
107
+ self.assertFalse(df.empty)
108
+ self.assertEqual(df.shape[0], 12) # 3 rows * 4 columns = 12 cells
109
+ # We should have 5 columns: row, col, value, row_label, col_label
110
+ self.assertEqual(df.shape[1], 5)
111
+
112
+ # Test case 2: No labels
113
+ tracked_dict = {'data': data}
114
+ df = _format_plot_heatmap(id, tracked_dict, {})
115
+ self.assertFalse(df.empty)
116
+
117
+ # Test case 3: Empty tracked_dict
118
+ df = _format_plot_heatmap(id, {}, {})
119
+ self.assertTrue(df.empty)
120
+
121
+ def test_format_plot_violin(self):
122
+ """Test _format_plot_violin function."""
123
+ # Test case 1: List data
124
+ data = [np.random.normal(0, 1, 100), np.random.normal(2, 1, 100)]
125
+ labels = ['Group A', 'Group B']
126
+
127
+ tracked_dict = {
128
+ 'data': data,
129
+ 'labels': labels
130
+ }
131
+ id = 'test_violin'
132
+ df = _format_plot_violin(id, tracked_dict, {})
133
+
134
+ # Verify it returned the DataFrame
135
+ self.assertFalse(df.empty)
136
+
137
+ # Test case 2: DataFrame data
138
+ data_df = pd.DataFrame({
139
+ 'values': np.concatenate([np.random.normal(0, 1, 100), np.random.normal(2, 1, 100)]),
140
+ 'group': ['A'] * 100 + ['B'] * 100
141
+ })
142
+ tracked_dict = {
143
+ 'data': data_df,
144
+ 'x': 'group',
145
+ 'y': 'values'
146
+ }
147
+ df = _format_plot_violin(id, tracked_dict, {})
148
+ self.assertFalse(df.empty)
149
+
150
+ # Test case 3: Empty tracked_dict
151
+ df = _format_plot_violin(id, {}, {})
152
+ self.assertTrue(df.empty)
153
+
154
+ def test_format_plot_shaded_line(self):
155
+ """Test _format_plot_shaded_line function."""
156
+ # Test case 1: Normal input
157
+ tracked_dict = {
158
+ 'plot_df': pd.DataFrame({
159
+ 'x': np.linspace(0, 10, 100),
160
+ 'y_lower': np.sin(np.linspace(0, 10, 100)) - 0.2,
161
+ 'y_middle': np.sin(np.linspace(0, 10, 100)),
162
+ 'y_upper': np.sin(np.linspace(0, 10, 100)) + 0.2
163
+ })
164
+ }
165
+ id = 'test_shaded'
166
+ df = _format_plot_shaded_line(id, tracked_dict, {})
167
+
168
+ # Verify it returned the DataFrame
169
+ self.assertFalse(df.empty)
170
+
171
+ # Test case 2: Empty tracked_dict
172
+ df = _format_plot_shaded_line(id, {}, {})
173
+ self.assertTrue(df.empty)
174
+
175
+ def test_format_plot_scatter_hist(self):
176
+ """Test _format_plot_scatter_hist function."""
177
+ # Test case 1: Normal input
178
+ tracked_dict = {
179
+ 'x': np.random.normal(0, 1, 100),
180
+ 'y': np.random.normal(0, 1, 100),
181
+ 'hist_x': np.random.rand(10),
182
+ 'hist_y': np.random.rand(10),
183
+ 'bin_edges_x': np.linspace(-3, 3, 11),
184
+ 'bin_edges_y': np.linspace(-3, 3, 11)
185
+ }
186
+ id = 'test_scatter_hist'
187
+ df = _format_plot_scatter_hist(id, tracked_dict, {})
188
+
189
+ # Verify it returned the DataFrame with expected columns
190
+ self.assertFalse(df.empty)
191
+ self.assertTrue(any(col.startswith(f"{id}_scatter_hist_x") for col in df.columns))
192
+ self.assertTrue(any(col.startswith(f"{id}_scatter_hist_y") for col in df.columns))
193
+
194
+ # Test case 2: Missing keys
195
+ tracked_dict = {
196
+ 'x': np.random.normal(0, 1, 100),
197
+ 'y': np.random.normal(0, 1, 100)
198
+ }
199
+ df = _format_plot_scatter_hist(id, tracked_dict, {})
200
+ self.assertFalse(df.empty) # Should still work with just x,y
201
+
202
+ # Test case 3: Empty tracked_dict
203
+ df = _format_plot_scatter_hist(id, {}, {})
204
+ self.assertTrue(df.empty)
205
+
206
+
207
+ if __name__ == '__main__':
208
+ unittest.main()
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env python3
2
+ """Font configuration for matplotlib figures."""
3
+
4
+ import os
5
+
6
+ import matplotlib as mpl
7
+ import matplotlib.font_manager as fm
8
+
9
+
10
+ def configure_arial_font():
11
+ """Configure Arial font for matplotlib if available.
12
+
13
+ Returns
14
+ -------
15
+ bool
16
+ True if Arial was successfully configured, False otherwise.
17
+ """
18
+ arial_enabled = False
19
+
20
+ # Try to find Arial
21
+ try:
22
+ fm.findfont("Arial", fallback_to_default=False)
23
+ arial_enabled = True
24
+ except Exception:
25
+ # Search for Arial font files and register them
26
+ arial_paths = [
27
+ f
28
+ for f in fm.findSystemFonts()
29
+ if os.path.basename(f).lower().startswith("arial")
30
+ ]
31
+
32
+ if arial_paths:
33
+ for path in arial_paths:
34
+ try:
35
+ fm.fontManager.addfont(path)
36
+ except Exception:
37
+ pass
38
+
39
+ # Verify Arial is now available
40
+ try:
41
+ fm.findfont("Arial", fallback_to_default=False)
42
+ arial_enabled = True
43
+ except Exception:
44
+ pass
45
+
46
+ # Configure matplotlib to use Arial if available
47
+ if arial_enabled:
48
+ mpl.rcParams["font.family"] = "Arial"
49
+ mpl.rcParams["font.sans-serif"] = [
50
+ "Arial",
51
+ "Helvetica",
52
+ "DejaVu Sans",
53
+ "Liberation Sans",
54
+ ]
55
+ else:
56
+ # Warn about missing Arial
57
+ from scitex import logging as _logging
58
+
59
+ _logger = _logging.getLogger(__name__)
60
+ _logger.warning(
61
+ "Arial font not found. Using fallback fonts (Helvetica/DejaVu Sans). "
62
+ "For publication figures with Arial: sudo apt-get install ttf-mscorefonts-installer && fc-cache -fv"
63
+ )
64
+
65
+ return arial_enabled
66
+
67
+
68
+ # Configure fonts at module import
69
+ _arial_enabled = configure_arial_font()
70
+
71
+ # EOF