scitex 2.7.0__py3-none-any.whl → 2.8.1__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 +6 -2
- scitex/__version__.py +1 -1
- scitex/audio/README.md +52 -0
- scitex/audio/__init__.py +384 -0
- scitex/audio/__main__.py +129 -0
- scitex/audio/_tts.py +334 -0
- scitex/audio/engines/__init__.py +44 -0
- scitex/audio/engines/base.py +275 -0
- scitex/audio/engines/elevenlabs_engine.py +143 -0
- scitex/audio/engines/gtts_engine.py +162 -0
- scitex/audio/engines/pyttsx3_engine.py +131 -0
- scitex/audio/mcp_server.py +757 -0
- scitex/bridge/_helpers.py +1 -1
- scitex/bridge/_plt_vis.py +1 -1
- scitex/bridge/_stats_vis.py +1 -1
- scitex/dev/plt/__init__.py +272 -0
- scitex/dev/plt/plot_mpl_axhline.py +28 -0
- scitex/dev/plt/plot_mpl_axhspan.py +28 -0
- scitex/dev/plt/plot_mpl_axvline.py +28 -0
- scitex/dev/plt/plot_mpl_axvspan.py +28 -0
- scitex/dev/plt/plot_mpl_bar.py +29 -0
- scitex/dev/plt/plot_mpl_barh.py +29 -0
- scitex/dev/plt/plot_mpl_boxplot.py +28 -0
- scitex/dev/plt/plot_mpl_contour.py +31 -0
- scitex/dev/plt/plot_mpl_contourf.py +31 -0
- scitex/dev/plt/plot_mpl_errorbar.py +30 -0
- scitex/dev/plt/plot_mpl_eventplot.py +28 -0
- scitex/dev/plt/plot_mpl_fill.py +30 -0
- scitex/dev/plt/plot_mpl_fill_between.py +31 -0
- scitex/dev/plt/plot_mpl_hexbin.py +28 -0
- scitex/dev/plt/plot_mpl_hist.py +28 -0
- scitex/dev/plt/plot_mpl_hist2d.py +28 -0
- scitex/dev/plt/plot_mpl_imshow.py +29 -0
- scitex/dev/plt/plot_mpl_pcolormesh.py +31 -0
- scitex/dev/plt/plot_mpl_pie.py +29 -0
- scitex/dev/plt/plot_mpl_plot.py +29 -0
- scitex/dev/plt/plot_mpl_quiver.py +31 -0
- scitex/dev/plt/plot_mpl_scatter.py +28 -0
- scitex/dev/plt/plot_mpl_stackplot.py +31 -0
- scitex/dev/plt/plot_mpl_stem.py +29 -0
- scitex/dev/plt/plot_mpl_step.py +29 -0
- scitex/dev/plt/plot_mpl_violinplot.py +28 -0
- scitex/dev/plt/plot_sns_barplot.py +29 -0
- scitex/dev/plt/plot_sns_boxplot.py +29 -0
- scitex/dev/plt/plot_sns_heatmap.py +28 -0
- scitex/dev/plt/plot_sns_histplot.py +29 -0
- scitex/dev/plt/plot_sns_kdeplot.py +29 -0
- scitex/dev/plt/plot_sns_lineplot.py +31 -0
- scitex/dev/plt/plot_sns_scatterplot.py +29 -0
- scitex/dev/plt/plot_sns_stripplot.py +29 -0
- scitex/dev/plt/plot_sns_swarmplot.py +29 -0
- scitex/dev/plt/plot_sns_violinplot.py +29 -0
- scitex/dev/plt/plot_stx_bar.py +29 -0
- scitex/dev/plt/plot_stx_barh.py +29 -0
- scitex/dev/plt/plot_stx_box.py +28 -0
- scitex/dev/plt/plot_stx_boxplot.py +28 -0
- scitex/dev/plt/plot_stx_conf_mat.py +28 -0
- scitex/dev/plt/plot_stx_contour.py +31 -0
- scitex/dev/plt/plot_stx_ecdf.py +28 -0
- scitex/dev/plt/plot_stx_errorbar.py +30 -0
- scitex/dev/plt/plot_stx_fill_between.py +31 -0
- scitex/dev/plt/plot_stx_fillv.py +28 -0
- scitex/dev/plt/plot_stx_heatmap.py +28 -0
- scitex/dev/plt/plot_stx_image.py +28 -0
- scitex/dev/plt/plot_stx_imshow.py +28 -0
- scitex/dev/plt/plot_stx_joyplot.py +28 -0
- scitex/dev/plt/plot_stx_kde.py +28 -0
- scitex/dev/plt/plot_stx_line.py +28 -0
- scitex/dev/plt/plot_stx_mean_ci.py +28 -0
- scitex/dev/plt/plot_stx_mean_std.py +28 -0
- scitex/dev/plt/plot_stx_median_iqr.py +28 -0
- scitex/dev/plt/plot_stx_raster.py +28 -0
- scitex/dev/plt/plot_stx_rectangle.py +28 -0
- scitex/dev/plt/plot_stx_scatter.py +29 -0
- scitex/dev/plt/plot_stx_shaded_line.py +29 -0
- scitex/dev/plt/plot_stx_violin.py +28 -0
- scitex/dev/plt/plot_stx_violinplot.py +28 -0
- scitex/diagram/README.md +197 -0
- scitex/diagram/__init__.py +48 -0
- scitex/diagram/_compile.py +312 -0
- scitex/diagram/_diagram.py +355 -0
- scitex/diagram/_presets.py +173 -0
- scitex/diagram/_schema.py +182 -0
- scitex/diagram/_split.py +278 -0
- scitex/fig/__init__.py +352 -0
- scitex/{vis → fig}/backend/_parser.py +1 -1
- scitex/{vis → fig}/canvas.py +1 -1
- scitex/{vis → fig}/editor/__init__.py +5 -2
- scitex/{vis → fig}/editor/_dearpygui_editor.py +1 -1
- scitex/{vis → fig}/editor/_defaults.py +70 -5
- scitex/{vis → fig}/editor/_mpl_editor.py +1 -1
- scitex/{vis → fig}/editor/_qt_editor.py +182 -2
- scitex/{vis → fig}/editor/_tkinter_editor.py +1 -1
- scitex/fig/editor/edit/__init__.py +50 -0
- scitex/fig/editor/edit/backend_detector.py +109 -0
- scitex/fig/editor/edit/bundle_resolver.py +240 -0
- scitex/fig/editor/edit/editor_launcher.py +239 -0
- scitex/fig/editor/edit/manual_handler.py +53 -0
- scitex/fig/editor/edit/panel_loader.py +232 -0
- scitex/fig/editor/edit/path_resolver.py +67 -0
- scitex/fig/editor/flask_editor/_bbox.py +1299 -0
- scitex/fig/editor/flask_editor/_core.py +1429 -0
- scitex/{vis → fig}/editor/flask_editor/_plotter.py +38 -4
- scitex/fig/editor/flask_editor/_renderer.py +813 -0
- scitex/fig/editor/flask_editor/static/css/base/reset.css +41 -0
- scitex/fig/editor/flask_editor/static/css/base/typography.css +16 -0
- scitex/fig/editor/flask_editor/static/css/base/variables.css +85 -0
- scitex/fig/editor/flask_editor/static/css/components/buttons.css +217 -0
- scitex/fig/editor/flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fig/editor/flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fig/editor/flask_editor/static/css/components/forms.css +112 -0
- scitex/fig/editor/flask_editor/static/css/components/modal.css +59 -0
- scitex/fig/editor/flask_editor/static/css/components/sections.css +212 -0
- scitex/fig/editor/flask_editor/static/css/features/canvas.css +176 -0
- scitex/fig/editor/flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fig/editor/flask_editor/static/css/features/loading.css +59 -0
- scitex/fig/editor/flask_editor/static/css/features/overlay.css +45 -0
- scitex/fig/editor/flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fig/editor/flask_editor/static/css/features/selection.css +101 -0
- scitex/fig/editor/flask_editor/static/css/features/statistics.css +138 -0
- scitex/fig/editor/flask_editor/static/css/index.css +31 -0
- scitex/fig/editor/flask_editor/static/css/layout/container.css +7 -0
- scitex/fig/editor/flask_editor/static/css/layout/controls.css +56 -0
- scitex/fig/editor/flask_editor/static/css/layout/preview.css +78 -0
- scitex/fig/editor/flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fig/editor/flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fig/editor/flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fig/editor/flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fig/editor/flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fig/editor/flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fig/editor/flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fig/editor/flask_editor/static/js/core/api.js +288 -0
- scitex/fig/editor/flask_editor/static/js/core/state.js +143 -0
- scitex/fig/editor/flask_editor/static/js/core/utils.js +245 -0
- scitex/fig/editor/flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fig/editor/flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fig/editor/flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fig/editor/flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fig/editor/flask_editor/static/js/editor/preview.js +293 -0
- scitex/fig/editor/flask_editor/static/js/main.js +426 -0
- scitex/fig/editor/flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fig/editor/flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fig/editor/flask_editor/static/js/ui/controls.js +184 -0
- scitex/fig/editor/flask_editor/static/js/ui/download.js +57 -0
- scitex/fig/editor/flask_editor/static/js/ui/help.js +100 -0
- scitex/fig/editor/flask_editor/static/js/ui/theme.js +34 -0
- scitex/fig/editor/flask_editor/templates/__init__.py +123 -0
- scitex/fig/editor/flask_editor/templates/_html.py +852 -0
- scitex/fig/editor/flask_editor/templates/_scripts.py +4933 -0
- scitex/fig/editor/flask_editor/templates/_styles.py +1658 -0
- scitex/{vis → fig}/io/__init__.py +13 -1
- scitex/fig/io/_bundle.py +1058 -0
- scitex/{vis → fig}/io/_canvas.py +1 -1
- scitex/{vis → fig}/io/_data.py +1 -1
- scitex/{vis → fig}/io/_export.py +1 -1
- scitex/{vis → fig}/io/_load.py +1 -1
- scitex/{vis → fig}/io/_panel.py +1 -1
- scitex/{vis → fig}/io/_save.py +1 -1
- scitex/{vis → fig}/model/__init__.py +1 -1
- scitex/{vis → fig}/model/_annotations.py +1 -1
- scitex/{vis → fig}/model/_axes.py +1 -1
- scitex/{vis → fig}/model/_figure.py +1 -1
- scitex/{vis → fig}/model/_guides.py +1 -1
- scitex/{vis → fig}/model/_plot.py +1 -1
- scitex/{vis → fig}/model/_styles.py +1 -1
- scitex/{vis → fig}/utils/__init__.py +1 -1
- scitex/io/__init__.py +22 -26
- scitex/io/_bundle.py +493 -0
- scitex/io/_flush.py +5 -2
- scitex/io/_load.py +98 -0
- scitex/io/_load_modules/_H5Explorer.py +5 -2
- scitex/io/_load_modules/_canvas.py +2 -2
- scitex/io/_load_modules/_image.py +3 -4
- scitex/io/_load_modules/_txt.py +4 -2
- scitex/io/_metadata.py +34 -324
- scitex/io/_metadata_modules/__init__.py +46 -0
- scitex/io/_metadata_modules/_embed.py +70 -0
- scitex/io/_metadata_modules/_read.py +64 -0
- scitex/io/_metadata_modules/_utils.py +79 -0
- scitex/io/_metadata_modules/embed_metadata_jpeg.py +74 -0
- scitex/io/_metadata_modules/embed_metadata_pdf.py +53 -0
- scitex/io/_metadata_modules/embed_metadata_png.py +26 -0
- scitex/io/_metadata_modules/embed_metadata_svg.py +62 -0
- scitex/io/_metadata_modules/read_metadata_jpeg.py +57 -0
- scitex/io/_metadata_modules/read_metadata_pdf.py +51 -0
- scitex/io/_metadata_modules/read_metadata_png.py +39 -0
- scitex/io/_metadata_modules/read_metadata_svg.py +44 -0
- scitex/io/_qr_utils.py +5 -3
- scitex/io/_save.py +548 -30
- scitex/io/_save_modules/_canvas.py +3 -3
- scitex/io/_save_modules/_image.py +5 -9
- scitex/io/_save_modules/_tex.py +7 -4
- scitex/io/_zip_bundle.py +439 -0
- scitex/io/utils/h5_to_zarr.py +11 -9
- scitex/msword/__init__.py +255 -0
- scitex/msword/profiles.py +357 -0
- scitex/msword/reader.py +753 -0
- scitex/msword/utils.py +289 -0
- scitex/msword/writer.py +362 -0
- scitex/plt/__init__.py +5 -2
- scitex/plt/_subplots/_AxesWrapper.py +6 -6
- scitex/plt/_subplots/_AxisWrapper.py +15 -9
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/__init__.py +36 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_labels.py +264 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_metadata.py +213 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin/_visual.py +128 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +59 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_base.py +34 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_scientific.py +593 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_statistical.py +654 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/_stx_aliases.py +527 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_RawMatplotlibMixin.py +321 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/__init__.py +33 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_base.py +152 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +600 -0
- scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +79 -5
- scitex/plt/_subplots/_FigWrapper.py +6 -6
- scitex/plt/_subplots/_SubplotsWrapper.py +28 -18
- scitex/plt/_subplots/_export_as_csv.py +35 -5
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +8 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +10 -21
- scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +18 -7
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +28 -12
- scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +10 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +13 -1
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +12 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +10 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +18 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +44 -36
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +14 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +11 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_bar.py +84 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_barh.py +85 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +14 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_contour.py +54 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +14 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_errorbar.py +120 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +16 -6
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +29 -19
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_imshow.py +63 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +22 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +18 -14
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +10 -2
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter.py +51 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +18 -9
- scitex/plt/ax/_plot/_stx_ecdf.py +4 -2
- scitex/plt/gallery/_generate.py +421 -14
- scitex/plt/io/__init__.py +53 -0
- scitex/plt/io/_bundle.py +490 -0
- scitex/plt/io/_layered_bundle.py +1343 -0
- scitex/plt/styles/SCITEX_STYLE.yaml +26 -0
- scitex/plt/styles/__init__.py +14 -0
- scitex/plt/styles/presets.py +78 -0
- scitex/plt/utils/__init__.py +13 -1
- scitex/plt/utils/_collect_figure_metadata.py +10 -14
- scitex/plt/utils/_configure_mpl.py +6 -18
- scitex/plt/utils/_crop.py +32 -14
- scitex/plt/utils/_csv_column_naming.py +54 -0
- scitex/plt/utils/_figure_mm.py +116 -1
- scitex/plt/utils/_hitmap.py +1643 -0
- scitex/plt/utils/metadata/__init__.py +25 -0
- scitex/plt/utils/metadata/_core.py +9 -10
- scitex/plt/utils/metadata/_dimensions.py +6 -3
- scitex/plt/utils/metadata/_editable_export.py +405 -0
- scitex/plt/utils/metadata/_geometry_extraction.py +570 -0
- scitex/schema/__init__.py +109 -16
- scitex/schema/_canvas.py +1 -1
- scitex/schema/_plot.py +1015 -0
- scitex/schema/_stats.py +2 -2
- scitex/stats/__init__.py +117 -0
- scitex/stats/io/__init__.py +29 -0
- scitex/stats/io/_bundle.py +156 -0
- scitex/tex/__init__.py +4 -0
- scitex/tex/_export.py +890 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/METADATA +11 -1
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/RECORD +294 -170
- scitex/io/memo.md +0 -2827
- scitex/plt/REQUESTS.md +0 -191
- scitex/plt/_subplots/TODO.md +0 -53
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +0 -559
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +0 -1609
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +0 -447
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +0 -110
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +0 -88
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +0 -103
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +0 -106
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +0 -83
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +0 -91
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +0 -97
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +0 -98
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +0 -101
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +0 -83
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +0 -121
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +0 -110
- scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +0 -95
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +0 -92
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +0 -94
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +0 -109
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +0 -108
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +0 -93
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +0 -84
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +0 -96
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +0 -99
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +0 -100
- scitex/vis/__init__.py +0 -177
- scitex/vis/editor/_edit.py +0 -390
- scitex/vis/editor/flask_editor/_bbox.py +0 -529
- scitex/vis/editor/flask_editor/_core.py +0 -168
- scitex/vis/editor/flask_editor/_renderer.py +0 -393
- scitex/vis/editor/flask_editor/templates/__init__.py +0 -33
- scitex/vis/editor/flask_editor/templates/_html.py +0 -513
- scitex/vis/editor/flask_editor/templates/_scripts.py +0 -1261
- scitex/vis/editor/flask_editor/templates/_styles.py +0 -739
- /scitex/{vis → fig}/README.md +0 -0
- /scitex/{vis → fig}/backend/__init__.py +0 -0
- /scitex/{vis → fig}/backend/_export.py +0 -0
- /scitex/{vis → fig}/backend/_render.py +0 -0
- /scitex/{vis → fig}/docs/CANVAS_ARCHITECTURE.md +0 -0
- /scitex/{vis → fig}/editor/_flask_editor.py +0 -0
- /scitex/{vis → fig}/editor/flask_editor/__init__.py +0 -0
- /scitex/{vis → fig}/editor/flask_editor/_utils.py +0 -0
- /scitex/{vis → fig}/io/_directory.py +0 -0
- /scitex/{vis → fig}/model/_plot_types.py +0 -0
- /scitex/{vis → fig}/utils/_defaults.py +0 -0
- /scitex/{vis → fig}/utils/_validate.py +0 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/WHEEL +0 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/entry_points.txt +0 -0
- {scitex-2.7.0.dist-info → scitex-2.8.1.dist-info}/licenses/LICENSE +0 -0
scitex/io/_bundle.py
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-12-13 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_bundle.py
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
SciTeX Bundle I/O - Shared utilities for .figz, .pltz, and .statsz formats.
|
|
8
|
+
|
|
9
|
+
This module provides common bundle operations. Domain-specific logic is in:
|
|
10
|
+
- scitex.plt.io._bundle (pltz: hitmap, CSV, overview)
|
|
11
|
+
- scitex.fig.io._bundle (figz: panel composition, nested pltz)
|
|
12
|
+
- scitex.stats.io._bundle (statsz: comparison metadata)
|
|
13
|
+
|
|
14
|
+
Bundle formats:
|
|
15
|
+
.figz - Publication Figure Bundle (panels + layout)
|
|
16
|
+
.pltz - Reproducible Plot Bundle (data + spec + exports)
|
|
17
|
+
.statsz - Statistical Results Bundle (stats + metadata)
|
|
18
|
+
|
|
19
|
+
Each bundle can be:
|
|
20
|
+
- Directory form: Figure1.figz.d/
|
|
21
|
+
- ZIP archive form: Figure1.figz
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
import shutil
|
|
26
|
+
import zipfile
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any, Dict, List, Optional, Union
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"is_bundle",
|
|
32
|
+
"load_bundle",
|
|
33
|
+
"save_bundle",
|
|
34
|
+
"pack_bundle",
|
|
35
|
+
"unpack_bundle",
|
|
36
|
+
"validate_bundle",
|
|
37
|
+
"validate_spec",
|
|
38
|
+
"BundleType",
|
|
39
|
+
"BundleValidationError",
|
|
40
|
+
"BUNDLE_EXTENSIONS",
|
|
41
|
+
"get_bundle_type",
|
|
42
|
+
"dir_to_zip_path",
|
|
43
|
+
"zip_to_dir_path",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
# Bundle extensions
|
|
47
|
+
BUNDLE_EXTENSIONS = (".figz", ".pltz", ".statsz")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class BundleValidationError(ValueError):
|
|
51
|
+
"""Error raised when bundle validation fails."""
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class BundleType:
|
|
56
|
+
"""Bundle type constants."""
|
|
57
|
+
FIGZ = "figz"
|
|
58
|
+
PLTZ = "pltz"
|
|
59
|
+
STATSZ = "statsz"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_bundle_type(path: Union[str, Path]) -> Optional[str]:
|
|
63
|
+
"""Get bundle type from path.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
path: Path to bundle (directory or ZIP).
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Bundle type string or None if not a bundle.
|
|
70
|
+
"""
|
|
71
|
+
p = Path(path)
|
|
72
|
+
|
|
73
|
+
# Directory bundle: ends with .figz.d, .pltz.d, .statsz.d
|
|
74
|
+
if p.is_dir() and p.suffix == ".d":
|
|
75
|
+
stem = p.stem # e.g., "Figure1.figz"
|
|
76
|
+
for ext in BUNDLE_EXTENSIONS:
|
|
77
|
+
if stem.endswith(ext):
|
|
78
|
+
return ext[1:] # Remove leading dot
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
# ZIP bundle: ends with .figz, .pltz, .statsz
|
|
82
|
+
if p.suffix in BUNDLE_EXTENSIONS:
|
|
83
|
+
return p.suffix[1:] # Remove leading dot
|
|
84
|
+
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def is_bundle(path: Union[str, Path]) -> bool:
|
|
89
|
+
"""Check if path is a SciTeX bundle (directory or ZIP).
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
path: Path to check.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
True if path is a bundle.
|
|
96
|
+
"""
|
|
97
|
+
return get_bundle_type(path) is not None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def dir_to_zip_path(dir_path: Path) -> Path:
|
|
101
|
+
"""Convert directory path to ZIP path.
|
|
102
|
+
|
|
103
|
+
Example: Figure1.figz.d/ -> Figure1.figz
|
|
104
|
+
"""
|
|
105
|
+
if dir_path.suffix == ".d":
|
|
106
|
+
return dir_path.with_suffix("")
|
|
107
|
+
return dir_path
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def zip_to_dir_path(zip_path: Path) -> Path:
|
|
111
|
+
"""Convert ZIP path to directory path.
|
|
112
|
+
|
|
113
|
+
Example: Figure1.figz -> Figure1.figz.d/
|
|
114
|
+
"""
|
|
115
|
+
return Path(str(zip_path) + ".d")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def pack_bundle(
|
|
119
|
+
dir_path: Union[str, Path], output_path: Optional[Union[str, Path]] = None
|
|
120
|
+
) -> Path:
|
|
121
|
+
"""Pack a bundle directory into a ZIP archive.
|
|
122
|
+
|
|
123
|
+
The ZIP archive includes the .d directory as a top-level folder so that
|
|
124
|
+
standard unzip extracts to: fname.pltz.d/fname.csv, fname.pltz.d/fname.json, etc.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
dir_path: Path to bundle directory (e.g., Figure1.figz.d/).
|
|
128
|
+
output_path: Output ZIP path. Auto-generated if None.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Path to created ZIP archive.
|
|
132
|
+
"""
|
|
133
|
+
dir_path = Path(dir_path)
|
|
134
|
+
|
|
135
|
+
if not dir_path.is_dir():
|
|
136
|
+
raise ValueError(f"Not a directory: {dir_path}")
|
|
137
|
+
|
|
138
|
+
if output_path is None:
|
|
139
|
+
output_path = dir_to_zip_path(dir_path)
|
|
140
|
+
else:
|
|
141
|
+
output_path = Path(output_path)
|
|
142
|
+
|
|
143
|
+
# Get the directory name to use as top-level folder in ZIP
|
|
144
|
+
# e.g., "stx_line.pltz.d" for path "/path/to/stx_line.pltz.d"
|
|
145
|
+
dir_name = dir_path.name
|
|
146
|
+
|
|
147
|
+
# Create ZIP archive with directory structure preserved
|
|
148
|
+
with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
|
149
|
+
for file_path in dir_path.rglob("*"):
|
|
150
|
+
if file_path.is_file():
|
|
151
|
+
# Include directory name in archive path
|
|
152
|
+
# e.g., "stx_line.pltz.d/stx_line.csv"
|
|
153
|
+
rel_path = file_path.relative_to(dir_path)
|
|
154
|
+
arcname = Path(dir_name) / rel_path
|
|
155
|
+
zf.write(file_path, arcname)
|
|
156
|
+
|
|
157
|
+
return output_path
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def unpack_bundle(
|
|
161
|
+
zip_path: Union[str, Path], output_path: Optional[Union[str, Path]] = None
|
|
162
|
+
) -> Path:
|
|
163
|
+
"""Unpack a bundle ZIP archive into a directory.
|
|
164
|
+
|
|
165
|
+
The ZIP archive contains a top-level .d directory, so extraction goes to
|
|
166
|
+
the parent directory. E.g., stx_line.pltz extracts to create stx_line.pltz.d/
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
zip_path: Path to bundle ZIP (e.g., Figure1.figz).
|
|
170
|
+
output_path: Output directory path. Auto-generated if None.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Path to created directory.
|
|
174
|
+
"""
|
|
175
|
+
zip_path = Path(zip_path)
|
|
176
|
+
|
|
177
|
+
if not zip_path.is_file():
|
|
178
|
+
raise ValueError(f"Not a file: {zip_path}")
|
|
179
|
+
|
|
180
|
+
# Determine extraction target
|
|
181
|
+
if output_path is None:
|
|
182
|
+
# Extract to same directory as ZIP file (ZIP contains .d folder structure)
|
|
183
|
+
extract_to = zip_path.parent
|
|
184
|
+
expected_dir = zip_to_dir_path(zip_path)
|
|
185
|
+
else:
|
|
186
|
+
output_path = Path(output_path)
|
|
187
|
+
extract_to = output_path.parent
|
|
188
|
+
expected_dir = output_path
|
|
189
|
+
|
|
190
|
+
# Extract ZIP archive (contains .d directory structure)
|
|
191
|
+
with zipfile.ZipFile(zip_path, "r") as zf:
|
|
192
|
+
zf.extractall(extract_to)
|
|
193
|
+
|
|
194
|
+
return expected_dir
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def validate_spec(
|
|
198
|
+
spec: Dict[str, Any], bundle_type: str, strict: bool = False
|
|
199
|
+
) -> List[str]:
|
|
200
|
+
"""Validate a bundle specification against its schema.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
spec: The specification dictionary to validate.
|
|
204
|
+
bundle_type: Bundle type ('figz', 'pltz', 'statsz').
|
|
205
|
+
strict: If True, raise BundleValidationError on failure.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
List of validation warning/error messages (empty if valid).
|
|
209
|
+
|
|
210
|
+
Raises:
|
|
211
|
+
BundleValidationError: If strict=True and validation fails.
|
|
212
|
+
"""
|
|
213
|
+
errors = []
|
|
214
|
+
|
|
215
|
+
# Basic schema validation
|
|
216
|
+
if "schema" not in spec:
|
|
217
|
+
errors.append("Missing required field: schema")
|
|
218
|
+
elif not isinstance(spec["schema"], dict):
|
|
219
|
+
errors.append("'schema' field must be a dictionary")
|
|
220
|
+
else:
|
|
221
|
+
schema = spec["schema"]
|
|
222
|
+
if "name" not in schema:
|
|
223
|
+
errors.append("schema.name is required")
|
|
224
|
+
if "version" not in schema:
|
|
225
|
+
errors.append("schema.version is required")
|
|
226
|
+
|
|
227
|
+
# Delegate to domain-specific validators
|
|
228
|
+
if bundle_type == BundleType.FIGZ:
|
|
229
|
+
from scitex.fig.io._bundle import validate_figz_spec
|
|
230
|
+
errors.extend(validate_figz_spec(spec))
|
|
231
|
+
elif bundle_type == BundleType.PLTZ:
|
|
232
|
+
from scitex.plt.io._bundle import validate_pltz_spec
|
|
233
|
+
errors.extend(validate_pltz_spec(spec))
|
|
234
|
+
elif bundle_type == BundleType.STATSZ:
|
|
235
|
+
from scitex.stats.io._bundle import validate_statsz_spec
|
|
236
|
+
errors.extend(validate_statsz_spec(spec))
|
|
237
|
+
else:
|
|
238
|
+
errors.append(f"Unknown bundle type: {bundle_type}")
|
|
239
|
+
|
|
240
|
+
if strict and errors:
|
|
241
|
+
raise BundleValidationError("; ".join(errors))
|
|
242
|
+
|
|
243
|
+
return errors
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def validate_bundle(
|
|
247
|
+
path: Union[str, Path], strict: bool = False
|
|
248
|
+
) -> Dict[str, Any]:
|
|
249
|
+
"""Validate a bundle and return validation results.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
path: Path to bundle (directory or ZIP).
|
|
253
|
+
strict: If True, raise BundleValidationError on failure.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Dictionary with:
|
|
257
|
+
- 'valid': bool
|
|
258
|
+
- 'errors': list of error messages
|
|
259
|
+
- 'bundle_type': detected bundle type
|
|
260
|
+
- 'spec': parsed spec (if available)
|
|
261
|
+
|
|
262
|
+
Raises:
|
|
263
|
+
BundleValidationError: If strict=True and validation fails.
|
|
264
|
+
"""
|
|
265
|
+
result = {
|
|
266
|
+
"valid": True,
|
|
267
|
+
"errors": [],
|
|
268
|
+
"bundle_type": None,
|
|
269
|
+
"spec": None,
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
p = Path(path)
|
|
273
|
+
|
|
274
|
+
# Check bundle type
|
|
275
|
+
bundle_type = get_bundle_type(p)
|
|
276
|
+
if bundle_type is None:
|
|
277
|
+
result["valid"] = False
|
|
278
|
+
result["errors"].append(f"Not a valid bundle path: {path}")
|
|
279
|
+
if strict:
|
|
280
|
+
raise BundleValidationError(result["errors"][0])
|
|
281
|
+
return result
|
|
282
|
+
|
|
283
|
+
result["bundle_type"] = bundle_type
|
|
284
|
+
|
|
285
|
+
# Try to load and validate
|
|
286
|
+
try:
|
|
287
|
+
bundle = load_bundle(path)
|
|
288
|
+
spec = bundle.get("spec")
|
|
289
|
+
result["spec"] = spec
|
|
290
|
+
|
|
291
|
+
if spec is not None:
|
|
292
|
+
errors = validate_spec(spec, bundle_type, strict=False)
|
|
293
|
+
if errors:
|
|
294
|
+
result["valid"] = False
|
|
295
|
+
result["errors"].extend(errors)
|
|
296
|
+
else:
|
|
297
|
+
result["valid"] = False
|
|
298
|
+
result["errors"].append("Bundle has no spec")
|
|
299
|
+
|
|
300
|
+
except Exception as e:
|
|
301
|
+
result["valid"] = False
|
|
302
|
+
result["errors"].append(f"Failed to load bundle: {e}")
|
|
303
|
+
|
|
304
|
+
if strict and not result["valid"]:
|
|
305
|
+
raise BundleValidationError("; ".join(result["errors"]))
|
|
306
|
+
|
|
307
|
+
return result
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def load_bundle(path: Union[str, Path], in_memory: bool = True) -> Dict[str, Any]:
|
|
311
|
+
"""Load bundle from directory or ZIP transparently.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
path: Path to bundle (directory or ZIP).
|
|
315
|
+
in_memory: If True, load ZIP contents in-memory without extracting.
|
|
316
|
+
If False, extract to temp directory (legacy behavior).
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
Bundle data as dictionary with:
|
|
320
|
+
- 'type': Bundle type ('figz', 'pltz', 'statsz')
|
|
321
|
+
- 'spec': Parsed JSON specification
|
|
322
|
+
- 'path': Original path
|
|
323
|
+
- 'is_zip': Whether loaded from ZIP
|
|
324
|
+
- Additional fields based on bundle type
|
|
325
|
+
"""
|
|
326
|
+
p = Path(path)
|
|
327
|
+
bundle_type = get_bundle_type(p)
|
|
328
|
+
|
|
329
|
+
if bundle_type is None:
|
|
330
|
+
raise ValueError(f"Not a valid bundle: {path}")
|
|
331
|
+
|
|
332
|
+
result = {
|
|
333
|
+
"type": bundle_type,
|
|
334
|
+
"path": p,
|
|
335
|
+
"is_zip": False,
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
# Handle ZIP vs directory
|
|
339
|
+
if p.is_file() and p.suffix in BUNDLE_EXTENSIONS:
|
|
340
|
+
result["is_zip"] = True
|
|
341
|
+
|
|
342
|
+
if in_memory:
|
|
343
|
+
# In-memory loading using ZipBundle
|
|
344
|
+
from ._zip_bundle import ZipBundle
|
|
345
|
+
with ZipBundle(p, mode="r") as zb:
|
|
346
|
+
result["_zip_bundle"] = zb
|
|
347
|
+
# Load common files in-memory
|
|
348
|
+
try:
|
|
349
|
+
result["spec"] = zb.read_json("spec.json")
|
|
350
|
+
except FileNotFoundError:
|
|
351
|
+
result["spec"] = None
|
|
352
|
+
try:
|
|
353
|
+
result["style"] = zb.read_json("style.json")
|
|
354
|
+
except FileNotFoundError:
|
|
355
|
+
result["style"] = None
|
|
356
|
+
try:
|
|
357
|
+
result["data"] = zb.read_csv("data.csv")
|
|
358
|
+
except FileNotFoundError:
|
|
359
|
+
result["data"] = None
|
|
360
|
+
|
|
361
|
+
# Get file list
|
|
362
|
+
result["files"] = zb.namelist()
|
|
363
|
+
|
|
364
|
+
return result
|
|
365
|
+
else:
|
|
366
|
+
# Legacy: extract to temp and load
|
|
367
|
+
import tempfile
|
|
368
|
+
temp_dir = Path(tempfile.mkdtemp())
|
|
369
|
+
with zipfile.ZipFile(p, "r") as zf:
|
|
370
|
+
zf.extractall(temp_dir)
|
|
371
|
+
bundle_dir = temp_dir
|
|
372
|
+
else:
|
|
373
|
+
bundle_dir = p
|
|
374
|
+
|
|
375
|
+
# Delegate to domain-specific loaders (for directory bundles or legacy mode)
|
|
376
|
+
if bundle_type == BundleType.FIGZ:
|
|
377
|
+
from scitex.fig.io._bundle import load_figz_bundle
|
|
378
|
+
result.update(load_figz_bundle(bundle_dir))
|
|
379
|
+
elif bundle_type == BundleType.PLTZ:
|
|
380
|
+
from scitex.plt.io import load_layered_pltz_bundle
|
|
381
|
+
result.update(load_layered_pltz_bundle(bundle_dir))
|
|
382
|
+
elif bundle_type == BundleType.STATSZ:
|
|
383
|
+
from scitex.stats.io._bundle import load_statsz_bundle
|
|
384
|
+
result.update(load_statsz_bundle(bundle_dir))
|
|
385
|
+
|
|
386
|
+
return result
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def save_bundle(
|
|
390
|
+
data: Dict[str, Any],
|
|
391
|
+
path: Union[str, Path],
|
|
392
|
+
bundle_type: Optional[str] = None,
|
|
393
|
+
as_zip: bool = False,
|
|
394
|
+
atomic: bool = True,
|
|
395
|
+
) -> Path:
|
|
396
|
+
"""Save data as a bundle.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
data: Bundle data dictionary.
|
|
400
|
+
path: Output path (with or without .d suffix).
|
|
401
|
+
bundle_type: Bundle type ('figz', 'pltz', 'statsz'). Auto-detected if None.
|
|
402
|
+
as_zip: If True, save as ZIP archive.
|
|
403
|
+
atomic: If True, use atomic write (temp file + rename) for ZIP.
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
Path to saved bundle.
|
|
407
|
+
"""
|
|
408
|
+
p = Path(path)
|
|
409
|
+
|
|
410
|
+
# Determine bundle type
|
|
411
|
+
if bundle_type is None:
|
|
412
|
+
bundle_type = get_bundle_type(p)
|
|
413
|
+
if bundle_type is None:
|
|
414
|
+
raise ValueError(f"Cannot determine bundle type from path: {path}")
|
|
415
|
+
|
|
416
|
+
# Determine if saving as directory or ZIP
|
|
417
|
+
if as_zip or (p.suffix in BUNDLE_EXTENSIONS and not str(p).endswith(".d")):
|
|
418
|
+
save_as_zip = True
|
|
419
|
+
if p.suffix == ".d":
|
|
420
|
+
zip_path = dir_to_zip_path(p)
|
|
421
|
+
else:
|
|
422
|
+
zip_path = p
|
|
423
|
+
dir_path = zip_to_dir_path(zip_path)
|
|
424
|
+
else:
|
|
425
|
+
save_as_zip = False
|
|
426
|
+
if not str(p).endswith(".d"):
|
|
427
|
+
dir_path = Path(str(p) + ".d")
|
|
428
|
+
else:
|
|
429
|
+
dir_path = p
|
|
430
|
+
|
|
431
|
+
# For direct ZIP saving with atomic writes, use ZipBundle
|
|
432
|
+
# Note: figz bundles need special handling for nested pltz panels,
|
|
433
|
+
# so they go through the directory-based save_figz_bundle path
|
|
434
|
+
if save_as_zip and atomic and bundle_type != BundleType.FIGZ:
|
|
435
|
+
from ._zip_bundle import ZipBundle
|
|
436
|
+
|
|
437
|
+
with ZipBundle(zip_path, mode="w") as zb:
|
|
438
|
+
# Write spec
|
|
439
|
+
if "spec" in data:
|
|
440
|
+
zb.write_json("spec.json", data["spec"])
|
|
441
|
+
|
|
442
|
+
# Write style
|
|
443
|
+
if "style" in data:
|
|
444
|
+
zb.write_json("style.json", data["style"])
|
|
445
|
+
|
|
446
|
+
# Write CSV data
|
|
447
|
+
if "data" in data and data["data"] is not None:
|
|
448
|
+
import pandas as pd
|
|
449
|
+
if isinstance(data["data"], pd.DataFrame):
|
|
450
|
+
zb.write_csv("data.csv", data["data"])
|
|
451
|
+
|
|
452
|
+
# Write exports (PNG, SVG, etc.)
|
|
453
|
+
for key in ["png", "svg", "pdf"]:
|
|
454
|
+
if key in data and data[key] is not None:
|
|
455
|
+
export_data = data[key]
|
|
456
|
+
if isinstance(export_data, bytes):
|
|
457
|
+
zb.write_bytes(f"exports/figure.{key}", export_data)
|
|
458
|
+
|
|
459
|
+
return zip_path
|
|
460
|
+
|
|
461
|
+
# Create directory for non-ZIP or non-atomic saves
|
|
462
|
+
dir_path.mkdir(parents=True, exist_ok=True)
|
|
463
|
+
|
|
464
|
+
# Delegate to domain-specific savers
|
|
465
|
+
if bundle_type == BundleType.FIGZ:
|
|
466
|
+
from scitex.fig.io._bundle import save_figz_bundle
|
|
467
|
+
save_figz_bundle(data, dir_path)
|
|
468
|
+
elif bundle_type == BundleType.PLTZ:
|
|
469
|
+
# Note: This path is only reached when calling save_bundle() directly
|
|
470
|
+
# The main stx.io.save() flow uses _save_pltz_bundle() which handles layered format
|
|
471
|
+
from scitex.plt.io._bundle import save_pltz_bundle
|
|
472
|
+
save_pltz_bundle(data, dir_path)
|
|
473
|
+
elif bundle_type == BundleType.STATSZ:
|
|
474
|
+
from scitex.stats.io._bundle import save_statsz_bundle
|
|
475
|
+
save_statsz_bundle(data, dir_path)
|
|
476
|
+
else:
|
|
477
|
+
raise ValueError(f"Unknown bundle type: {bundle_type}")
|
|
478
|
+
|
|
479
|
+
# Pack to ZIP if requested (non-atomic path)
|
|
480
|
+
if save_as_zip:
|
|
481
|
+
pack_bundle(dir_path, zip_path)
|
|
482
|
+
shutil.rmtree(dir_path) # Remove temp directory
|
|
483
|
+
return zip_path
|
|
484
|
+
|
|
485
|
+
return dir_path
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
# Backward compatibility aliases
|
|
489
|
+
_get_bundle_type = get_bundle_type
|
|
490
|
+
_dir_to_zip_path = dir_to_zip_path
|
|
491
|
+
_zip_to_dir_path = zip_to_dir_path
|
|
492
|
+
|
|
493
|
+
# EOF
|
scitex/io/_flush.py
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
from scitex import logging
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
def flush(sys=sys):
|
|
@@ -14,7 +17,7 @@ def flush(sys=sys):
|
|
|
14
17
|
This ensures all pending write operations are completed.
|
|
15
18
|
"""
|
|
16
19
|
if sys is None:
|
|
17
|
-
|
|
20
|
+
logger.warning("flush needs sys. Skipping.")
|
|
18
21
|
else:
|
|
19
22
|
sys.stdout.flush()
|
|
20
23
|
sys.stderr.flush()
|
scitex/io/_load.py
CHANGED
|
@@ -48,6 +48,98 @@ from ._load_modules._zarr import _load_zarr
|
|
|
48
48
|
from ._load_modules._canvas import _load_canvas
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
def _load_bundle(lpath, verbose=False, **kwargs):
|
|
52
|
+
"""Load a .pltz, .figz, or .statsz bundle.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
lpath : str or Path
|
|
57
|
+
Path to the bundle (directory or ZIP).
|
|
58
|
+
verbose : bool
|
|
59
|
+
If True, print verbose output.
|
|
60
|
+
**kwargs
|
|
61
|
+
Additional arguments.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
For .pltz bundles:
|
|
66
|
+
tuple: (fig, ax, data) where fig is reconstructed figure,
|
|
67
|
+
ax is the axes, data is DataFrame or None.
|
|
68
|
+
For .figz bundles:
|
|
69
|
+
dict: Figure data with 'spec' and 'panels'.
|
|
70
|
+
For .statsz bundles:
|
|
71
|
+
dict: Stats data with 'spec' and 'comparisons'.
|
|
72
|
+
"""
|
|
73
|
+
from ._bundle import load_bundle, BundleType
|
|
74
|
+
|
|
75
|
+
bundle = load_bundle(lpath)
|
|
76
|
+
bundle_type = bundle.get('type')
|
|
77
|
+
|
|
78
|
+
if bundle_type == BundleType.PLTZ:
|
|
79
|
+
# Return (fig, ax, data) tuple for .pltz bundles
|
|
80
|
+
# Note: We return the spec and data, not a reconstructed figure
|
|
81
|
+
# as matplotlib figures cannot be perfectly serialized/deserialized
|
|
82
|
+
import matplotlib.pyplot as plt
|
|
83
|
+
from pathlib import Path
|
|
84
|
+
|
|
85
|
+
p = Path(lpath)
|
|
86
|
+
bundle_dir = p
|
|
87
|
+
|
|
88
|
+
# Handle ZIP extraction
|
|
89
|
+
if not p.is_dir():
|
|
90
|
+
import tempfile
|
|
91
|
+
import zipfile
|
|
92
|
+
temp_dir = Path(tempfile.mkdtemp())
|
|
93
|
+
with zipfile.ZipFile(p, 'r') as zf:
|
|
94
|
+
zf.extractall(temp_dir)
|
|
95
|
+
bundle_dir = temp_dir
|
|
96
|
+
|
|
97
|
+
# Find PNG file - layered format stores in exports/
|
|
98
|
+
basename = bundle.get('basename', 'plot')
|
|
99
|
+
png_path = bundle_dir / "exports" / f"{basename}.png"
|
|
100
|
+
if not png_path.exists():
|
|
101
|
+
# Fallback to root level (legacy format)
|
|
102
|
+
png_path = bundle_dir / f"{basename}.png"
|
|
103
|
+
|
|
104
|
+
# Load the PNG as a figure
|
|
105
|
+
if png_path.exists():
|
|
106
|
+
img = plt.imread(str(png_path))
|
|
107
|
+
fig, ax = plt.subplots()
|
|
108
|
+
ax.imshow(img)
|
|
109
|
+
ax.axis('off')
|
|
110
|
+
|
|
111
|
+
# Attach metadata from spec
|
|
112
|
+
spec = bundle.get('spec', {})
|
|
113
|
+
if spec:
|
|
114
|
+
# Handle both layered and legacy spec formats
|
|
115
|
+
axes_list = spec.get('axes', [])
|
|
116
|
+
if axes_list and isinstance(axes_list, list):
|
|
117
|
+
for key, val in axes_list[0].items():
|
|
118
|
+
setattr(ax, f'_scitex_{key}', val)
|
|
119
|
+
# Theme from style (layered) or spec (legacy)
|
|
120
|
+
style = bundle.get('style', {})
|
|
121
|
+
theme = style.get('theme', {}) if style else spec.get('theme', {})
|
|
122
|
+
if theme:
|
|
123
|
+
fig._scitex_theme = theme.get('mode')
|
|
124
|
+
|
|
125
|
+
# Data from bundle (merged in load_layered_pltz_bundle)
|
|
126
|
+
data = bundle.get('data')
|
|
127
|
+
return fig, ax, data
|
|
128
|
+
else:
|
|
129
|
+
# No PNG, return spec and data
|
|
130
|
+
return bundle.get('spec'), None, bundle.get('data')
|
|
131
|
+
|
|
132
|
+
elif bundle_type == BundleType.FIGZ:
|
|
133
|
+
# Return figure dict for .figz bundles
|
|
134
|
+
return bundle
|
|
135
|
+
|
|
136
|
+
elif bundle_type == BundleType.STATSZ:
|
|
137
|
+
# Return stats dict for .statsz bundles
|
|
138
|
+
return bundle
|
|
139
|
+
|
|
140
|
+
return bundle
|
|
141
|
+
|
|
142
|
+
|
|
51
143
|
def load(
|
|
52
144
|
lpath: Union[str, Path],
|
|
53
145
|
ext: str = None,
|
|
@@ -157,6 +249,12 @@ def load(
|
|
|
157
249
|
if lpath.endswith(".canvas"):
|
|
158
250
|
return _load_canvas(lpath, verbose=verbose, **kwargs)
|
|
159
251
|
|
|
252
|
+
# Handle bundle formats (.pltz, .figz, .statsz and their .d variants)
|
|
253
|
+
bundle_extensions = (".figz", ".pltz", ".statsz")
|
|
254
|
+
for bext in bundle_extensions:
|
|
255
|
+
if lpath.endswith(bext) or lpath.endswith(f"{bext}.d"):
|
|
256
|
+
return _load_bundle(lpath, verbose=verbose, **kwargs)
|
|
257
|
+
|
|
160
258
|
# Check if it's a glob pattern
|
|
161
259
|
if "*" in lpath or "?" in lpath or "[" in lpath:
|
|
162
260
|
# Handle glob pattern
|
|
@@ -11,7 +11,6 @@ __DIR__ = os.path.dirname(__FILE__)
|
|
|
11
11
|
|
|
12
12
|
import random
|
|
13
13
|
import time
|
|
14
|
-
import warnings
|
|
15
14
|
|
|
16
15
|
# Time-stamp: "2025-06-13 21:00:00 (ywatanabe)"
|
|
17
16
|
|
|
@@ -22,6 +21,10 @@ from typing import Any, Dict, List, Optional
|
|
|
22
21
|
import h5py
|
|
23
22
|
import numpy as np
|
|
24
23
|
|
|
24
|
+
from scitex import logging
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
25
28
|
|
|
26
29
|
class H5Explorer:
|
|
27
30
|
"""Interactive HDF5 file explorer.
|
|
@@ -262,7 +265,7 @@ def explore_h5(filepath: str) -> None:
|
|
|
262
265
|
explorer.explore()
|
|
263
266
|
explorer.close()
|
|
264
267
|
else:
|
|
265
|
-
|
|
268
|
+
logger.warning(f"File does not exist: {filepath}")
|
|
266
269
|
|
|
267
270
|
|
|
268
271
|
def has_h5_key(h5_path, key, max_retries=3, action_on_corrupted="delete"):
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Timestamp: 2025-12-08
|
|
4
4
|
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_load_modules/_canvas.py
|
|
5
5
|
"""
|
|
6
|
-
Load canvas directory (.canvas) for scitex.
|
|
6
|
+
Load canvas directory (.canvas) for scitex.fig.
|
|
7
7
|
|
|
8
8
|
Canvas directories are portable figure bundles containing:
|
|
9
9
|
- canvas.json: Layout, panels, composition settings
|
|
@@ -100,7 +100,7 @@ def _load_canvas(
|
|
|
100
100
|
# Return Canvas object by default
|
|
101
101
|
if not as_dict:
|
|
102
102
|
try:
|
|
103
|
-
from scitex.
|
|
103
|
+
from scitex.fig.canvas import Canvas
|
|
104
104
|
|
|
105
105
|
canvas_obj = Canvas.from_dict(canvas_dict)
|
|
106
106
|
# Store reference to original directory for copying
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
# Time-stamp: "2024-11-14 07:55:38 (ywatanabe)"
|
|
4
4
|
# File: ./scitex_repo/src/scitex/io/_load_modules/_image.py
|
|
5
5
|
|
|
6
|
-
import logging
|
|
7
6
|
from typing import Any, Dict, Optional, Tuple, Union
|
|
8
7
|
|
|
9
8
|
from PIL import Image
|
|
10
9
|
|
|
10
|
+
from scitex import logging
|
|
11
|
+
|
|
11
12
|
logger = logging.getLogger(__name__)
|
|
12
13
|
|
|
13
14
|
|
|
@@ -58,9 +59,7 @@ def _load_image(
|
|
|
58
59
|
|
|
59
60
|
except Exception as e:
|
|
60
61
|
# If metadata reading fails, return None
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
warnings.warn(f"Failed to read metadata: {e}")
|
|
62
|
+
logger.warning(f"Failed to read metadata: {e}")
|
|
64
63
|
return img, None
|
|
65
64
|
|
|
66
65
|
|