scitex 2.8.1__py3-none-any.whl → 2.10.2__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.
- scitex/__init__.py +15 -7
- scitex/__version__.py +1 -2
- scitex/_install_guide.py +250 -0
- scitex/_optional_deps.py +206 -39
- scitex/ai/_gen_ai/_Groq.py +2 -4
- scitex/ai/_gen_ai/_OpenAI.py +5 -2
- scitex/ai/_gen_ai/_Perplexity.py +20 -6
- scitex/audio/__init__.py +24 -15
- scitex/audio/_cross_process_lock.py +139 -0
- scitex/audio/_mcp_handlers.py +256 -0
- scitex/audio/_mcp_tool_schemas.py +203 -0
- scitex/audio/engines/elevenlabs_engine.py +5 -2
- scitex/audio/mcp_server.py +98 -457
- scitex/bridge/__init__.py +30 -19
- scitex/bridge/_figrecipe.py +245 -0
- scitex/bridge/_helpers.py +2 -1
- scitex/bridge/_plt_vis.py +23 -10
- scitex/bridge/_stats_plt.py +18 -5
- scitex/bridge/_stats_vis.py +16 -2
- scitex/browser/__init__.py +84 -44
- scitex/browser/automation/__init__.py +5 -1
- scitex/browser/core/BrowserMixin.py +17 -4
- scitex/browser/core/__init__.py +11 -2
- scitex/browser/remote/CaptchaHandler.py +1 -1
- scitex/browser/remote/ZenRowsAPIClient.py +1 -1
- scitex/capture/grid.py +487 -0
- scitex/capture/mcp_handlers.py +401 -0
- scitex/capture/mcp_tool_defs.py +192 -0
- scitex/capture/mcp_tools.py +241 -0
- scitex/capture/mcp_utils.py +30 -0
- scitex/cli/convert.py +421 -0
- scitex/cli/main.py +6 -4
- scitex/datetime/__init__.py +46 -0
- scitex/datetime/_linspace.py +100 -0
- scitex/datetime/_normalize_timestamp.py +306 -0
- scitex/db/_delete_duplicates.py +4 -4
- scitex/db/_sqlite3/_delete_duplicates.py +11 -2
- scitex/dev/plt/__init__.py +61 -62
- scitex/dev/plt/demo_plotters/__init__.py +0 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
- scitex/dev/plt/mpl/get_dir_ax.py +46 -0
- scitex/dev/plt/mpl/get_signatures.py +176 -0
- scitex/dev/plt/mpl/get_signatures_details.py +522 -0
- scitex/dict/_pop_keys.py +1 -7
- scitex/dsp/__init__.py +15 -10
- scitex/dsp/add_noise.py +5 -2
- scitex/dsp/example.py +35 -22
- scitex/dsp/filt.py +8 -3
- scitex/dsp/reference.py +3 -2
- scitex/dsp/utils/__init__.py +2 -1
- scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
- scitex/dt/__init__.py +39 -2
- scitex/errors.py +82 -521
- scitex/fig/__init__.py +4 -4
- scitex/fig/editor/edit/panel_loader.py +1 -1
- scitex/fig/io/_bundle.py +7 -7
- scitex/fts/README.md +262 -0
- scitex/fts/TODO.md +66 -0
- scitex/fts/__init__.py +90 -0
- scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
- scitex/fts/_bundle/_FTS.py +657 -0
- scitex/fts/_bundle/__init__.py +38 -0
- scitex/fts/_bundle/_children.py +216 -0
- scitex/fts/_bundle/_conversion/__init__.py +15 -0
- scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
- scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
- scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
- scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
- scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
- scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
- scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
- scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
- scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
- scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
- scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
- scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
- scitex/fts/_bundle/_extractors/__init__.py +32 -0
- scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
- scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
- scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
- scitex/fts/_bundle/_loader.py +134 -0
- scitex/fts/_bundle/_mpl_helpers.py +389 -0
- scitex/fts/_bundle/_saver.py +269 -0
- scitex/fts/_bundle/_storage.py +200 -0
- scitex/fts/_bundle/_utils/__init__.py +55 -0
- scitex/fts/_bundle/_utils/_const.py +26 -0
- scitex/fts/_bundle/_utils/_errors.py +73 -0
- scitex/fts/_bundle/_utils/_generate.py +21 -0
- scitex/fts/_bundle/_utils/_types.py +76 -0
- scitex/fts/_bundle/_validation.py +434 -0
- scitex/fts/_bundle/_zipbundle.py +165 -0
- scitex/fts/_fig/__init__.py +22 -0
- scitex/fts/_fig/_backend/__init__.py +53 -0
- scitex/fts/_fig/_backend/_export.py +165 -0
- scitex/fts/_fig/_backend/_parser.py +188 -0
- scitex/fts/_fig/_backend/_render.py +538 -0
- scitex/fts/_fig/_composite.py +345 -0
- scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
- scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_fig/_dataclasses/__init__.py +47 -0
- scitex/fts/_fig/_editor/__init__.py +14 -0
- scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
- scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
- scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
- scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
- scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
- scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
- scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
- scitex/fts/_fig/_editor/_defaults.py +300 -0
- scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
- scitex/fts/_fig/_models/_Annotations.py +115 -0
- scitex/fts/_fig/_models/_Axes.py +152 -0
- scitex/fts/_fig/_models/_Figure.py +138 -0
- scitex/fts/_fig/_models/_Guides.py +104 -0
- scitex/fts/_fig/_models/_Plot.py +123 -0
- scitex/fts/_fig/_models/_Styles.py +245 -0
- scitex/fts/_fig/_models/__init__.py +80 -0
- scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
- scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
- scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
- scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_fig/_utils/__init__.py +129 -0
- scitex/fts/_fig/_utils/_auto_layout.py +127 -0
- scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
- scitex/fts/_fig/_utils/_const_sizes.py +48 -0
- scitex/fts/_fig/_utils/_convert_coords.py +77 -0
- scitex/fts/_fig/_utils/_get_template.py +178 -0
- scitex/fts/_fig/_utils/_normalize.py +73 -0
- scitex/fts/_fig/_utils/_plot_layout.py +397 -0
- scitex/fts/_fig/_utils/_validate.py +197 -0
- scitex/fts/_kinds/__init__.py +45 -0
- scitex/fts/_kinds/_figure/__init__.py +19 -0
- scitex/fts/_kinds/_figure/_composite.py +345 -0
- scitex/fts/_kinds/_plot/__init__.py +25 -0
- scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
- scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
- scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
- scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
- scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
- scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
- scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
- scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
- scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
- scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
- scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
- scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
- scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
- scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
- scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
- scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
- scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
- scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
- scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
- scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
- scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
- scitex/fts/_kinds/_shape/__init__.py +141 -0
- scitex/fts/_kinds/_stats/__init__.py +56 -0
- scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_kinds/_table/__init__.py +72 -0
- scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
- scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
- scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
- scitex/fts/_kinds/_table/_latex/_export.py +279 -0
- scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
- scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
- scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
- scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
- scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
- scitex/fts/_kinds/_text/__init__.py +77 -0
- scitex/fts/_schemas/data_info.schema.json +75 -0
- scitex/fts/_schemas/encoding.schema.json +90 -0
- scitex/fts/_schemas/node.schema.json +145 -0
- scitex/fts/_schemas/render_manifest.schema.json +62 -0
- scitex/fts/_schemas/stats.schema.json +132 -0
- scitex/fts/_schemas/theme.schema.json +141 -0
- scitex/fts/_stats/__init__.py +48 -0
- scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_tables/__init__.py +65 -0
- scitex/fts/_tables/_latex/__init__.py +93 -0
- scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
- scitex/fts/_tables/_latex/_editor/_app.py +725 -0
- scitex/fts/_tables/_latex/_export.py +279 -0
- scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
- scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
- scitex/fts/_tables/_latex/_table_exporter.py +362 -0
- scitex/fts/_tables/_latex/_utils.py +369 -0
- scitex/fts/_tables/_latex/_validator.py +445 -0
- scitex/gen/__init__.py +66 -25
- scitex/gen/misc.py +28 -0
- scitex/io/__init__.py +47 -32
- scitex/io/_load.py +87 -36
- scitex/io/_load_modules/__init__.py +10 -7
- scitex/io/_load_modules/_pandas.py +6 -1
- scitex/io/_save.py +299 -1556
- scitex/io/_save_modules/__init__.py +76 -19
- scitex/io/_save_modules/_figure_utils.py +90 -0
- scitex/io/_save_modules/_image_csv.py +497 -0
- scitex/io/_save_modules/_legends.py +91 -0
- scitex/io/_save_modules/_pltz_bundle.py +356 -0
- scitex/io/_save_modules/_pltz_stx.py +536 -0
- scitex/io/_save_modules/_stx_bundle.py +104 -0
- scitex/io/_save_modules/_symlink.py +96 -0
- scitex/io/_save_modules/_yaml.py +1 -1
- scitex/io/_save_modules/_zarr.py +64 -18
- scitex/io/bundle/README.md +212 -0
- scitex/io/bundle/__init__.py +110 -0
- scitex/io/{_bundle.py → bundle/_core.py} +168 -97
- scitex/io/bundle/_nested.py +713 -0
- scitex/io/bundle/_types.py +74 -0
- scitex/io/{_zip_bundle.py → bundle/_zip.py} +93 -45
- scitex/io/utils/h5_to_zarr.py +1 -1
- scitex/logging/__init__.py +108 -13
- scitex/logging/_errors.py +508 -0
- scitex/logging/_formatters.py +30 -6
- scitex/logging/_warnings.py +261 -0
- scitex/plt/__init__.py +4 -1
- scitex/plt/_figrecipe.py +236 -0
- scitex/plt/_subplots/_AxisWrapper.py +6 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
- scitex/plt/_subplots/_FigWrapper.py +15 -0
- scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
- scitex/plt/_subplots/_export_as_csv.py +11 -0
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
- scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
- scitex/plt/_subplots/_fonts.py +71 -0
- scitex/plt/_subplots/_mm_layout.py +282 -0
- scitex/plt/gallery/__init__.py +99 -2
- scitex/plt/styles/_plot_postprocess.py +3 -1
- scitex/plt/utils/_configure_mpl.py +16 -19
- scitex/repro/_RandomStateManager.py +13 -8
- scitex/resource/__init__.py +19 -1
- scitex/resource/_utils/_get_env_info.py +13 -25
- scitex/schema/__init__.py +149 -160
- scitex/schema/_encoding.py +273 -0
- scitex/schema/_figure_elements.py +406 -0
- scitex/schema/_theme.py +360 -0
- scitex/schema/_validation.py +0 -98
- scitex/scholar/__init__.py +56 -14
- scitex/scholar/auth/ScholarAuthManager.py +1 -1
- scitex/scholar/auth/__init__.py +11 -2
- scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
- scitex/scholar/config/ScholarConfig.py +1 -1
- scitex/scholar/core/Scholar.py +1 -1
- scitex/session/_decorator.py +18 -16
- scitex/session/_lifecycle.py +9 -11
- scitex/session/template.py +9 -8
- scitex/sh/test_sh.py +72 -0
- scitex/sh/test_sh_simple.py +61 -0
- scitex/stats/__init__.py +221 -97
- scitex/stats/_schema.py +21 -22
- scitex/stats/descriptive/_circular.py +212 -351
- scitex/stats/descriptive/_describe.py +81 -132
- scitex/stats/descriptive/_nan.py +205 -433
- scitex/stats/descriptive/_real.py +127 -141
- scitex/str/_format_plot_text.py +5 -5
- scitex/str/_latex.py +26 -84
- scitex/str/_latex_fallback.py +53 -47
- scitex/web/_search_pubmed.py +5 -4
- scitex/writer/tests/test_diff_between.py +451 -0
- scitex/writer/tests/test_document_section.py +311 -0
- scitex/writer/tests/test_document_workflow.py +393 -0
- scitex/writer/tests/test_writer.py +361 -0
- scitex/writer/tests/test_writer_integration.py +303 -0
- {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/METADATA +368 -183
- {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/RECORD +412 -97
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
- {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/WHEEL +0 -0
- {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/entry_points.txt +0 -0
- {scitex-2.8.1.dist-info → scitex-2.10.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-12-21 06:10:24 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fts/_bundle/_children.py
|
|
5
|
+
|
|
6
|
+
# Timestamp: 2025-12-20
|
|
7
|
+
|
|
8
|
+
"""Child embedding for FTS composite bundles.
|
|
9
|
+
|
|
10
|
+
This module handles embedding child bundles into composite FTS containers.
|
|
11
|
+
Children are stored in the children/ directory with UUID-based names to avoid
|
|
12
|
+
collisions, while labels and positions are stored in layout.panels.
|
|
13
|
+
|
|
14
|
+
Design principles:
|
|
15
|
+
- Use child's own node.id (from canonical/spec.json) for naming
|
|
16
|
+
- Handle collisions with _2, _3, etc. suffixes
|
|
17
|
+
- Return (child_name, child_id) tuple for identity tracking
|
|
18
|
+
- Always validate child is valid FTS bundle
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import tempfile
|
|
22
|
+
import zipfile
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import TYPE_CHECKING
|
|
25
|
+
from typing import Dict, Tuple, Union
|
|
26
|
+
|
|
27
|
+
from ._storage import Storage
|
|
28
|
+
from ._storage import get_storage
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from ._FTS import FTS
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ValidationError(Exception):
|
|
35
|
+
"""Error raised when bundle validation fails."""
|
|
36
|
+
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def embed_child(
|
|
41
|
+
container_storage: Storage,
|
|
42
|
+
child_path: Union[str, Path],
|
|
43
|
+
validate: bool = False, # Default False for WIP workflows
|
|
44
|
+
) -> Tuple[str, str]:
|
|
45
|
+
"""Embed a child bundle into container's children/ directory.
|
|
46
|
+
|
|
47
|
+
Uses the child's own node.id (from canonical/spec.json) for naming to
|
|
48
|
+
preserve identity. Handles collisions with _2, _3, etc. suffixes.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
container_storage: Storage for the container bundle
|
|
52
|
+
child_path: Path to child bundle (.zip or directory)
|
|
53
|
+
validate: Validate child is valid FTS bundle (default False for WIP)
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
(child_name, child_id):
|
|
57
|
+
child_name: Filename in children/ (e.g., "690b1931.zip" or "690b1931_2.zip")
|
|
58
|
+
child_id: Full UUID from child's spec.json (for identity tracking)
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
ValidationError: If validate=True and child is invalid FTS bundle
|
|
62
|
+
FileNotFoundError: If child path doesn't exist
|
|
63
|
+
"""
|
|
64
|
+
child_path = Path(child_path)
|
|
65
|
+
if not child_path.exists():
|
|
66
|
+
raise FileNotFoundError(f"Child bundle not found: {child_path}")
|
|
67
|
+
|
|
68
|
+
# Load child bundle to get its node.id and optionally validate
|
|
69
|
+
from ._FTS import FTS
|
|
70
|
+
|
|
71
|
+
child_fts = FTS(child_path)
|
|
72
|
+
|
|
73
|
+
if validate:
|
|
74
|
+
errors = child_fts.validate()
|
|
75
|
+
if errors:
|
|
76
|
+
raise ValidationError(f"Invalid child bundle: {errors}")
|
|
77
|
+
|
|
78
|
+
# Get child's identity
|
|
79
|
+
full_child_id = child_fts.node.id
|
|
80
|
+
short_id = full_child_id[:8]
|
|
81
|
+
|
|
82
|
+
# Determine child filename with collision handling
|
|
83
|
+
child_name = f"{short_id}.zip"
|
|
84
|
+
suffix = 2
|
|
85
|
+
while container_storage.exists(f"children/{child_name}"):
|
|
86
|
+
child_name = f"{short_id}_{suffix}.zip"
|
|
87
|
+
suffix += 1
|
|
88
|
+
|
|
89
|
+
# Copy child bundle into children/ directory
|
|
90
|
+
_copy_child_to_storage(container_storage, child_path, child_name)
|
|
91
|
+
|
|
92
|
+
return child_name, full_child_id
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _copy_child_to_storage(
|
|
96
|
+
container_storage: Storage,
|
|
97
|
+
child_path: Path,
|
|
98
|
+
child_name: str,
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Copy child bundle into container's children/ directory.
|
|
101
|
+
|
|
102
|
+
Handles both ZIP and directory child bundles, always storing as ZIP.
|
|
103
|
+
The child ZIP uses a prefix matching its name (for proper unzip behavior).
|
|
104
|
+
"""
|
|
105
|
+
import io
|
|
106
|
+
|
|
107
|
+
dest_path = f"children/{child_name}"
|
|
108
|
+
# Use child_name (e.g., "6352df06.zip") stem as prefix
|
|
109
|
+
child_prefix = child_name.replace(".zip", "") + "/"
|
|
110
|
+
|
|
111
|
+
if child_path.suffix == ".zip":
|
|
112
|
+
# Read ZIP contents and copy to container
|
|
113
|
+
with zipfile.ZipFile(child_path, "r") as zf:
|
|
114
|
+
buf = io.BytesIO()
|
|
115
|
+
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as out_zf:
|
|
116
|
+
# Get original prefix (e.g., "my_plot/" from "my_plot.zip")
|
|
117
|
+
orig_prefix = child_path.stem + "/"
|
|
118
|
+
for item in zf.namelist():
|
|
119
|
+
if item.endswith("/"): # Skip directory entries
|
|
120
|
+
continue
|
|
121
|
+
data = zf.read(item)
|
|
122
|
+
# Strip original prefix and add new prefix
|
|
123
|
+
if item.startswith(orig_prefix):
|
|
124
|
+
rel_path = item[len(orig_prefix) :]
|
|
125
|
+
else:
|
|
126
|
+
rel_path = item
|
|
127
|
+
out_zf.writestr(child_prefix + rel_path, data)
|
|
128
|
+
|
|
129
|
+
container_storage.write(dest_path, buf.getvalue())
|
|
130
|
+
else:
|
|
131
|
+
# Directory bundle: create ZIP from directory
|
|
132
|
+
buf = io.BytesIO()
|
|
133
|
+
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as out_zf:
|
|
134
|
+
for item in child_path.rglob("*"):
|
|
135
|
+
if item.is_file():
|
|
136
|
+
rel_path = item.relative_to(child_path)
|
|
137
|
+
out_zf.writestr(
|
|
138
|
+
child_prefix + str(rel_path), item.read_bytes()
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
container_storage.write(dest_path, buf.getvalue())
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def load_embedded_children(
|
|
145
|
+
container_path: Union[str, Path],
|
|
146
|
+
) -> Dict[str, "FTS"]:
|
|
147
|
+
"""Load all embedded children from children/ directory.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
container_path: Path to container bundle
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Dict mapping child_name -> FTS object
|
|
154
|
+
"""
|
|
155
|
+
from ._FTS import FTS
|
|
156
|
+
|
|
157
|
+
container_path = Path(container_path)
|
|
158
|
+
storage = get_storage(container_path)
|
|
159
|
+
|
|
160
|
+
children = {}
|
|
161
|
+
for item in storage.list():
|
|
162
|
+
if item.startswith("children/") and item.endswith(".zip"):
|
|
163
|
+
child_name = item.split("/", 1)[1]
|
|
164
|
+
# Extract child to temp and load
|
|
165
|
+
child_fts = _load_child_from_storage(storage, child_name)
|
|
166
|
+
if child_fts:
|
|
167
|
+
children[child_name] = child_fts
|
|
168
|
+
|
|
169
|
+
return children
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _load_child_from_storage(
|
|
173
|
+
storage: Storage,
|
|
174
|
+
child_name: str,
|
|
175
|
+
) -> "FTS":
|
|
176
|
+
"""Load a single child bundle from storage.
|
|
177
|
+
|
|
178
|
+
Extracts child ZIP to temp directory and loads as FTS.
|
|
179
|
+
The temp file name matches the prefix inside the ZIP.
|
|
180
|
+
|
|
181
|
+
NOTE: Temp files are kept alive because FTS.validate() needs to
|
|
182
|
+
access storage for file existence checks. The OS will clean up
|
|
183
|
+
temp files eventually, or they can be cleaned up explicitly.
|
|
184
|
+
"""
|
|
185
|
+
from ._FTS import FTS
|
|
186
|
+
|
|
187
|
+
child_path = f"children/{child_name}"
|
|
188
|
+
|
|
189
|
+
# Check if child exists
|
|
190
|
+
if not storage.exists(child_path):
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
# Read child ZIP data
|
|
194
|
+
child_data = storage.read(child_path)
|
|
195
|
+
|
|
196
|
+
# Create temp directory and use matching filename
|
|
197
|
+
# The child ZIP has files prefixed with child_name stem (e.g., "6352df06/canonical/...")
|
|
198
|
+
# ZipStorage uses the file stem as prefix, so temp file must match
|
|
199
|
+
tmp_dir = Path(tempfile.mkdtemp())
|
|
200
|
+
tmp_path = tmp_dir / child_name # Use same name to match internal prefix
|
|
201
|
+
|
|
202
|
+
tmp_path.write_bytes(child_data)
|
|
203
|
+
fts = FTS(tmp_path)
|
|
204
|
+
# Store temp path reference on FTS object for potential cleanup
|
|
205
|
+
fts._temp_path = tmp_path
|
|
206
|
+
fts._temp_dir = tmp_dir
|
|
207
|
+
return fts
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
__all__ = [
|
|
211
|
+
"embed_child",
|
|
212
|
+
"load_embedded_children",
|
|
213
|
+
"ValidationError",
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
# EOF
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-12-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_conversion/__init__.py
|
|
4
|
+
|
|
5
|
+
"""FTS Conversion Utilities."""
|
|
6
|
+
|
|
7
|
+
from .bundle2dict import bundle_to_dict
|
|
8
|
+
from .dict2bundle import dict_to_bundle
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"bundle_to_dict",
|
|
12
|
+
"dict_to_bundle",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
# EOF
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-12-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_conversion/bundle2dict.py
|
|
4
|
+
|
|
5
|
+
"""Convert FTS bundle to dictionary."""
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Dict
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from .._FSB import FTS
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def bundle_to_dict(bundle: "FTS") -> Dict[str, Any]:
|
|
14
|
+
"""Convert Figure-Statistics Bundle to a flat dictionary.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
bundle: Figure-Statistics Bundle instance.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Dictionary with all bundle data.
|
|
21
|
+
"""
|
|
22
|
+
result = {
|
|
23
|
+
"path": str(bundle.path),
|
|
24
|
+
"is_zip": bundle.path.suffix == ".zip",
|
|
25
|
+
"type": bundle.bundle_type,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if bundle.node:
|
|
29
|
+
result["node"] = bundle.node.to_dict()
|
|
30
|
+
if bundle._encoding:
|
|
31
|
+
result["encoding"] = bundle._encoding.to_dict()
|
|
32
|
+
if bundle._theme:
|
|
33
|
+
result["theme"] = bundle._theme.to_dict()
|
|
34
|
+
if bundle._stats:
|
|
35
|
+
result["stats"] = bundle._stats.to_dict()
|
|
36
|
+
if bundle._data_info:
|
|
37
|
+
result["data_info"] = bundle._data_info.to_dict()
|
|
38
|
+
|
|
39
|
+
return result
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
__all__ = ["bundle_to_dict"]
|
|
43
|
+
|
|
44
|
+
# EOF
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-12-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_conversion/dict2bundle.py
|
|
4
|
+
|
|
5
|
+
"""Convert dictionary to FTS bundle."""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Dict, Union
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from .._FSB import FTS
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def dict_to_bundle(
|
|
15
|
+
data: Dict[str, Any],
|
|
16
|
+
path: Union[str, Path],
|
|
17
|
+
) -> "FTS":
|
|
18
|
+
"""Create Figure-Statistics Bundle from dictionary data.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
data: Dictionary with 'node', 'encoding', 'theme', etc.
|
|
22
|
+
path: Path for the new bundle.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Figure-Statistics Bundle instance.
|
|
26
|
+
"""
|
|
27
|
+
from .._FSB import FTS
|
|
28
|
+
|
|
29
|
+
node_data = data.get("node", {})
|
|
30
|
+
node_type = node_data.get("type", "plot")
|
|
31
|
+
name = node_data.get("name")
|
|
32
|
+
size_mm = node_data.get("size_mm")
|
|
33
|
+
|
|
34
|
+
bundle = FTS(path, create=True, node_type=node_type, name=name, size_mm=size_mm)
|
|
35
|
+
|
|
36
|
+
if "encoding" in data:
|
|
37
|
+
bundle.encoding = data["encoding"]
|
|
38
|
+
if "theme" in data:
|
|
39
|
+
bundle.theme = data["theme"]
|
|
40
|
+
if "stats" in data:
|
|
41
|
+
bundle.stats = data["stats"]
|
|
42
|
+
if "data_info" in data:
|
|
43
|
+
bundle.data_info = data["data_info"]
|
|
44
|
+
|
|
45
|
+
return bundle
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
__all__ = ["dict_to_bundle"]
|
|
49
|
+
|
|
50
|
+
# EOF
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-12-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_dataclasses/Axes.py
|
|
4
|
+
|
|
5
|
+
"""Axes - Axis configuration for plot nodes."""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any, Dict, Optional, Tuple
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class Axes:
|
|
13
|
+
"""Axis configuration for plot nodes.
|
|
14
|
+
|
|
15
|
+
Defines axis limits, scales, and labels.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
xlim: Optional[Tuple[float, float]] = None
|
|
19
|
+
ylim: Optional[Tuple[float, float]] = None
|
|
20
|
+
xscale: str = "linear" # linear, log, symlog
|
|
21
|
+
yscale: str = "linear"
|
|
22
|
+
xlabel: Optional[str] = None
|
|
23
|
+
ylabel: Optional[str] = None
|
|
24
|
+
|
|
25
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
26
|
+
result = {}
|
|
27
|
+
if self.xlim is not None:
|
|
28
|
+
result["xlim"] = list(self.xlim)
|
|
29
|
+
if self.ylim is not None:
|
|
30
|
+
result["ylim"] = list(self.ylim)
|
|
31
|
+
if self.xscale != "linear":
|
|
32
|
+
result["xscale"] = self.xscale
|
|
33
|
+
if self.yscale != "linear":
|
|
34
|
+
result["yscale"] = self.yscale
|
|
35
|
+
if self.xlabel:
|
|
36
|
+
result["xlabel"] = self.xlabel
|
|
37
|
+
if self.ylabel:
|
|
38
|
+
result["ylabel"] = self.ylabel
|
|
39
|
+
return result
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def from_dict(cls, data: Dict[str, Any]) -> "Axes":
|
|
43
|
+
xlim = data.get("xlim")
|
|
44
|
+
ylim = data.get("ylim")
|
|
45
|
+
return cls(
|
|
46
|
+
xlim=tuple(xlim) if xlim else None,
|
|
47
|
+
ylim=tuple(ylim) if ylim else None,
|
|
48
|
+
xscale=data.get("xscale", "linear"),
|
|
49
|
+
yscale=data.get("yscale", "linear"),
|
|
50
|
+
xlabel=data.get("xlabel"),
|
|
51
|
+
ylabel=data.get("ylabel"),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
__all__ = ["Axes"]
|
|
56
|
+
|
|
57
|
+
# EOF
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-12-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_dataclasses/BBox.py
|
|
4
|
+
|
|
5
|
+
"""BBox - Bounding box in normalized coordinates (0-1)."""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class BBox:
|
|
13
|
+
"""Bounding box in normalized coordinates (0-1).
|
|
14
|
+
|
|
15
|
+
Used for positioning elements within parent containers.
|
|
16
|
+
Origin is top-left, y increases downward.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
x0: float = 0.0
|
|
20
|
+
y0: float = 0.0
|
|
21
|
+
x1: float = 1.0
|
|
22
|
+
y1: float = 1.0
|
|
23
|
+
|
|
24
|
+
def __post_init__(self):
|
|
25
|
+
# Clamp to valid range
|
|
26
|
+
self.x0 = max(0.0, min(1.0, self.x0))
|
|
27
|
+
self.y0 = max(0.0, min(1.0, self.y0))
|
|
28
|
+
self.x1 = max(0.0, min(1.0, self.x1))
|
|
29
|
+
self.y1 = max(0.0, min(1.0, self.y1))
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def width(self) -> float:
|
|
33
|
+
return self.x1 - self.x0
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def height(self) -> float:
|
|
37
|
+
return self.y1 - self.y0
|
|
38
|
+
|
|
39
|
+
def to_dict(self) -> Dict[str, float]:
|
|
40
|
+
return {"x0": self.x0, "y0": self.y0, "x1": self.x1, "y1": self.y1}
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def from_dict(cls, data: Dict[str, Any]) -> "BBox":
|
|
44
|
+
return cls(
|
|
45
|
+
x0=data.get("x0", 0.0),
|
|
46
|
+
y0=data.get("y0", 0.0),
|
|
47
|
+
x1=data.get("x1", 1.0),
|
|
48
|
+
y1=data.get("y1", 1.0),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
__all__ = ["BBox"]
|
|
53
|
+
|
|
54
|
+
# EOF
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-12-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_dataclasses/ColumnDef.py
|
|
4
|
+
|
|
5
|
+
"""ColumnDef - Column definition with metadata."""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class ColumnDef:
|
|
13
|
+
"""Column definition with metadata."""
|
|
14
|
+
|
|
15
|
+
name: str
|
|
16
|
+
dtype: str # float64, int64, string, bool, datetime, category
|
|
17
|
+
description: Optional[str] = None
|
|
18
|
+
unit: Optional[str] = None # Physical unit: mV, ms, Hz, etc.
|
|
19
|
+
role: Optional[str] = None # x, y, group, subject, condition, time, error, weight
|
|
20
|
+
missing_count: Optional[int] = None
|
|
21
|
+
unique_count: Optional[int] = None
|
|
22
|
+
min: Optional[float] = None
|
|
23
|
+
max: Optional[float] = None
|
|
24
|
+
mean: Optional[float] = None
|
|
25
|
+
std: Optional[float] = None
|
|
26
|
+
categories: Optional[List[str]] = None
|
|
27
|
+
|
|
28
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
29
|
+
result = {"name": self.name, "dtype": self.dtype}
|
|
30
|
+
for attr in [
|
|
31
|
+
"description",
|
|
32
|
+
"unit",
|
|
33
|
+
"role",
|
|
34
|
+
"missing_count",
|
|
35
|
+
"unique_count",
|
|
36
|
+
"min",
|
|
37
|
+
"max",
|
|
38
|
+
"mean",
|
|
39
|
+
"std",
|
|
40
|
+
"categories",
|
|
41
|
+
]:
|
|
42
|
+
val = getattr(self, attr)
|
|
43
|
+
if val is not None:
|
|
44
|
+
result[attr] = val
|
|
45
|
+
return result
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def from_dict(cls, data: Any) -> "ColumnDef":
|
|
49
|
+
# Handle case where column is just a string (column name)
|
|
50
|
+
if isinstance(data, str):
|
|
51
|
+
return cls(name=data, dtype="unknown")
|
|
52
|
+
if not isinstance(data, dict):
|
|
53
|
+
return cls(name="", dtype="unknown") # Default
|
|
54
|
+
return cls(
|
|
55
|
+
name=data.get("name", ""),
|
|
56
|
+
dtype=data.get("dtype", "float64"),
|
|
57
|
+
description=data.get("description"),
|
|
58
|
+
unit=data.get("unit"),
|
|
59
|
+
role=data.get("role"),
|
|
60
|
+
missing_count=data.get("missing_count"),
|
|
61
|
+
unique_count=data.get("unique_count"),
|
|
62
|
+
min=data.get("min"),
|
|
63
|
+
max=data.get("max"),
|
|
64
|
+
mean=data.get("mean"),
|
|
65
|
+
std=data.get("std"),
|
|
66
|
+
categories=data.get("categories"),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
__all__ = ["ColumnDef"]
|
|
71
|
+
|
|
72
|
+
# EOF
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-12-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_dataclasses/DataFormat.py
|
|
4
|
+
|
|
5
|
+
"""DataFormat - Data file format specification."""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class DataFormat:
|
|
13
|
+
"""Data file format specification."""
|
|
14
|
+
|
|
15
|
+
type: str = "csv" # csv, tsv, parquet, json
|
|
16
|
+
encoding: str = "utf-8"
|
|
17
|
+
delimiter: str = ","
|
|
18
|
+
header_row: int = 0
|
|
19
|
+
|
|
20
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
21
|
+
return {
|
|
22
|
+
"type": self.type,
|
|
23
|
+
"encoding": self.encoding,
|
|
24
|
+
"delimiter": self.delimiter,
|
|
25
|
+
"header_row": self.header_row,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def from_dict(cls, data: Dict[str, Any]) -> "DataFormat":
|
|
30
|
+
return cls(
|
|
31
|
+
type=data.get("type", "csv"),
|
|
32
|
+
encoding=data.get("encoding", "utf-8"),
|
|
33
|
+
delimiter=data.get("delimiter", ","),
|
|
34
|
+
header_row=data.get("header_row", 0),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
__all__ = ["DataFormat"]
|
|
39
|
+
|
|
40
|
+
# EOF
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-12-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_dataclasses/DataInfo.py
|
|
4
|
+
|
|
5
|
+
"""DataInfo - Complete data info specification for a bundle."""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
from ._ColumnDef import ColumnDef
|
|
12
|
+
from ._DataFormat import DataFormat
|
|
13
|
+
from ._DataSource import DataSource
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
import pandas
|
|
17
|
+
|
|
18
|
+
DATA_INFO_VERSION = "1.0.0"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class DataInfo:
|
|
23
|
+
"""Complete data info specification for a bundle.
|
|
24
|
+
|
|
25
|
+
Stored in data/data_info.json.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
columns: List[ColumnDef] = field(default_factory=list)
|
|
29
|
+
source: Optional[DataSource] = None
|
|
30
|
+
format: DataFormat = field(default_factory=DataFormat)
|
|
31
|
+
shape: Optional[Dict[str, int]] = None # rows, columns
|
|
32
|
+
|
|
33
|
+
# Schema metadata
|
|
34
|
+
schema_name: str = "fsb.data_info"
|
|
35
|
+
schema_version: str = DATA_INFO_VERSION
|
|
36
|
+
|
|
37
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
38
|
+
result = {
|
|
39
|
+
"columns": [c.to_dict() for c in self.columns],
|
|
40
|
+
"format": self.format.to_dict(),
|
|
41
|
+
}
|
|
42
|
+
if self.source:
|
|
43
|
+
result["source"] = self.source.to_dict()
|
|
44
|
+
if self.shape:
|
|
45
|
+
result["shape"] = self.shape
|
|
46
|
+
return result
|
|
47
|
+
|
|
48
|
+
def to_json(self, indent: int = 2) -> str:
|
|
49
|
+
return json.dumps(self.to_dict(), indent=indent)
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def from_dict(cls, data: Dict[str, Any]) -> "DataInfo":
|
|
53
|
+
source = None
|
|
54
|
+
if "source" in data:
|
|
55
|
+
source = DataSource.from_dict(data["source"])
|
|
56
|
+
return cls(
|
|
57
|
+
columns=[ColumnDef.from_dict(c) for c in data.get("columns", [])],
|
|
58
|
+
source=source,
|
|
59
|
+
format=DataFormat.from_dict(data.get("format", {})),
|
|
60
|
+
shape=data.get("shape"),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def from_json(cls, json_str: str) -> "DataInfo":
|
|
65
|
+
return cls.from_dict(json.loads(json_str))
|
|
66
|
+
|
|
67
|
+
def get_column(self, name: str) -> Optional[ColumnDef]:
|
|
68
|
+
"""Get column definition by name."""
|
|
69
|
+
for col in self.columns:
|
|
70
|
+
if col.name == name:
|
|
71
|
+
return col
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def from_dataframe(
|
|
76
|
+
cls, df: "pandas.DataFrame", source_path: Optional[str] = None
|
|
77
|
+
) -> "DataInfo":
|
|
78
|
+
"""Create DataInfo from a pandas DataFrame."""
|
|
79
|
+
import pandas as pd
|
|
80
|
+
|
|
81
|
+
columns = []
|
|
82
|
+
for col_name in df.columns:
|
|
83
|
+
col = df[col_name]
|
|
84
|
+
dtype = str(col.dtype)
|
|
85
|
+
|
|
86
|
+
# Map pandas dtypes to our types
|
|
87
|
+
if "float" in dtype:
|
|
88
|
+
dtype_str = "float64"
|
|
89
|
+
elif "int" in dtype:
|
|
90
|
+
dtype_str = "int64"
|
|
91
|
+
elif "bool" in dtype:
|
|
92
|
+
dtype_str = "bool"
|
|
93
|
+
elif "datetime" in dtype:
|
|
94
|
+
dtype_str = "datetime"
|
|
95
|
+
elif col.dtype.name == "category":
|
|
96
|
+
dtype_str = "category"
|
|
97
|
+
else:
|
|
98
|
+
dtype_str = "string"
|
|
99
|
+
|
|
100
|
+
col_def = ColumnDef(
|
|
101
|
+
name=str(col_name),
|
|
102
|
+
dtype=dtype_str,
|
|
103
|
+
missing_count=int(col.isna().sum()),
|
|
104
|
+
unique_count=int(col.nunique()),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Add numeric stats
|
|
108
|
+
if dtype_str in ("float64", "int64"):
|
|
109
|
+
col_def.min = float(col.min()) if not pd.isna(col.min()) else None
|
|
110
|
+
col_def.max = float(col.max()) if not pd.isna(col.max()) else None
|
|
111
|
+
col_def.mean = float(col.mean()) if not pd.isna(col.mean()) else None
|
|
112
|
+
col_def.std = float(col.std()) if not pd.isna(col.std()) else None
|
|
113
|
+
|
|
114
|
+
# Add categories
|
|
115
|
+
if dtype_str == "category" or (
|
|
116
|
+
dtype_str == "string" and col.nunique() < 20
|
|
117
|
+
):
|
|
118
|
+
col_def.categories = col.dropna().unique().tolist()
|
|
119
|
+
|
|
120
|
+
columns.append(col_def)
|
|
121
|
+
|
|
122
|
+
source = None
|
|
123
|
+
if source_path:
|
|
124
|
+
source = DataSource(path=source_path)
|
|
125
|
+
|
|
126
|
+
return cls(
|
|
127
|
+
columns=columns,
|
|
128
|
+
source=source,
|
|
129
|
+
shape={"rows": len(df), "columns": len(df.columns)},
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
__all__ = ["DATA_INFO_VERSION", "DataInfo"]
|
|
134
|
+
|
|
135
|
+
# EOF
|