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
@@ -0,0 +1,445 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_tables/_latex/_validator.py
4
+
5
+ """LaTeX validation for error detection and suggestions."""
6
+
7
+ import re
8
+ import subprocess
9
+ import tempfile
10
+ from dataclasses import dataclass, field
11
+ from enum import Enum
12
+ from pathlib import Path
13
+ from typing import List, Optional, Tuple
14
+
15
+
16
+ class ErrorSeverity(Enum):
17
+ """Error severity levels."""
18
+
19
+ ERROR = "error"
20
+ WARNING = "warning"
21
+ INFO = "info"
22
+
23
+
24
+ @dataclass
25
+ class LaTeXError:
26
+ """A LaTeX validation error.
27
+
28
+ Attributes:
29
+ line: Line number (1-indexed)
30
+ column: Column number (1-indexed)
31
+ message: Error description
32
+ severity: Error severity
33
+ code: Error code (e.g., E001, W001)
34
+ suggestion: Fix suggestion
35
+ context: Line content for context
36
+ """
37
+
38
+ line: int
39
+ column: int
40
+ message: str
41
+ severity: ErrorSeverity
42
+ code: str
43
+ suggestion: Optional[str] = None
44
+ context: Optional[str] = None
45
+
46
+
47
+ @dataclass
48
+ class ValidationResult:
49
+ """Result of LaTeX validation.
50
+
51
+ Attributes:
52
+ is_valid: Whether the LaTeX is valid (no errors)
53
+ errors: List of errors
54
+ warnings: List of warnings
55
+ info: List of info messages
56
+ """
57
+
58
+ is_valid: bool = True
59
+ errors: List[LaTeXError] = field(default_factory=list)
60
+ warnings: List[LaTeXError] = field(default_factory=list)
61
+ info: List[LaTeXError] = field(default_factory=list)
62
+
63
+ def add_error(self, error: LaTeXError) -> None:
64
+ """Add an error to the appropriate list."""
65
+ if error.severity == ErrorSeverity.ERROR:
66
+ self.errors.append(error)
67
+ self.is_valid = False
68
+ elif error.severity == ErrorSeverity.WARNING:
69
+ self.warnings.append(error)
70
+ else:
71
+ self.info.append(error)
72
+
73
+ @property
74
+ def all_issues(self) -> List[LaTeXError]:
75
+ """Get all issues sorted by line number."""
76
+ return sorted(
77
+ self.errors + self.warnings + self.info,
78
+ key=lambda e: (e.line, e.column),
79
+ )
80
+
81
+
82
+ def validate_latex(
83
+ latex_code: str,
84
+ level: str = "syntax",
85
+ ) -> ValidationResult:
86
+ """Validate LaTeX code.
87
+
88
+ Args:
89
+ latex_code: LaTeX source code
90
+ level: Validation level ("syntax", "semantic", "compile")
91
+
92
+ Returns:
93
+ ValidationResult with errors and warnings
94
+ """
95
+ result = ValidationResult()
96
+ lines = latex_code.split("\n")
97
+
98
+ # Always do syntax validation
99
+ _validate_syntax(lines, result)
100
+
101
+ # Semantic validation
102
+ if level in ("semantic", "compile"):
103
+ _validate_semantic(lines, result)
104
+
105
+ # Compilation validation (optional, requires pdflatex)
106
+ if level == "compile":
107
+ _validate_compilation(latex_code, result)
108
+
109
+ return result
110
+
111
+
112
+ def _validate_syntax(lines: List[str], result: ValidationResult) -> None:
113
+ """Check for syntax errors.
114
+
115
+ Args:
116
+ lines: Source lines
117
+ result: ValidationResult to populate
118
+ """
119
+ # Track brace balance
120
+ brace_stack = [] # List of (line, col, char)
121
+ env_stack = [] # List of (line, env_name)
122
+
123
+ for line_num, line in enumerate(lines, 1):
124
+ # Skip comments
125
+ line_no_comment = line.split("%")[0] if "%" in line and "\\%" not in line.split("%")[0] else line
126
+
127
+ # Check brace balance
128
+ for col, char in enumerate(line_no_comment, 1):
129
+ if char == "{":
130
+ # Check if escaped
131
+ if col > 1 and line_no_comment[col - 2] == "\\":
132
+ continue
133
+ brace_stack.append((line_num, col, char))
134
+ elif char == "}":
135
+ if col > 1 and line_no_comment[col - 2] == "\\":
136
+ continue
137
+ if brace_stack:
138
+ brace_stack.pop()
139
+ else:
140
+ result.add_error(
141
+ LaTeXError(
142
+ line=line_num,
143
+ column=col,
144
+ message="Unmatched closing brace '}'",
145
+ severity=ErrorSeverity.ERROR,
146
+ code="E001",
147
+ suggestion="Remove the extra '}' or add a matching '{'",
148
+ context=line,
149
+ )
150
+ )
151
+
152
+ # Check environment matching
153
+ begin_match = re.search(r"\\begin\{(\w+)\}", line_no_comment)
154
+ if begin_match:
155
+ env_name = begin_match.group(1)
156
+ env_stack.append((line_num, env_name))
157
+
158
+ end_match = re.search(r"\\end\{(\w+)\}", line_no_comment)
159
+ if end_match:
160
+ env_name = end_match.group(1)
161
+ if env_stack:
162
+ _, expected_env = env_stack[-1]
163
+ if env_name == expected_env:
164
+ env_stack.pop()
165
+ else:
166
+ result.add_error(
167
+ LaTeXError(
168
+ line=line_num,
169
+ column=end_match.start() + 1,
170
+ message=f"Environment mismatch: expected \\end{{{expected_env}}}, got \\end{{{env_name}}}",
171
+ severity=ErrorSeverity.ERROR,
172
+ code="E002",
173
+ suggestion=f"Change to \\end{{{expected_env}}} or fix the \\begin command",
174
+ context=line,
175
+ )
176
+ )
177
+ else:
178
+ result.add_error(
179
+ LaTeXError(
180
+ line=line_num,
181
+ column=end_match.start() + 1,
182
+ message=f"\\end{{{env_name}}} without matching \\begin{{{env_name}}}",
183
+ severity=ErrorSeverity.ERROR,
184
+ code="E003",
185
+ suggestion=f"Add \\begin{{{env_name}}} before this or remove the \\end command",
186
+ context=line,
187
+ )
188
+ )
189
+
190
+ # Check for common typos
191
+ _check_common_typos(line_num, line, result)
192
+
193
+ # Report unclosed braces
194
+ for line_num, col, _ in brace_stack:
195
+ result.add_error(
196
+ LaTeXError(
197
+ line=line_num,
198
+ column=col,
199
+ message="Unclosed brace '{'",
200
+ severity=ErrorSeverity.ERROR,
201
+ code="E004",
202
+ suggestion="Add a matching '}'",
203
+ context=lines[line_num - 1] if line_num <= len(lines) else "",
204
+ )
205
+ )
206
+
207
+ # Report unclosed environments
208
+ for line_num, env_name in env_stack:
209
+ result.add_error(
210
+ LaTeXError(
211
+ line=line_num,
212
+ column=1,
213
+ message=f"Unclosed environment: \\begin{{{env_name}}} without \\end{{{env_name}}}",
214
+ severity=ErrorSeverity.ERROR,
215
+ code="E005",
216
+ suggestion=f"Add \\end{{{env_name}}} at the end",
217
+ context=lines[line_num - 1] if line_num <= len(lines) else "",
218
+ )
219
+ )
220
+
221
+
222
+ def _check_common_typos(line_num: int, line: str, result: ValidationResult) -> None:
223
+ """Check for common LaTeX typos.
224
+
225
+ Args:
226
+ line_num: Line number
227
+ line: Line content
228
+ result: ValidationResult to populate
229
+ """
230
+ typos = [
231
+ (r"\\beign\{", "\\begin{", "W001"),
232
+ (r"\\beigin\{", "\\begin{", "W001"),
233
+ (r"\\ened\{", "\\end{", "W002"),
234
+ (r"\\ceneter", "\\centering", "W003"),
235
+ (r"\\includegraphcis", "\\includegraphics", "W004"),
236
+ (r"\\captoin", "\\caption", "W005"),
237
+ (r"\\lable\{", "\\label{", "W006"),
238
+ (r"\\toperule", "\\toprule", "W007"),
239
+ (r"\\bottomrul", "\\bottomrule", "W008"),
240
+ (r"\\midruel", "\\midrule", "W009"),
241
+ ]
242
+
243
+ for pattern, correct, code in typos:
244
+ match = re.search(pattern, line, re.IGNORECASE)
245
+ if match:
246
+ result.add_error(
247
+ LaTeXError(
248
+ line=line_num,
249
+ column=match.start() + 1,
250
+ message=f"Possible typo: '{match.group()}' should be '{correct}'",
251
+ severity=ErrorSeverity.WARNING,
252
+ code=code,
253
+ suggestion=f"Replace with '{correct}'",
254
+ context=line,
255
+ )
256
+ )
257
+
258
+
259
+ def _validate_semantic(lines: List[str], result: ValidationResult) -> None:
260
+ """Check for semantic issues.
261
+
262
+ Args:
263
+ lines: Source lines
264
+ result: ValidationResult to populate
265
+ """
266
+ labels = set()
267
+ refs = set()
268
+ has_caption = False
269
+ in_float = False
270
+
271
+ for line_num, line in enumerate(lines, 1):
272
+ # Track float environments
273
+ if re.search(r"\\begin\{(figure|table)\}", line):
274
+ in_float = True
275
+ has_caption = False
276
+
277
+ if re.search(r"\\end\{(figure|table)\}", line):
278
+ if in_float and not has_caption:
279
+ result.add_error(
280
+ LaTeXError(
281
+ line=line_num,
282
+ column=1,
283
+ message="Float environment without caption",
284
+ severity=ErrorSeverity.WARNING,
285
+ code="W010",
286
+ suggestion="Add \\caption{...} to the figure/table",
287
+ context=line,
288
+ )
289
+ )
290
+ in_float = False
291
+
292
+ # Track captions
293
+ if "\\caption" in line:
294
+ has_caption = True
295
+
296
+ # Collect labels
297
+ label_match = re.search(r"\\label\{([^}]+)\}", line)
298
+ if label_match:
299
+ label = label_match.group(1)
300
+ if label in labels:
301
+ result.add_error(
302
+ LaTeXError(
303
+ line=line_num,
304
+ column=label_match.start() + 1,
305
+ message=f"Duplicate label: '{label}'",
306
+ severity=ErrorSeverity.WARNING,
307
+ code="W011",
308
+ suggestion="Use a unique label",
309
+ context=line,
310
+ )
311
+ )
312
+ labels.add(label)
313
+
314
+ # Collect refs
315
+ for ref_match in re.finditer(r"\\ref\{([^}]+)\}", line):
316
+ refs.add(ref_match.group(1))
317
+
318
+ # Check for undefined refs (only warning since refs might be in other files)
319
+ # for ref in refs - labels:
320
+ # result.add_error(...)
321
+
322
+
323
+ def _validate_compilation(latex_code: str, result: ValidationResult) -> None:
324
+ """Attempt to compile LaTeX and capture errors.
325
+
326
+ Args:
327
+ latex_code: LaTeX source code
328
+ result: ValidationResult to populate
329
+ """
330
+ # Create a minimal document if needed
331
+ if "\\documentclass" not in latex_code:
332
+ latex_code = _wrap_in_document(latex_code)
333
+
334
+ try:
335
+ with tempfile.TemporaryDirectory() as tmpdir:
336
+ tex_path = Path(tmpdir) / "test.tex"
337
+ tex_path.write_text(latex_code, encoding="utf-8")
338
+
339
+ proc = subprocess.run(
340
+ ["pdflatex", "-interaction=nonstopmode", "-halt-on-error", str(tex_path)],
341
+ cwd=tmpdir,
342
+ capture_output=True,
343
+ text=True,
344
+ timeout=30,
345
+ )
346
+
347
+ if proc.returncode != 0:
348
+ # Parse pdflatex output for errors
349
+ _parse_pdflatex_output(proc.stdout, result)
350
+
351
+ except FileNotFoundError:
352
+ result.add_error(
353
+ LaTeXError(
354
+ line=0,
355
+ column=0,
356
+ message="pdflatex not found - compilation check skipped",
357
+ severity=ErrorSeverity.INFO,
358
+ code="I001",
359
+ suggestion="Install TeX Live or MiKTeX for compilation validation",
360
+ )
361
+ )
362
+ except subprocess.TimeoutExpired:
363
+ result.add_error(
364
+ LaTeXError(
365
+ line=0,
366
+ column=0,
367
+ message="Compilation timed out",
368
+ severity=ErrorSeverity.WARNING,
369
+ code="W012",
370
+ suggestion="Check for infinite loops in macros",
371
+ )
372
+ )
373
+ except Exception as e:
374
+ result.add_error(
375
+ LaTeXError(
376
+ line=0,
377
+ column=0,
378
+ message=f"Compilation error: {str(e)}",
379
+ severity=ErrorSeverity.INFO,
380
+ code="I002",
381
+ )
382
+ )
383
+
384
+
385
+ def _wrap_in_document(content: str) -> str:
386
+ """Wrap content in a minimal LaTeX document.
387
+
388
+ Args:
389
+ content: LaTeX content
390
+
391
+ Returns:
392
+ Complete document string
393
+ """
394
+ return f"""\\documentclass{{article}}
395
+ \\usepackage{{graphicx}}
396
+ \\usepackage{{booktabs}}
397
+ \\usepackage{{amsmath}}
398
+ \\begin{{document}}
399
+ {content}
400
+ \\end{{document}}
401
+ """
402
+
403
+
404
+ def _parse_pdflatex_output(output: str, result: ValidationResult) -> None:
405
+ """Parse pdflatex output for errors.
406
+
407
+ Args:
408
+ output: pdflatex stdout
409
+ result: ValidationResult to populate
410
+ """
411
+ # Look for error lines
412
+ error_pattern = re.compile(r"^! (.+)$", re.MULTILINE)
413
+ line_pattern = re.compile(r"^l\.(\d+) (.*)$", re.MULTILINE)
414
+
415
+ for match in error_pattern.finditer(output):
416
+ error_msg = match.group(1)
417
+
418
+ # Try to find line number
419
+ line_num = 0
420
+ context = ""
421
+ line_match = line_pattern.search(output, match.end())
422
+ if line_match:
423
+ line_num = int(line_match.group(1))
424
+ context = line_match.group(2)
425
+
426
+ result.add_error(
427
+ LaTeXError(
428
+ line=line_num,
429
+ column=1,
430
+ message=error_msg,
431
+ severity=ErrorSeverity.ERROR,
432
+ code="C001",
433
+ context=context,
434
+ )
435
+ )
436
+
437
+
438
+ __all__ = [
439
+ "ErrorSeverity",
440
+ "LaTeXError",
441
+ "ValidationResult",
442
+ "validate_latex",
443
+ ]
444
+
445
+ # EOF
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-12-21
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/fts/_kinds/_text/__init__.py
4
+
5
+ """Text kind - Text annotation elements.
6
+
7
+ A text bundle contains styled text (no payload data).
8
+ Used for titles, labels, annotations in figures.
9
+
10
+ Structure:
11
+ - canonical/node.json: Text content and styling
12
+ - No payload (text is in node specification)
13
+ """
14
+
15
+ from typing import Any, Optional, TYPE_CHECKING
16
+
17
+ if TYPE_CHECKING:
18
+ from matplotlib.axes import Axes
19
+
20
+
21
+ def render_text(
22
+ ax: "Axes",
23
+ text: str,
24
+ x: float,
25
+ y: float,
26
+ fontsize: Optional[float] = None,
27
+ fontweight: Optional[str] = None,
28
+ color: Optional[str] = None,
29
+ ha: str = "center",
30
+ va: str = "center",
31
+ rotation: float = 0,
32
+ **kwargs: Any,
33
+ ) -> None:
34
+ """Render text annotation on axes.
35
+
36
+ Parameters
37
+ ----------
38
+ ax : Axes
39
+ Matplotlib axes to render on
40
+ text : str
41
+ Text content to display
42
+ x, y : float
43
+ Position in axes coordinates
44
+ fontsize : float, optional
45
+ Font size in points
46
+ fontweight : str, optional
47
+ Font weight (normal, bold, etc.)
48
+ color : str, optional
49
+ Text color
50
+ ha : str
51
+ Horizontal alignment (left, center, right)
52
+ va : str
53
+ Vertical alignment (top, center, bottom, baseline)
54
+ rotation : float
55
+ Rotation angle in degrees
56
+ **kwargs : Any
57
+ Additional kwargs passed to ax.text()
58
+ """
59
+ text_kwargs = {
60
+ "ha": ha,
61
+ "va": va,
62
+ "rotation": rotation,
63
+ }
64
+ if fontsize is not None:
65
+ text_kwargs["fontsize"] = fontsize
66
+ if fontweight is not None:
67
+ text_kwargs["fontweight"] = fontweight
68
+ if color is not None:
69
+ text_kwargs["color"] = color
70
+ text_kwargs.update(kwargs)
71
+
72
+ ax.text(x, y, text, transform=ax.transAxes, **text_kwargs)
73
+
74
+
75
+ __all__ = ["render_text"]
76
+
77
+ # EOF
@@ -0,0 +1,75 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://scitex.ai/schemas/fts/data_info.schema.json",
4
+ "title": "FTS Data Info Schema",
5
+ "description": "Schema for data file metadata (column meanings, units, dtypes, source)",
6
+ "type": "object",
7
+ "required": ["columns"],
8
+ "properties": {
9
+ "source": {
10
+ "type": "object",
11
+ "description": "Original data source information",
12
+ "properties": {
13
+ "path": {"type": "string"},
14
+ "sha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
15
+ "created_at": {"type": "string", "format": "date-time"},
16
+ "description": {"type": "string"}
17
+ }
18
+ },
19
+ "format": {
20
+ "type": "object",
21
+ "properties": {
22
+ "type": {"type": "string", "enum": ["csv", "tsv", "parquet", "json"]},
23
+ "encoding": {"type": "string", "default": "utf-8"},
24
+ "delimiter": {"type": "string", "default": ","},
25
+ "header_row": {"type": "integer", "default": 0}
26
+ }
27
+ },
28
+ "shape": {
29
+ "type": "object",
30
+ "properties": {
31
+ "rows": {"type": "integer", "minimum": 0},
32
+ "columns": {"type": "integer", "minimum": 0}
33
+ }
34
+ },
35
+ "columns": {
36
+ "type": "array",
37
+ "description": "Column definitions",
38
+ "items": {
39
+ "type": "object",
40
+ "required": ["name", "dtype"],
41
+ "properties": {
42
+ "name": {"type": "string"},
43
+ "dtype": {
44
+ "type": "string",
45
+ "enum": ["float64", "int64", "string", "bool", "datetime", "category"]
46
+ },
47
+ "description": {"type": "string"},
48
+ "unit": {"type": "string", "description": "Physical unit (e.g., 'mV', 'ms', 'Hz')"},
49
+ "role": {
50
+ "type": "string",
51
+ "enum": ["x", "y", "group", "subject", "condition", "time", "error", "weight", "other"]
52
+ },
53
+ "missing_count": {"type": "integer", "minimum": 0},
54
+ "unique_count": {"type": "integer", "minimum": 0},
55
+ "min": {"type": "number"},
56
+ "max": {"type": "number"},
57
+ "mean": {"type": "number"},
58
+ "std": {"type": "number"},
59
+ "categories": {
60
+ "type": "array",
61
+ "items": {"type": "string"},
62
+ "description": "Unique values for categorical columns"
63
+ }
64
+ }
65
+ }
66
+ },
67
+ "index": {
68
+ "type": "object",
69
+ "properties": {
70
+ "name": {"type": "string"},
71
+ "dtype": {"type": "string"}
72
+ }
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,90 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://scitex.ai/schemas/fts/encoding.schema.json",
4
+ "title": "FTS Encoding Schema",
5
+ "description": "Schema for data-to-visual channel mappings",
6
+ "type": "object",
7
+ "properties": {
8
+ "traces": {
9
+ "type": "array",
10
+ "description": "List of trace encodings",
11
+ "items": {
12
+ "type": "object",
13
+ "required": ["trace_id"],
14
+ "properties": {
15
+ "trace_id": {
16
+ "type": "string",
17
+ "description": "Unique identifier for this trace"
18
+ },
19
+ "data_ref": {
20
+ "type": "string",
21
+ "description": "Path to data file"
22
+ },
23
+ "x": {
24
+ "$ref": "#/definitions/channelEncoding"
25
+ },
26
+ "y": {
27
+ "$ref": "#/definitions/channelEncoding"
28
+ },
29
+ "color": {
30
+ "$ref": "#/definitions/channelEncoding"
31
+ },
32
+ "size": {
33
+ "$ref": "#/definitions/channelEncoding"
34
+ },
35
+ "shape": {
36
+ "$ref": "#/definitions/channelEncoding"
37
+ },
38
+ "group": {
39
+ "$ref": "#/definitions/channelEncoding"
40
+ },
41
+ "error_y": {
42
+ "type": "object",
43
+ "properties": {
44
+ "column": {"type": "string"},
45
+ "type": {"type": "string", "enum": ["SEM", "SD", "CI95", "raw"]}
46
+ }
47
+ }
48
+ }
49
+ }
50
+ },
51
+ "legends": {
52
+ "type": "object",
53
+ "description": "Legend encoding definitions",
54
+ "properties": {
55
+ "color": {
56
+ "type": "object",
57
+ "additionalProperties": {"type": "string"}
58
+ },
59
+ "linestyle": {
60
+ "type": "object",
61
+ "additionalProperties": {"type": "string"}
62
+ },
63
+ "marker": {
64
+ "type": "object",
65
+ "additionalProperties": {"type": "string"}
66
+ }
67
+ }
68
+ }
69
+ },
70
+ "definitions": {
71
+ "channelEncoding": {
72
+ "type": "object",
73
+ "properties": {
74
+ "column": {
75
+ "type": "string",
76
+ "description": "Column name in data file"
77
+ },
78
+ "scale": {
79
+ "type": "string",
80
+ "enum": ["linear", "log", "ordinal", "categorical"],
81
+ "default": "linear"
82
+ },
83
+ "aggregate": {
84
+ "type": "string",
85
+ "enum": ["none", "mean", "median", "sum", "count"]
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }