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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Timestamp: 2025-12-08
|
|
4
4
|
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_save_modules/_canvas.py
|
|
5
5
|
"""
|
|
6
|
-
Save canvas directory (.canvas) for scitex.
|
|
6
|
+
Save 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
|
|
@@ -113,7 +113,7 @@ def _export_canvas_figures(
|
|
|
113
113
|
formats = ["png", "pdf", "svg"]
|
|
114
114
|
|
|
115
115
|
try:
|
|
116
|
-
from scitex.
|
|
116
|
+
from scitex.fig.io.export import _compose_and_export
|
|
117
117
|
import json
|
|
118
118
|
|
|
119
119
|
# Load canvas.json
|
|
@@ -136,7 +136,7 @@ def _export_canvas_figures(
|
|
|
136
136
|
transparent=False,
|
|
137
137
|
)
|
|
138
138
|
except ImportError:
|
|
139
|
-
# scitex.
|
|
139
|
+
# scitex.fig not available
|
|
140
140
|
pass
|
|
141
141
|
except Exception as e:
|
|
142
142
|
# Log but don't fail save if export fails
|
|
@@ -8,11 +8,12 @@ import os
|
|
|
8
8
|
__FILE__ = __file__
|
|
9
9
|
|
|
10
10
|
import io as _io
|
|
11
|
-
import logging
|
|
12
11
|
|
|
13
12
|
import plotly
|
|
14
13
|
from PIL import Image
|
|
15
14
|
|
|
15
|
+
from scitex import logging
|
|
16
|
+
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
18
19
|
|
|
@@ -66,9 +67,7 @@ def save_image(
|
|
|
66
67
|
fig = obj if hasattr(obj, "savefig") else obj.figure
|
|
67
68
|
obj = add_qr_to_figure(fig, metadata, position=qr_position)
|
|
68
69
|
except Exception as e:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
warnings.warn(f"Failed to add QR code: {e}")
|
|
70
|
+
logger.warning(f"Failed to add QR code: {e}")
|
|
72
71
|
|
|
73
72
|
# png
|
|
74
73
|
if fmt == 'png':
|
|
@@ -216,9 +215,7 @@ def save_image(
|
|
|
216
215
|
if verbose:
|
|
217
216
|
logger.debug(f" • Embedded metadata: {metadata}")
|
|
218
217
|
except Exception as e:
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
warnings.warn(f"Failed to embed metadata: {e}")
|
|
218
|
+
logger.warning(f"Failed to embed metadata: {e}")
|
|
222
219
|
|
|
223
220
|
def _save_stats_from_figure(obj, spath, verbose=False):
|
|
224
221
|
"""
|
|
@@ -297,8 +294,7 @@ def _save_stats_from_figure(obj, spath, verbose=False):
|
|
|
297
294
|
logger.info(f" • Auto-saved stats to: {stats_path}")
|
|
298
295
|
|
|
299
296
|
except Exception as e:
|
|
300
|
-
|
|
301
|
-
warnings.warn(f"Failed to auto-save stats: {e}")
|
|
297
|
+
logger.warning(f"Failed to auto-save stats: {e}")
|
|
302
298
|
|
|
303
299
|
|
|
304
300
|
# EOF
|
scitex/io/_save_modules/_tex.py
CHANGED
|
@@ -20,7 +20,10 @@ Supports multiple input types:
|
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
from typing import Union, Optional, Any
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
from scitex import logging
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
24
27
|
|
|
25
28
|
|
|
26
29
|
def save_tex(
|
|
@@ -121,21 +124,21 @@ def save_tex(
|
|
|
121
124
|
tex_content = _wrap_with_table_env(tex_content, caption, label)
|
|
122
125
|
|
|
123
126
|
except ImportError:
|
|
124
|
-
|
|
127
|
+
logger.warning(
|
|
125
128
|
"Cannot convert dict/list to LaTeX: "
|
|
126
129
|
"scitex.stats.utils.convert_results not available. "
|
|
127
130
|
"Converting to string instead."
|
|
128
131
|
)
|
|
129
132
|
tex_content = str(obj)
|
|
130
133
|
except Exception as e:
|
|
131
|
-
|
|
134
|
+
logger.warning(
|
|
132
135
|
f"Failed to convert object to LaTeX: {e}. Converting to string instead."
|
|
133
136
|
)
|
|
134
137
|
tex_content = str(obj)
|
|
135
138
|
|
|
136
139
|
else:
|
|
137
140
|
# Fallback: convert to string
|
|
138
|
-
|
|
141
|
+
logger.warning(
|
|
139
142
|
f"Unsupported type {type(obj)} for LaTeX export. Converting to string."
|
|
140
143
|
)
|
|
141
144
|
tex_content = str(obj)
|
scitex/io/_zip_bundle.py
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-12-14 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/_zip_bundle.py
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
SciTeX ZipBundle - In-memory zip archive handler with atomic writes.
|
|
8
|
+
|
|
9
|
+
Provides efficient access to .figz, .pltz, .statsz bundles without extracting
|
|
10
|
+
to disk. Supports:
|
|
11
|
+
- In-memory file reading
|
|
12
|
+
- Atomic writes using temp files
|
|
13
|
+
- Context manager protocol
|
|
14
|
+
- Dict-like access to files
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import io
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import shutil
|
|
21
|
+
import tempfile
|
|
22
|
+
import zipfile
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any, BinaryIO, Dict, Iterator, List, Optional, Union
|
|
25
|
+
|
|
26
|
+
import pandas as pd
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"ZipBundle",
|
|
30
|
+
"open_bundle",
|
|
31
|
+
"create_bundle",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ZipBundle:
|
|
36
|
+
"""In-memory zip bundle handler.
|
|
37
|
+
|
|
38
|
+
Provides efficient read/write access to zip archives without extracting
|
|
39
|
+
to disk. Files are read into memory on demand and writes are atomic.
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
# Reading
|
|
43
|
+
with ZipBundle("figure.figz") as bundle:
|
|
44
|
+
spec = bundle.read_json("spec.json")
|
|
45
|
+
csv_data = bundle.read_csv("data.csv")
|
|
46
|
+
png_bytes = bundle.read_bytes("exports/figure.png")
|
|
47
|
+
|
|
48
|
+
# Writing
|
|
49
|
+
with ZipBundle("output.pltz", mode="w") as bundle:
|
|
50
|
+
bundle.write_json("spec.json", spec_dict)
|
|
51
|
+
bundle.write_csv("data.csv", dataframe)
|
|
52
|
+
bundle.write_bytes("exports/plot.png", png_bytes)
|
|
53
|
+
|
|
54
|
+
# Modifying (read then write atomically)
|
|
55
|
+
with ZipBundle("figure.figz", mode="a") as bundle:
|
|
56
|
+
spec = bundle.read_json("spec.json")
|
|
57
|
+
spec["title"] = "Updated"
|
|
58
|
+
bundle.write_json("spec.json", spec)
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
path: Union[str, Path],
|
|
64
|
+
mode: str = "r",
|
|
65
|
+
compression: int = zipfile.ZIP_DEFLATED,
|
|
66
|
+
):
|
|
67
|
+
"""Initialize ZipBundle.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
path: Path to zip file (.figz, .pltz, .statsz)
|
|
71
|
+
mode: 'r' for read, 'w' for write, 'a' for append/modify
|
|
72
|
+
compression: ZIP compression method (default: ZIP_DEFLATED)
|
|
73
|
+
"""
|
|
74
|
+
self.path = Path(path)
|
|
75
|
+
self.mode = mode
|
|
76
|
+
self.compression = compression
|
|
77
|
+
self._zipfile: Optional[zipfile.ZipFile] = None
|
|
78
|
+
self._cache: Dict[str, bytes] = {} # Cache for read files
|
|
79
|
+
self._pending_writes: Dict[str, bytes] = {} # Pending writes for atomic commit
|
|
80
|
+
self._temp_path: Optional[Path] = None
|
|
81
|
+
self._closed = False
|
|
82
|
+
|
|
83
|
+
def __enter__(self) -> "ZipBundle":
|
|
84
|
+
"""Enter context manager."""
|
|
85
|
+
self.open()
|
|
86
|
+
return self
|
|
87
|
+
|
|
88
|
+
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
|
89
|
+
"""Exit context manager with atomic commit on success."""
|
|
90
|
+
if exc_type is None and self.mode in ("w", "a"):
|
|
91
|
+
self._atomic_commit()
|
|
92
|
+
self.close()
|
|
93
|
+
|
|
94
|
+
def open(self) -> None:
|
|
95
|
+
"""Open the zip bundle."""
|
|
96
|
+
if self.mode == "r":
|
|
97
|
+
if not self.path.exists():
|
|
98
|
+
raise FileNotFoundError(f"Bundle not found: {self.path}")
|
|
99
|
+
self._zipfile = zipfile.ZipFile(self.path, "r")
|
|
100
|
+
elif self.mode == "w":
|
|
101
|
+
# Create new archive (write to temp, commit on close)
|
|
102
|
+
self._temp_path = Path(tempfile.mktemp(suffix=self.path.suffix))
|
|
103
|
+
self._zipfile = zipfile.ZipFile(
|
|
104
|
+
self._temp_path, "w", self.compression
|
|
105
|
+
)
|
|
106
|
+
elif self.mode == "a":
|
|
107
|
+
# Append mode: read existing, write to temp, commit atomically
|
|
108
|
+
if self.path.exists():
|
|
109
|
+
# Load existing contents into cache
|
|
110
|
+
with zipfile.ZipFile(self.path, "r") as zf:
|
|
111
|
+
for name in zf.namelist():
|
|
112
|
+
self._cache[name] = zf.read(name)
|
|
113
|
+
self._temp_path = Path(tempfile.mktemp(suffix=self.path.suffix))
|
|
114
|
+
self._zipfile = zipfile.ZipFile(
|
|
115
|
+
self._temp_path, "w", self.compression
|
|
116
|
+
)
|
|
117
|
+
else:
|
|
118
|
+
raise ValueError(f"Invalid mode: {self.mode}")
|
|
119
|
+
|
|
120
|
+
def close(self) -> None:
|
|
121
|
+
"""Close the zip bundle."""
|
|
122
|
+
if self._zipfile and not self._closed:
|
|
123
|
+
self._zipfile.close()
|
|
124
|
+
self._closed = True
|
|
125
|
+
# Clean up temp file if not committed (error case)
|
|
126
|
+
if self._temp_path and self._temp_path.exists():
|
|
127
|
+
if self.mode in ("w", "a") and not self.path.exists():
|
|
128
|
+
# Commit wasn't called, clean up
|
|
129
|
+
self._temp_path.unlink()
|
|
130
|
+
|
|
131
|
+
def _atomic_commit(self) -> None:
|
|
132
|
+
"""Atomically commit writes by renaming temp file.
|
|
133
|
+
|
|
134
|
+
This ensures the bundle is never in an inconsistent state.
|
|
135
|
+
Either the old file exists completely, or the new one does.
|
|
136
|
+
"""
|
|
137
|
+
if self._temp_path is None:
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
# Write all pending and cached content to the temp zip
|
|
141
|
+
self._zipfile.close()
|
|
142
|
+
self._closed = True
|
|
143
|
+
|
|
144
|
+
# Atomic rename (works on same filesystem)
|
|
145
|
+
# On Windows, we need to remove target first
|
|
146
|
+
if os.name == "nt" and self.path.exists():
|
|
147
|
+
backup_path = self.path.with_suffix(self.path.suffix + ".bak")
|
|
148
|
+
self.path.rename(backup_path)
|
|
149
|
+
try:
|
|
150
|
+
self._temp_path.rename(self.path)
|
|
151
|
+
backup_path.unlink()
|
|
152
|
+
except Exception:
|
|
153
|
+
# Restore backup on failure
|
|
154
|
+
backup_path.rename(self.path)
|
|
155
|
+
raise
|
|
156
|
+
else:
|
|
157
|
+
# Unix: atomic rename
|
|
158
|
+
self._temp_path.rename(self.path)
|
|
159
|
+
|
|
160
|
+
# =========================================================================
|
|
161
|
+
# File listing
|
|
162
|
+
# =========================================================================
|
|
163
|
+
|
|
164
|
+
def namelist(self) -> List[str]:
|
|
165
|
+
"""List all files in the bundle."""
|
|
166
|
+
if self.mode == "r":
|
|
167
|
+
return self._zipfile.namelist()
|
|
168
|
+
else:
|
|
169
|
+
# Include both cached and pending writes
|
|
170
|
+
names = set(self._cache.keys())
|
|
171
|
+
names.update(self._pending_writes.keys())
|
|
172
|
+
return sorted(names)
|
|
173
|
+
|
|
174
|
+
def __contains__(self, name: str) -> bool:
|
|
175
|
+
"""Check if file exists in bundle."""
|
|
176
|
+
return name in self.namelist()
|
|
177
|
+
|
|
178
|
+
def __iter__(self) -> Iterator[str]:
|
|
179
|
+
"""Iterate over file names in bundle."""
|
|
180
|
+
return iter(self.namelist())
|
|
181
|
+
|
|
182
|
+
# =========================================================================
|
|
183
|
+
# Reading
|
|
184
|
+
# =========================================================================
|
|
185
|
+
|
|
186
|
+
def read_bytes(self, name: str) -> bytes:
|
|
187
|
+
"""Read file as bytes.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
name: File path within the zip (e.g., "spec.json", "exports/plot.png")
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
File contents as bytes.
|
|
194
|
+
"""
|
|
195
|
+
# Check cache first (for append mode)
|
|
196
|
+
if name in self._pending_writes:
|
|
197
|
+
return self._pending_writes[name]
|
|
198
|
+
if name in self._cache:
|
|
199
|
+
return self._cache[name]
|
|
200
|
+
|
|
201
|
+
# Read from zip
|
|
202
|
+
if self._zipfile is None:
|
|
203
|
+
raise RuntimeError("Bundle not opened")
|
|
204
|
+
|
|
205
|
+
# Handle files inside .d directory structure
|
|
206
|
+
# Try direct name first, then with .d prefix
|
|
207
|
+
try:
|
|
208
|
+
data = self._zipfile.read(name)
|
|
209
|
+
self._cache[name] = data
|
|
210
|
+
return data
|
|
211
|
+
except KeyError:
|
|
212
|
+
# Try with directory prefix (e.g., "bundle.pltz.d/spec.json")
|
|
213
|
+
# The path.name includes extension, so "foo.pltz" -> "foo.pltz.d"
|
|
214
|
+
dir_name = self.path.name + ".d"
|
|
215
|
+
full_name = f"{dir_name}/{name}"
|
|
216
|
+
try:
|
|
217
|
+
data = self._zipfile.read(full_name)
|
|
218
|
+
self._cache[name] = data
|
|
219
|
+
return data
|
|
220
|
+
except KeyError:
|
|
221
|
+
# Try to find the .d directory dynamically
|
|
222
|
+
# (handles case where pltz was renamed, e.g., panel_A.pltz -> A.pltz
|
|
223
|
+
# but internal structure is still panel_A.pltz.d/)
|
|
224
|
+
for arc_name in self._zipfile.namelist():
|
|
225
|
+
if arc_name.endswith('.d/' + name) or arc_name.endswith('.d/' + name.replace('/', '/')):
|
|
226
|
+
data = self._zipfile.read(arc_name)
|
|
227
|
+
self._cache[name] = data
|
|
228
|
+
return data
|
|
229
|
+
raise FileNotFoundError(f"File not found in bundle: {name}")
|
|
230
|
+
|
|
231
|
+
def read_text(self, name: str, encoding: str = "utf-8") -> str:
|
|
232
|
+
"""Read file as text.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
name: File path within the zip.
|
|
236
|
+
encoding: Text encoding (default: utf-8).
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
File contents as string.
|
|
240
|
+
"""
|
|
241
|
+
return self.read_bytes(name).decode(encoding)
|
|
242
|
+
|
|
243
|
+
def read_json(self, name: str = "spec.json") -> Dict[str, Any]:
|
|
244
|
+
"""Read and parse JSON file.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
name: JSON file path within the zip.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Parsed JSON as dictionary.
|
|
251
|
+
"""
|
|
252
|
+
return json.loads(self.read_text(name))
|
|
253
|
+
|
|
254
|
+
def read_csv(self, name: str = "data.csv", **kwargs) -> pd.DataFrame:
|
|
255
|
+
"""Read CSV file as pandas DataFrame.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
name: CSV file path within the zip.
|
|
259
|
+
**kwargs: Additional arguments passed to pd.read_csv.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
DataFrame with CSV contents.
|
|
263
|
+
"""
|
|
264
|
+
data = self.read_bytes(name)
|
|
265
|
+
return pd.read_csv(io.BytesIO(data), **kwargs)
|
|
266
|
+
|
|
267
|
+
def read_image(self, name: str) -> bytes:
|
|
268
|
+
"""Read image file as bytes.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
name: Image file path (e.g., "exports/figure.png").
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Image bytes.
|
|
275
|
+
"""
|
|
276
|
+
return self.read_bytes(name)
|
|
277
|
+
|
|
278
|
+
# =========================================================================
|
|
279
|
+
# Writing
|
|
280
|
+
# =========================================================================
|
|
281
|
+
|
|
282
|
+
def write_bytes(self, name: str, data: bytes) -> None:
|
|
283
|
+
"""Write bytes to file in bundle.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
name: File path within the zip.
|
|
287
|
+
data: Bytes to write.
|
|
288
|
+
"""
|
|
289
|
+
if self.mode == "r":
|
|
290
|
+
raise RuntimeError("Cannot write in read mode")
|
|
291
|
+
|
|
292
|
+
self._pending_writes[name] = data
|
|
293
|
+
self._cache[name] = data # Update cache
|
|
294
|
+
|
|
295
|
+
# Write to zip immediately
|
|
296
|
+
self._zipfile.writestr(name, data)
|
|
297
|
+
|
|
298
|
+
def write_text(self, name: str, text: str, encoding: str = "utf-8") -> None:
|
|
299
|
+
"""Write text to file in bundle.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
name: File path within the zip.
|
|
303
|
+
text: Text to write.
|
|
304
|
+
encoding: Text encoding (default: utf-8).
|
|
305
|
+
"""
|
|
306
|
+
self.write_bytes(name, text.encode(encoding))
|
|
307
|
+
|
|
308
|
+
def write_json(
|
|
309
|
+
self, name: str, data: Dict[str, Any], indent: int = 2
|
|
310
|
+
) -> None:
|
|
311
|
+
"""Write dictionary as JSON file.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
name: JSON file path within the zip.
|
|
315
|
+
data: Dictionary to serialize.
|
|
316
|
+
indent: JSON indentation (default: 2).
|
|
317
|
+
"""
|
|
318
|
+
text = json.dumps(data, indent=indent, ensure_ascii=False)
|
|
319
|
+
self.write_text(name, text)
|
|
320
|
+
|
|
321
|
+
def write_csv(
|
|
322
|
+
self, name: str, df: pd.DataFrame, index: bool = False, **kwargs
|
|
323
|
+
) -> None:
|
|
324
|
+
"""Write pandas DataFrame as CSV.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
name: CSV file path within the zip.
|
|
328
|
+
df: DataFrame to write.
|
|
329
|
+
index: Include index column (default: False).
|
|
330
|
+
**kwargs: Additional arguments passed to df.to_csv.
|
|
331
|
+
"""
|
|
332
|
+
buffer = io.BytesIO()
|
|
333
|
+
df.to_csv(buffer, index=index, **kwargs)
|
|
334
|
+
self.write_bytes(name, buffer.getvalue())
|
|
335
|
+
|
|
336
|
+
def write_image(self, name: str, data: bytes) -> None:
|
|
337
|
+
"""Write image bytes to bundle.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
name: Image file path (e.g., "exports/figure.png").
|
|
341
|
+
data: Image bytes.
|
|
342
|
+
"""
|
|
343
|
+
self.write_bytes(name, data)
|
|
344
|
+
|
|
345
|
+
# =========================================================================
|
|
346
|
+
# Convenience methods
|
|
347
|
+
# =========================================================================
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def spec(self) -> Optional[Dict[str, Any]]:
|
|
351
|
+
"""Get bundle specification (spec.json)."""
|
|
352
|
+
try:
|
|
353
|
+
return self.read_json("spec.json")
|
|
354
|
+
except FileNotFoundError:
|
|
355
|
+
return None
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
def style(self) -> Optional[Dict[str, Any]]:
|
|
359
|
+
"""Get bundle style (style.json)."""
|
|
360
|
+
try:
|
|
361
|
+
return self.read_json("style.json")
|
|
362
|
+
except FileNotFoundError:
|
|
363
|
+
return None
|
|
364
|
+
|
|
365
|
+
@property
|
|
366
|
+
def data(self) -> Optional[pd.DataFrame]:
|
|
367
|
+
"""Get bundle data (data.csv)."""
|
|
368
|
+
try:
|
|
369
|
+
return self.read_csv("data.csv")
|
|
370
|
+
except FileNotFoundError:
|
|
371
|
+
return None
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def open_bundle(
|
|
375
|
+
path: Union[str, Path], mode: str = "r"
|
|
376
|
+
) -> ZipBundle:
|
|
377
|
+
"""Open a bundle for reading or writing.
|
|
378
|
+
|
|
379
|
+
Args:
|
|
380
|
+
path: Path to bundle file.
|
|
381
|
+
mode: 'r' for read, 'w' for write, 'a' for append.
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
ZipBundle instance (use as context manager).
|
|
385
|
+
|
|
386
|
+
Example:
|
|
387
|
+
with open_bundle("figure.figz") as bundle:
|
|
388
|
+
spec = bundle.spec
|
|
389
|
+
data = bundle.data
|
|
390
|
+
"""
|
|
391
|
+
return ZipBundle(path, mode=mode)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def create_bundle(
|
|
395
|
+
path: Union[str, Path],
|
|
396
|
+
spec: Dict[str, Any],
|
|
397
|
+
data: Optional[pd.DataFrame] = None,
|
|
398
|
+
style: Optional[Dict[str, Any]] = None,
|
|
399
|
+
exports: Optional[Dict[str, bytes]] = None,
|
|
400
|
+
) -> Path:
|
|
401
|
+
"""Create a new bundle with atomic write.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
path: Output path (.figz, .pltz, or .statsz).
|
|
405
|
+
spec: Bundle specification dictionary.
|
|
406
|
+
data: Optional CSV data as DataFrame.
|
|
407
|
+
style: Optional style dictionary.
|
|
408
|
+
exports: Optional dict mapping export paths to bytes.
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
Path to created bundle.
|
|
412
|
+
|
|
413
|
+
Example:
|
|
414
|
+
create_bundle(
|
|
415
|
+
"plot.pltz",
|
|
416
|
+
spec={"schema": {"name": "scitex.plt", "version": "1.0"}},
|
|
417
|
+
data=df,
|
|
418
|
+
exports={"exports/plot.png": png_bytes},
|
|
419
|
+
)
|
|
420
|
+
"""
|
|
421
|
+
path = Path(path)
|
|
422
|
+
|
|
423
|
+
with ZipBundle(path, mode="w") as bundle:
|
|
424
|
+
bundle.write_json("spec.json", spec)
|
|
425
|
+
|
|
426
|
+
if data is not None:
|
|
427
|
+
bundle.write_csv("data.csv", data)
|
|
428
|
+
|
|
429
|
+
if style is not None:
|
|
430
|
+
bundle.write_json("style.json", style)
|
|
431
|
+
|
|
432
|
+
if exports:
|
|
433
|
+
for export_path, export_data in exports.items():
|
|
434
|
+
bundle.write_bytes(export_path, export_data)
|
|
435
|
+
|
|
436
|
+
return path
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
# EOF
|
scitex/io/utils/h5_to_zarr.py
CHANGED
|
@@ -33,9 +33,9 @@ import numpy as np
|
|
|
33
33
|
import os
|
|
34
34
|
from pathlib import Path
|
|
35
35
|
from typing import Optional, Union, Dict, Any, List, Tuple
|
|
36
|
-
import warnings
|
|
37
36
|
from tqdm import tqdm
|
|
38
37
|
|
|
38
|
+
from scitex import logging
|
|
39
39
|
from scitex.errors import (
|
|
40
40
|
IOError as SciTeXIOError,
|
|
41
41
|
FileFormatError,
|
|
@@ -46,6 +46,8 @@ from scitex.errors import (
|
|
|
46
46
|
warn_performance,
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
+
logger = logging.getLogger(__name__)
|
|
50
|
+
|
|
49
51
|
|
|
50
52
|
def _get_zarr_compressor(
|
|
51
53
|
compressor: Optional[Union[str, Any]] = "zstd",
|
|
@@ -114,7 +116,7 @@ def _copy_h5_attributes(
|
|
|
114
116
|
|
|
115
117
|
zarr_obj.attrs[key] = value
|
|
116
118
|
except Exception as e:
|
|
117
|
-
|
|
119
|
+
logger.warning(f"Could not copy attribute '{key}': {e}")
|
|
118
120
|
|
|
119
121
|
|
|
120
122
|
def _migrate_dataset(
|
|
@@ -132,7 +134,7 @@ def _migrate_dataset(
|
|
|
132
134
|
if hasattr(h5_dataset, "dtype"):
|
|
133
135
|
test_dtype = h5_dataset.dtype
|
|
134
136
|
except Exception as e:
|
|
135
|
-
|
|
137
|
+
logger.warning(f"Skipping corrupted dataset '{name}': {e}")
|
|
136
138
|
return None
|
|
137
139
|
|
|
138
140
|
# Get dataset info
|
|
@@ -241,7 +243,7 @@ def _migrate_dataset(
|
|
|
241
243
|
else: # Scalar
|
|
242
244
|
zarr_array[()] = h5_dataset[()]
|
|
243
245
|
except Exception as e:
|
|
244
|
-
|
|
246
|
+
logger.warning(
|
|
245
247
|
f"Error copying data for dataset '{name}': {e}. Leaving empty."
|
|
246
248
|
)
|
|
247
249
|
# The array structure is created but data might be zeros/empty
|
|
@@ -268,14 +270,14 @@ def _migrate_group(
|
|
|
268
270
|
try:
|
|
269
271
|
keys = list(h5_group.keys())
|
|
270
272
|
except Exception as e:
|
|
271
|
-
|
|
273
|
+
logger.warning(f"Cannot access group keys: {e}")
|
|
272
274
|
return
|
|
273
275
|
|
|
274
276
|
for key in keys:
|
|
275
277
|
try:
|
|
276
278
|
item = h5_group[key]
|
|
277
279
|
except Exception as e:
|
|
278
|
-
|
|
280
|
+
logger.warning(f"Cannot access item '{key}': {e}")
|
|
279
281
|
continue
|
|
280
282
|
|
|
281
283
|
if isinstance(item, h5py.Dataset):
|
|
@@ -297,7 +299,7 @@ def _migrate_group(
|
|
|
297
299
|
)
|
|
298
300
|
|
|
299
301
|
else:
|
|
300
|
-
|
|
302
|
+
logger.warning(f"Unknown HDF5 object type for '{key}': {type(item)}")
|
|
301
303
|
|
|
302
304
|
|
|
303
305
|
def migrate_h5_to_zarr(
|
|
@@ -418,7 +420,7 @@ def migrate_h5_to_zarr(
|
|
|
418
420
|
except OSError as e:
|
|
419
421
|
if "Unable to open file" in str(e) or "bad symbol table" in str(e):
|
|
420
422
|
# File is corrupted
|
|
421
|
-
|
|
423
|
+
logger.warning(f"HDF5 file appears to be corrupted: {h5_path}")
|
|
422
424
|
raise FileFormatError(
|
|
423
425
|
str(h5_path), expected_format="HDF5", actual_format="corrupted HDF5"
|
|
424
426
|
)
|
|
@@ -452,7 +454,7 @@ def _validate_migration(
|
|
|
452
454
|
# Compare dtypes (approximately)
|
|
453
455
|
if h5_item.dtype.kind != "O" and zarr_item.dtype.kind != "O":
|
|
454
456
|
if h5_item.dtype != zarr_item.dtype:
|
|
455
|
-
|
|
457
|
+
logger.warning(
|
|
456
458
|
f"Dtype mismatch at {path}: "
|
|
457
459
|
f"HDF5={h5_item.dtype}, Zarr={zarr_item.dtype}"
|
|
458
460
|
)
|