scitex 2.10.2__py3-none-any.whl → 2.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scitex/__init__.py +1 -4
- scitex/__version__.py +1 -1
- scitex/_install_guide.py +14 -2
- scitex/bridge/_figrecipe.py +1 -1
- scitex/bridge/_helpers.py +1 -1
- scitex/bridge/_plt_vis.py +1 -1
- scitex/bridge/_stats_plt.py +1 -1
- scitex/bridge/_stats_vis.py +2 -2
- scitex/{fig → canvas}/__init__.py +84 -96
- scitex/{fig → canvas}/backend/_parser.py +1 -1
- scitex/{fig → canvas}/canvas.py +13 -14
- scitex/{fts/_fig/_editor → canvas/editor}/_defaults.py +2 -2
- scitex/{fig → canvas}/editor/edit/__init__.py +11 -14
- scitex/{fig → canvas}/editor/edit/bundle_resolver.py +56 -48
- scitex/{fig → canvas}/editor/edit/editor_launcher.py +79 -26
- scitex/{fts/_fig/_editor/_cui/_panel_loader.py → canvas/editor/edit/panel_loader.py} +8 -8
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_bbox.py +2 -1
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_core.py +84 -84
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/_renderer.py +7 -6
- scitex/{fts/_fig/_editor/_gui/_flask_editor → canvas/editor/flask_editor}/static/css/features/canvas.css +2 -2
- scitex/{fig → canvas}/editor/flask_editor/static/css/features/panel-grid.css +1 -1
- scitex/{fig → canvas}/editor/flask_editor/static/js/core/api.js +3 -4
- scitex/{fig → canvas}/editor/flask_editor/static/js/editor/preview.js +5 -5
- scitex/{fig → canvas}/editor/flask_editor/templates/_html.py +3 -3
- scitex/{fig → canvas}/editor/flask_editor/templates/_scripts.py +10 -10
- scitex/{fig → canvas}/editor/flask_editor/templates/_styles.py +3 -3
- scitex/{fig → canvas}/io/__init__.py +32 -38
- scitex/{fig → canvas}/io/_bundle.py +217 -154
- scitex/{fig → canvas}/io/_canvas.py +1 -1
- scitex/{fig → canvas}/io/_data.py +1 -1
- scitex/{fig → canvas}/io/_export.py +1 -1
- scitex/{fig → canvas}/io/_load.py +1 -1
- scitex/{fig → canvas}/io/_panel.py +1 -1
- scitex/{fig → canvas}/io/_save.py +1 -1
- scitex/{fig → canvas}/model/__init__.py +1 -1
- scitex/{fig → canvas}/model/_annotations.py +1 -1
- scitex/{fig → canvas}/model/_axes.py +1 -1
- scitex/{fig → canvas}/model/_figure.py +1 -1
- scitex/{fig → canvas}/model/_guides.py +1 -1
- scitex/{fig → canvas}/model/_plot.py +1 -1
- scitex/{fig → canvas}/model/_styles.py +1 -1
- scitex/{fig → canvas}/utils/__init__.py +1 -1
- scitex/cli/convert.py +10 -6
- scitex/diagram/README.md +7 -7
- scitex/io/__init__.py +7 -19
- scitex/io/_load.py +15 -19
- scitex/io/_load_modules/_canvas.py +2 -2
- scitex/io/_load_modules/_con.py +5 -5
- scitex/io/_load_modules/_eeg.py +16 -12
- scitex/io/_save.py +11 -16
- scitex/io/_save_modules/__init__.py +6 -10
- scitex/io/_save_modules/_canvas.py +3 -3
- scitex/io/_save_modules/_plot_bundle.py +112 -0
- scitex/io/_save_modules/{_pltz_stx.py → _plot_scitex.py} +7 -7
- scitex/io/_save_modules/_stx_bundle.py +16 -16
- scitex/io/bundle/README.md +89 -80
- scitex/{fts/_bundle/_FTS.py → io/bundle/_Bundle.py} +197 -95
- scitex/io/bundle/__init__.py +67 -35
- scitex/{fts/_bundle → io/bundle}/_children.py +32 -40
- scitex/io/bundle/_core.py +184 -97
- scitex/{fts/_bundle/_dataclasses/_Node.py → io/bundle/_dataclasses/_Spec.py} +29 -23
- scitex/{fts/_bundle/_dataclasses/_NodeRefs.py → io/bundle/_dataclasses/_SpecRefs.py} +6 -6
- scitex/{fts/_bundle → io/bundle}/_dataclasses/__init__.py +4 -4
- scitex/{fts/_bundle → io/bundle}/_loader.py +19 -19
- scitex/io/bundle/_manifest.py +99 -0
- scitex/{fts/_bundle → io/bundle}/_mpl_helpers.py +119 -28
- scitex/io/bundle/_nested.py +113 -100
- scitex/{fts/_bundle → io/bundle}/_saver.py +13 -14
- scitex/{fts/_bundle → io/bundle}/_storage.py +3 -3
- scitex/io/bundle/_types.py +41 -16
- scitex/{fts/_bundle → io/bundle}/_validation.py +20 -18
- scitex/io/bundle/_zip.py +21 -31
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_backend/_parser.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Annotations.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Axes.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Figure.py +1 -1
- scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Guides.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_models/_Plot.py +1 -1
- scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_Styles.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_plot/_utils/_plot_layout.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/__init__.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/_app.py +1 -1
- scitex/{fts/_tables → io/bundle/kinds/_table}/_latex/_export.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_figure_exporter.py +1 -1
- scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_table_exporter.py +1 -1
- scitex/io/bundle/schemas/__init__.py +30 -0
- scitex/parallel/_run.py +5 -4
- scitex/path/_find.py +60 -83
- scitex/path/_get_module_path.py +23 -21
- scitex/path/_get_spath.py +6 -27
- scitex/path/_getsize.py +23 -9
- scitex/path/_increment_version.py +31 -38
- scitex/path/_mk_spath.py +26 -29
- scitex/path/_path.py +5 -12
- scitex/path/_split.py +27 -15
- scitex/path/_this_path.py +23 -9
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin/__init__.py +2 -1
- scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +2 -2
- scitex/plt/gallery/_generate.py +76 -50
- scitex/plt/io/__init__.py +17 -19
- scitex/plt/io/_bundle.py +99 -52
- scitex/plt/io/_layered_bundle.py +303 -168
- scitex/plt/utils/_csv_column_naming.py +250 -118
- scitex/schema/__init__.py +69 -73
- scitex/schema/_canvas.py +1 -1
- scitex/schema/_stats.py +2 -2
- scitex/stats/__init__.py +30 -33
- scitex/stats/_schema.py +1 -1
- scitex/stats/io/__init__.py +10 -11
- scitex/stats/io/_bundle.py +16 -16
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/METADATA +191 -72
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/RECORD +237 -360
- scitex/fig/editor/_defaults.py +0 -300
- scitex/fig/editor/edit/panel_loader.py +0 -232
- scitex/fig/editor/flask_editor/_bbox.py +0 -1299
- scitex/fig/editor/flask_editor/_core.py +0 -1429
- scitex/fig/editor/flask_editor/_renderer.py +0 -813
- scitex/fig/editor/flask_editor/static/css/features/canvas.css +0 -176
- scitex/fts/README.md +0 -262
- scitex/fts/TODO.md +0 -66
- scitex/fts/__init__.py +0 -90
- scitex/fts/_bundle/README_IN_BUNDLE.md +0 -102
- scitex/fts/_bundle/__init__.py +0 -38
- scitex/fts/_bundle/_utils/__init__.py +0 -55
- scitex/fts/_bundle/_utils/_const.py +0 -26
- scitex/fts/_bundle/_utils/_errors.py +0 -73
- scitex/fts/_bundle/_utils/_generate.py +0 -21
- scitex/fts/_bundle/_utils/_types.py +0 -76
- scitex/fts/_bundle/_zipbundle.py +0 -165
- scitex/fts/_fig/__init__.py +0 -22
- scitex/fts/_fig/_backend/_parser.py +0 -188
- scitex/fts/_fig/_editor/__init__.py +0 -14
- scitex/fts/_fig/_editor/_cui/__init__.py +0 -33
- scitex/fts/_fig/_editor/_cui/_backend_detector.py +0 -39
- scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +0 -366
- scitex/fts/_fig/_editor/_cui/_editor_launcher.py +0 -175
- scitex/fts/_fig/_editor/_cui/_manual_handler.py +0 -52
- scitex/fts/_fig/_editor/_cui/_path_resolver.py +0 -66
- scitex/fts/_fig/_editor/_gui/__init__.py +0 -11
- scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +0 -20
- scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +0 -664
- scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +0 -79
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +0 -41
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +0 -16
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +0 -85
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +0 -217
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +0 -93
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +0 -57
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +0 -112
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +0 -59
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +0 -212
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +0 -190
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +0 -59
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +0 -45
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +0 -95
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +0 -101
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +0 -138
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +0 -31
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +0 -7
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +0 -56
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +0 -78
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +0 -314
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +0 -107
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +0 -54
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +0 -172
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +0 -258
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +0 -48
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +0 -71
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +0 -288
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +0 -143
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +0 -245
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +0 -992
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +0 -339
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +0 -286
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +0 -371
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +0 -293
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +0 -426
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +0 -152
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +0 -265
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +0 -184
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +0 -57
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +0 -100
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +0 -34
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +0 -124
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +0 -851
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +0 -4932
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +0 -1657
- scitex/fts/_fig/_editor/_gui/_flask_editor.py +0 -36
- scitex/fts/_fig/_models/_Annotations.py +0 -115
- scitex/fts/_fig/_models/_Axes.py +0 -152
- scitex/fts/_fig/_models/_Figure.py +0 -138
- scitex/fts/_fig/_models/_Plot.py +0 -123
- scitex/fts/_fig/_utils/_plot_layout.py +0 -397
- scitex/fts/_kinds/_figure/_composite.py +0 -345
- scitex/fts/_kinds/_plot/_backend/__init__.py +0 -53
- scitex/fts/_kinds/_plot/_backend/_export.py +0 -165
- scitex/fts/_kinds/_plot/_backend/_render.py +0 -538
- scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +0 -46
- scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +0 -82
- scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +0 -441
- scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +0 -52
- scitex/fts/_kinds/_plot/_dataclasses/__init__.py +0 -47
- scitex/fts/_kinds/_plot/_models/_Guides.py +0 -104
- scitex/fts/_kinds/_plot/_models/_Styles.py +0 -245
- scitex/fts/_kinds/_plot/_models/__init__.py +0 -80
- scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +0 -156
- scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +0 -43
- scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +0 -38
- scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +0 -36
- scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +0 -60
- scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +0 -30
- scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +0 -61
- scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +0 -57
- scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +0 -30
- scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +0 -121
- scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +0 -36
- scitex/fts/_kinds/_plot/_utils/__init__.py +0 -129
- scitex/fts/_kinds/_plot/_utils/_auto_layout.py +0 -127
- scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +0 -111
- scitex/fts/_kinds/_plot/_utils/_const_sizes.py +0 -48
- scitex/fts/_kinds/_plot/_utils/_convert_coords.py +0 -77
- scitex/fts/_kinds/_plot/_utils/_get_template.py +0 -178
- scitex/fts/_kinds/_plot/_utils/_normalize.py +0 -73
- scitex/fts/_kinds/_plot/_utils/_validate.py +0 -197
- scitex/fts/_kinds/_table/_latex/_export.py +0 -279
- scitex/fts/_stats/__init__.py +0 -48
- scitex/fts/_stats/_dataclasses/_Stats.py +0 -423
- scitex/fts/_stats/_dataclasses/__init__.py +0 -48
- scitex/fts/_tables/__init__.py +0 -65
- scitex/fts/_tables/_latex/__init__.py +0 -93
- scitex/fts/_tables/_latex/_editor/__init__.py +0 -11
- scitex/fts/_tables/_latex/_editor/_app.py +0 -725
- scitex/fts/_tables/_latex/_figure_exporter.py +0 -153
- scitex/fts/_tables/_latex/_stats_formatter.py +0 -274
- scitex/fts/_tables/_latex/_table_exporter.py +0 -362
- scitex/fts/_tables/_latex/_utils.py +0 -369
- scitex/fts/_tables/_latex/_validator.py +0 -445
- scitex/io/_save_modules/_pltz_bundle.py +0 -356
- /scitex/{fig → canvas}/README.md +0 -0
- /scitex/{fig → canvas}/backend/__init__.py +0 -0
- /scitex/{fig → canvas}/backend/_export.py +0 -0
- /scitex/{fig → canvas}/backend/_render.py +0 -0
- /scitex/{fig → canvas}/docs/CANVAS_ARCHITECTURE.md +0 -0
- /scitex/{fig → canvas}/editor/__init__.py +0 -0
- /scitex/{fig → canvas}/editor/_dearpygui_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_flask_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_mpl_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_qt_editor.py +0 -0
- /scitex/{fig → canvas}/editor/_tkinter_editor.py +0 -0
- /scitex/{fig → canvas}/editor/edit/backend_detector.py +0 -0
- /scitex/{fig → canvas}/editor/edit/manual_handler.py +0 -0
- /scitex/{fig → canvas}/editor/edit/path_resolver.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/__init__.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/_plotter.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/_utils.py +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/reset.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/typography.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/base/variables.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/buttons.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/context-menu.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/dropdown.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/forms.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/modal.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/components/sections.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/element-inspector.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/loading.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/overlay.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/selection.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/features/statistics.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/index.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/container.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/controls.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/css/layout/preview.css +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/axis.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/basic.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/alignment/distribute.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/canvas.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/dragging.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/resize.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/canvas/selection.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/core/state.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/core/utils.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/dev/element-inspector.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/bbox.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/element-drag.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/editor/overlay.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/main.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/context-menu.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/shortcuts/keyboard.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/controls.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/download.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/help.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/static/js/ui/theme.js +0 -0
- /scitex/{fig → canvas}/editor/flask_editor/templates/__init__.py +0 -0
- /scitex/{fig → canvas}/io/_directory.py +0 -0
- /scitex/{fig → canvas}/model/_plot_types.py +0 -0
- /scitex/{fig → canvas}/utils/_defaults.py +0 -0
- /scitex/{fig → canvas}/utils/_validate.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/__init__.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/_bundle2dict.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_conversion/_dict2bundle.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_Axes.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_BBox.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_ColumnDef.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataFormat.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataInfo.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_DataSource.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_dataclasses/_SizeMM.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/__init__.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_bar.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_line.py +0 -0
- /scitex/{fts/_bundle → io/bundle}/_extractors/_extract_scatter.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_figure/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_figure}/_composite.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_plot/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_export.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_backend/_render.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_ChannelEncoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Encoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_Theme.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/_TraceEncoding.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_dataclasses/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_bar.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_box.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_distribution.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_errorbar.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_histogram.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_image.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_line.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_scatter.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_seaborn.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_models/_plot_types/_violin.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/__init__.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_auto_layout.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_calc_bounds.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_const_sizes.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_convert_coords.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_get_template.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_normalize.py +0 -0
- /scitex/{fts/_fig → io/bundle/kinds/_plot}/_utils/_validate.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_shape/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/_Stats.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_stats/_dataclasses/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_editor/__init__.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_stats_formatter.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_utils.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_table/_latex/_validator.py +0 -0
- /scitex/{fts/_kinds → io/bundle/kinds}/_text/__init__.py +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/data_info.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/encoding.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/node.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/render_manifest.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/stats.schema.json +0 -0
- /scitex/{fts/_schemas → io/bundle/schemas}/theme.schema.json +0 -0
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/WHEEL +0 -0
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.10.2.dist-info → scitex-2.11.0.dist-info}/licenses/LICENSE +0 -0
scitex/io/bundle/_nested.py
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
#
|
|
3
|
-
# Timestamp: "2025-12-16 (ywatanabe)"
|
|
2
|
+
# Timestamp: 2026-01-07
|
|
4
3
|
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/bundle/_nested.py
|
|
5
4
|
|
|
6
5
|
"""
|
|
7
6
|
SciTeX Nested Bundle Access - Transparent access to nested bundles.
|
|
8
7
|
|
|
9
|
-
Provides unified API to access nested bundles (
|
|
8
|
+
Provides unified API to access nested bundles (plot inside figure) regardless
|
|
10
9
|
of whether they are stored as:
|
|
11
|
-
- ZIP files (.
|
|
12
|
-
- Directories (.
|
|
13
|
-
- Nested paths (Figure1.
|
|
10
|
+
- ZIP files (.figure.zip, .plot.zip)
|
|
11
|
+
- Directories (.figure, .plot)
|
|
12
|
+
- Nested paths (Figure1.figure.zip/A.plot or Figure1.figure/A.plot)
|
|
14
13
|
|
|
15
14
|
Usage:
|
|
16
15
|
from scitex.io.bundle import nested
|
|
17
16
|
|
|
18
|
-
# Get
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
# Get plot bundle from inside figure (works with both ZIP and directory)
|
|
18
|
+
plot_data = nested.resolve("Figure1.figure.zip/A.plot")
|
|
19
|
+
plot_data = nested.resolve("Figure1.figure/A.plot")
|
|
21
20
|
|
|
22
21
|
# Get specific file from nested bundle
|
|
23
|
-
png_bytes = nested.get_file("Figure1.
|
|
24
|
-
spec = nested.get_json("Figure1.
|
|
22
|
+
png_bytes = nested.get_file("Figure1.figure/A.plot/exports/plot.png")
|
|
23
|
+
spec = nested.get_json("Figure1.figure/A.plot/spec.json")
|
|
25
24
|
|
|
26
25
|
# Get preview image (common use case)
|
|
27
|
-
preview_bytes = nested.get_preview("Figure1.
|
|
26
|
+
preview_bytes = nested.get_preview("Figure1.figure/A.plot")
|
|
28
27
|
"""
|
|
29
28
|
|
|
30
29
|
import json
|
|
@@ -47,25 +46,24 @@ __all__ = [
|
|
|
47
46
|
|
|
48
47
|
|
|
49
48
|
def parse_path(
|
|
50
|
-
path: Union[str, Path]
|
|
49
|
+
path: Union[str, Path],
|
|
51
50
|
) -> Tuple[Optional[Path], Optional[str], Optional[str]]:
|
|
52
51
|
"""Parse a path to identify parent bundle, nested bundle, and file.
|
|
53
52
|
|
|
54
53
|
Handles paths like:
|
|
55
|
-
- Figure1.
|
|
56
|
-
- Figure1.
|
|
57
|
-
- Figure1.
|
|
58
|
-
-
|
|
59
|
-
- A.
|
|
60
|
-
- A.pltz (standalone ZIP)
|
|
54
|
+
- Figure1.figure/A.plot
|
|
55
|
+
- Figure1.figure.zip/A.plot
|
|
56
|
+
- Figure1.figure/A.plot/exports/plot.png
|
|
57
|
+
- A.plot (standalone directory)
|
|
58
|
+
- A.plot.zip (standalone ZIP)
|
|
61
59
|
|
|
62
60
|
Args:
|
|
63
61
|
path: Path to parse. Can be absolute or relative.
|
|
64
62
|
|
|
65
63
|
Returns:
|
|
66
64
|
Tuple of (parent_bundle_path, nested_bundle_name, file_path_within_nested):
|
|
67
|
-
- parent_bundle_path: Path to .
|
|
68
|
-
- nested_bundle_name: Name of nested bundle (e.g., "A.
|
|
65
|
+
- parent_bundle_path: Path to .figure or .figure.zip, or None if standalone
|
|
66
|
+
- nested_bundle_name: Name of nested bundle (e.g., "A.plot"), or None
|
|
69
67
|
- file_path_within_nested: Path within nested bundle, or None
|
|
70
68
|
"""
|
|
71
69
|
p = Path(path)
|
|
@@ -75,19 +73,19 @@ def parse_path(
|
|
|
75
73
|
nested_bundle = None
|
|
76
74
|
file_within = None
|
|
77
75
|
|
|
78
|
-
# Find the first .
|
|
79
|
-
|
|
76
|
+
# Find the first .figure.zip or .figure component
|
|
77
|
+
figure_idx = None
|
|
80
78
|
for i, part in enumerate(parts):
|
|
81
|
-
if part.endswith(".
|
|
82
|
-
|
|
79
|
+
if part.endswith(".figure.zip") or part.endswith(".figure"):
|
|
80
|
+
figure_idx = i
|
|
83
81
|
break
|
|
84
82
|
|
|
85
|
-
if
|
|
86
|
-
parent_bundle = Path(*parts[:
|
|
87
|
-
remaining = parts[
|
|
83
|
+
if figure_idx is not None:
|
|
84
|
+
parent_bundle = Path(*parts[: figure_idx + 1])
|
|
85
|
+
remaining = parts[figure_idx + 1 :]
|
|
88
86
|
|
|
89
87
|
if remaining:
|
|
90
|
-
if remaining[0].endswith(".
|
|
88
|
+
if remaining[0].endswith(".plot") or remaining[0].endswith(".plot.zip"):
|
|
91
89
|
nested_bundle = remaining[0]
|
|
92
90
|
if len(remaining) > 1:
|
|
93
91
|
file_within = str(Path(*remaining[1:]))
|
|
@@ -95,7 +93,7 @@ def parse_path(
|
|
|
95
93
|
file_within = str(Path(*remaining))
|
|
96
94
|
else:
|
|
97
95
|
for i, part in enumerate(parts):
|
|
98
|
-
if part.endswith(".
|
|
96
|
+
if part.endswith(".plot") or part.endswith(".plot.zip"):
|
|
99
97
|
nested_bundle = part
|
|
100
98
|
parent_bundle = Path(*parts[:i]) if i > 0 else None
|
|
101
99
|
if i + 1 < len(parts):
|
|
@@ -111,17 +109,23 @@ def _find_bundle_path(base_path: Path, prefer_directory: bool = True) -> Optiona
|
|
|
111
109
|
"""Find the actual bundle path (ZIP or directory).
|
|
112
110
|
|
|
113
111
|
Args:
|
|
114
|
-
base_path: Path to search for (with or without .
|
|
115
|
-
prefer_directory: If True, prefer
|
|
116
|
-
This is important for
|
|
112
|
+
base_path: Path to search for (with or without .zip extension).
|
|
113
|
+
prefer_directory: If True, prefer directory over ZIP when both exist.
|
|
114
|
+
This is important for figure bundles where panels may be in the directory
|
|
117
115
|
while the ZIP is an older export.
|
|
118
116
|
|
|
119
117
|
Returns:
|
|
120
118
|
Path to the bundle (directory or ZIP), or None if not found.
|
|
121
119
|
"""
|
|
122
|
-
# Check for .
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
# Check for ZIP extension (.figure.zip, .plot.zip, .stats.zip)
|
|
121
|
+
name = base_path.name
|
|
122
|
+
if (
|
|
123
|
+
name.endswith(".figure.zip")
|
|
124
|
+
or name.endswith(".plot.zip")
|
|
125
|
+
or name.endswith(".stats.zip")
|
|
126
|
+
):
|
|
127
|
+
# ZIP path provided - check for directory variant
|
|
128
|
+
dir_path = Path(str(base_path)[:-4]) # Remove .zip
|
|
125
129
|
if prefer_directory and dir_path.exists():
|
|
126
130
|
return dir_path
|
|
127
131
|
if base_path.exists():
|
|
@@ -129,9 +133,9 @@ def _find_bundle_path(base_path: Path, prefer_directory: bool = True) -> Optiona
|
|
|
129
133
|
if dir_path.exists():
|
|
130
134
|
return dir_path
|
|
131
135
|
|
|
132
|
-
# Check for
|
|
133
|
-
if
|
|
134
|
-
zip_path = Path(str(base_path)
|
|
136
|
+
# Check for directory extension (.figure, .plot, .stats)
|
|
137
|
+
if name.endswith(".figure") or name.endswith(".plot") or name.endswith(".stats"):
|
|
138
|
+
zip_path = Path(str(base_path) + ".zip")
|
|
135
139
|
if not prefer_directory and zip_path.exists():
|
|
136
140
|
return zip_path
|
|
137
141
|
if base_path.exists():
|
|
@@ -158,20 +162,20 @@ def _read_from_zip(zip_path: Path, internal_path: str) -> bytes:
|
|
|
158
162
|
if name.endswith("/" + internal_path) or name.endswith(internal_path):
|
|
159
163
|
return zf.read(name)
|
|
160
164
|
|
|
161
|
-
if ".
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
if ".plot/" in internal_path:
|
|
166
|
+
plot_dir_name, file_in_plot = internal_path.split(".plot/", 1)
|
|
167
|
+
plot_dir = plot_dir_name + ".plot"
|
|
164
168
|
|
|
165
169
|
for name in namelist:
|
|
166
|
-
if f"/{
|
|
167
|
-
if name.endswith("/" +
|
|
170
|
+
if f"/{plot_dir}/" in name or name.startswith(plot_dir + "/"):
|
|
171
|
+
if name.endswith("/" + file_in_plot) or name.endswith(file_in_plot):
|
|
168
172
|
return zf.read(name)
|
|
169
173
|
|
|
170
|
-
base_name =
|
|
174
|
+
base_name = plot_dir_name
|
|
171
175
|
for name in namelist:
|
|
172
|
-
if name.endswith(".
|
|
173
|
-
|
|
174
|
-
return _read_from_nested_zip(
|
|
176
|
+
if name.endswith(".plot.zip") and base_name in name:
|
|
177
|
+
plot_data = zf.read(name)
|
|
178
|
+
return _read_from_nested_zip(plot_data, file_in_plot, name)
|
|
175
179
|
|
|
176
180
|
raise NestedBundleNotFoundError(
|
|
177
181
|
f"File not found in {zip_path}: {internal_path}\n"
|
|
@@ -179,7 +183,9 @@ def _read_from_zip(zip_path: Path, internal_path: str) -> bytes:
|
|
|
179
183
|
)
|
|
180
184
|
|
|
181
185
|
|
|
182
|
-
def _read_from_nested_zip(
|
|
186
|
+
def _read_from_nested_zip(
|
|
187
|
+
zip_data: bytes, internal_path: str, zip_name: str = ""
|
|
188
|
+
) -> bytes:
|
|
183
189
|
"""Read file from a ZIP archive stored as bytes (nested ZIP)."""
|
|
184
190
|
import io
|
|
185
191
|
|
|
@@ -189,10 +195,12 @@ def _read_from_nested_zip(zip_data: bytes, internal_path: str, zip_name: str = "
|
|
|
189
195
|
if internal_path in namelist:
|
|
190
196
|
return nested_zf.read(internal_path)
|
|
191
197
|
|
|
192
|
-
base_name =
|
|
193
|
-
|
|
198
|
+
base_name = (
|
|
199
|
+
zip_name.replace(".plot.zip", "").replace(".plot", "") if zip_name else ""
|
|
200
|
+
)
|
|
201
|
+
dir_prefix = f"{base_name}.plot/"
|
|
194
202
|
|
|
195
|
-
full_path =
|
|
203
|
+
full_path = dir_prefix + internal_path
|
|
196
204
|
if full_path in namelist:
|
|
197
205
|
return nested_zf.read(full_path)
|
|
198
206
|
|
|
@@ -212,8 +220,9 @@ def _read_from_directory(dir_path: Path, internal_path: str) -> bytes:
|
|
|
212
220
|
if file_path.exists():
|
|
213
221
|
return file_path.read_bytes()
|
|
214
222
|
|
|
215
|
-
|
|
216
|
-
|
|
223
|
+
# Try alternate form: .plot.zip -> .plot directory
|
|
224
|
+
if ".plot.zip/" in internal_path:
|
|
225
|
+
alt_path = internal_path.replace(".plot.zip/", ".plot/")
|
|
217
226
|
alt_file_path = dir_path / alt_path
|
|
218
227
|
if alt_file_path.exists():
|
|
219
228
|
return alt_file_path.read_bytes()
|
|
@@ -228,9 +237,9 @@ def get_file(path: Union[str, Path]) -> bytes:
|
|
|
228
237
|
|
|
229
238
|
Args:
|
|
230
239
|
path: Full path to file, e.g.:
|
|
231
|
-
- "Figure1.
|
|
232
|
-
- "Figure1.
|
|
233
|
-
- "/abs/path/Figure1.
|
|
240
|
+
- "Figure1.figure/A.plot/exports/plot.png"
|
|
241
|
+
- "Figure1.figure.zip/A.plot/exports/plot.png"
|
|
242
|
+
- "/abs/path/Figure1.figure/A.plot/spec.json"
|
|
234
243
|
|
|
235
244
|
Returns:
|
|
236
245
|
File contents as bytes.
|
|
@@ -297,8 +306,8 @@ def put_file(path: Union[str, Path], data: bytes) -> None:
|
|
|
297
306
|
|
|
298
307
|
Args:
|
|
299
308
|
path: Full path to file, e.g.:
|
|
300
|
-
- "Figure1.
|
|
301
|
-
- "Figure1.
|
|
309
|
+
- "Figure1.figure/A.plot/exports/plot.png"
|
|
310
|
+
- "Figure1.figure.zip/A.plot/exports/plot.png"
|
|
302
311
|
data: File contents as bytes.
|
|
303
312
|
|
|
304
313
|
Raises:
|
|
@@ -403,10 +412,10 @@ def get_preview(
|
|
|
403
412
|
bundle_path: Union[str, Path],
|
|
404
413
|
filename: str = None,
|
|
405
414
|
) -> bytes:
|
|
406
|
-
"""Get preview PNG from a nested
|
|
415
|
+
"""Get preview PNG from a nested plot bundle.
|
|
407
416
|
|
|
408
|
-
Handles .
|
|
409
|
-
filename matches the bundle name
|
|
417
|
+
Handles .plot and .plot.zip interchangeably. Does not assume the PNG
|
|
418
|
+
filename matches the bundle name.
|
|
410
419
|
|
|
411
420
|
Search order:
|
|
412
421
|
1. Specific filename if provided
|
|
@@ -418,8 +427,8 @@ def get_preview(
|
|
|
418
427
|
7. Any PNG in bundle not containing 'hitmap' or 'overview'
|
|
419
428
|
|
|
420
429
|
Args:
|
|
421
|
-
bundle_path: Path to
|
|
422
|
-
Handles both .
|
|
430
|
+
bundle_path: Path to plot bundle (can be nested in figure).
|
|
431
|
+
Handles both .plot and .plot.zip extensions interchangeably.
|
|
423
432
|
filename: Specific filename to look for (optional).
|
|
424
433
|
|
|
425
434
|
Returns:
|
|
@@ -427,7 +436,7 @@ def get_preview(
|
|
|
427
436
|
"""
|
|
428
437
|
parent_bundle, nested_bundle, file_within = parse_path(bundle_path)
|
|
429
438
|
|
|
430
|
-
# Build base path, handling .
|
|
439
|
+
# Build base path, handling .plot ↔ .plot.zip interchangeably
|
|
431
440
|
if parent_bundle and nested_bundle:
|
|
432
441
|
base_path = f"{parent_bundle}/{nested_bundle}"
|
|
433
442
|
elif nested_bundle:
|
|
@@ -437,15 +446,15 @@ def get_preview(
|
|
|
437
446
|
|
|
438
447
|
# Try alternate extension if list_files fails
|
|
439
448
|
def try_with_fallback(path: str) -> List[str]:
|
|
440
|
-
"""Try to list files, with .
|
|
449
|
+
"""Try to list files, with .plot ↔ .plot.zip fallback."""
|
|
441
450
|
try:
|
|
442
451
|
return list_files(path)
|
|
443
452
|
except NestedBundleNotFoundError:
|
|
444
453
|
# Try alternate extension
|
|
445
|
-
if path.endswith(".
|
|
446
|
-
alt_path = path + ".
|
|
447
|
-
elif path.endswith(".
|
|
448
|
-
alt_path = path[:-
|
|
454
|
+
if path.endswith(".plot"):
|
|
455
|
+
alt_path = path + ".zip"
|
|
456
|
+
elif path.endswith(".plot.zip"):
|
|
457
|
+
alt_path = path[:-4] # Remove .zip
|
|
449
458
|
else:
|
|
450
459
|
raise
|
|
451
460
|
return list_files(alt_path)
|
|
@@ -455,14 +464,14 @@ def get_preview(
|
|
|
455
464
|
try:
|
|
456
465
|
files = try_with_fallback(base_path)
|
|
457
466
|
# Determine which path worked
|
|
458
|
-
if base_path.endswith(".
|
|
459
|
-
alt_path = base_path + ".
|
|
467
|
+
if base_path.endswith(".plot") and not base_path.endswith(".plot.zip"):
|
|
468
|
+
alt_path = base_path + ".zip"
|
|
460
469
|
try:
|
|
461
470
|
list_files(base_path)
|
|
462
471
|
except NestedBundleNotFoundError:
|
|
463
472
|
working_path = alt_path
|
|
464
|
-
elif base_path.endswith(".
|
|
465
|
-
alt_path = base_path[:-
|
|
473
|
+
elif base_path.endswith(".plot.zip"):
|
|
474
|
+
alt_path = base_path[:-4]
|
|
466
475
|
try:
|
|
467
476
|
list_files(base_path)
|
|
468
477
|
except NestedBundleNotFoundError:
|
|
@@ -470,7 +479,7 @@ def get_preview(
|
|
|
470
479
|
except NestedBundleNotFoundError:
|
|
471
480
|
files = []
|
|
472
481
|
|
|
473
|
-
bundle_name = Path(working_path).stem.replace(".
|
|
482
|
+
bundle_name = Path(working_path).stem.replace(".plot", "")
|
|
474
483
|
|
|
475
484
|
# Standard locations to try
|
|
476
485
|
locations = [
|
|
@@ -560,12 +569,12 @@ def list_files(bundle_path: Union[str, Path]) -> List[str]:
|
|
|
560
569
|
if files:
|
|
561
570
|
return files
|
|
562
571
|
|
|
563
|
-
if nested_bundle.endswith(".
|
|
564
|
-
base_name = nested_bundle[:-
|
|
572
|
+
if nested_bundle.endswith(".plot"):
|
|
573
|
+
base_name = nested_bundle[:-5]
|
|
565
574
|
for name in namelist:
|
|
566
|
-
if name.endswith(".
|
|
567
|
-
|
|
568
|
-
return _list_nested_zip_files(
|
|
575
|
+
if name.endswith(".plot.zip") and base_name in name:
|
|
576
|
+
plot_data = zf.read(name)
|
|
577
|
+
return _list_nested_zip_files(plot_data, name)
|
|
569
578
|
|
|
570
579
|
return files
|
|
571
580
|
else:
|
|
@@ -573,18 +582,18 @@ def list_files(bundle_path: Union[str, Path]) -> List[str]:
|
|
|
573
582
|
else:
|
|
574
583
|
target = actual_parent
|
|
575
584
|
if nested_bundle:
|
|
576
|
-
# Prefer
|
|
577
|
-
if nested_bundle.endswith(".
|
|
578
|
-
dir_target = actual_parent /
|
|
585
|
+
# Prefer directory over ZIP when both exist
|
|
586
|
+
if nested_bundle.endswith(".plot.zip"):
|
|
587
|
+
dir_target = actual_parent / nested_bundle[:-4] # Remove .zip
|
|
579
588
|
if dir_target.exists():
|
|
580
589
|
target = dir_target
|
|
581
590
|
else:
|
|
582
591
|
target = actual_parent / nested_bundle
|
|
583
|
-
elif nested_bundle.endswith(".
|
|
592
|
+
elif nested_bundle.endswith(".plot"):
|
|
584
593
|
target = actual_parent / nested_bundle
|
|
585
594
|
if not target.exists():
|
|
586
|
-
# Try
|
|
587
|
-
zip_target = actual_parent / nested_bundle
|
|
595
|
+
# Try with .zip
|
|
596
|
+
zip_target = actual_parent / (nested_bundle + ".zip")
|
|
588
597
|
if zip_target.exists():
|
|
589
598
|
target = zip_target
|
|
590
599
|
else:
|
|
@@ -593,7 +602,9 @@ def list_files(bundle_path: Union[str, Path]) -> List[str]:
|
|
|
593
602
|
if not target.exists():
|
|
594
603
|
raise NestedBundleNotFoundError(f"Bundle not found: {target}")
|
|
595
604
|
|
|
596
|
-
return [
|
|
605
|
+
return [
|
|
606
|
+
str(f.relative_to(target)) for f in target.rglob("*") if f.is_file()
|
|
607
|
+
]
|
|
597
608
|
|
|
598
609
|
bundle = Path(bundle_path)
|
|
599
610
|
actual = _find_bundle_path(bundle)
|
|
@@ -614,13 +625,15 @@ def _list_nested_zip_files(zip_data: bytes, zip_name: str = "") -> List[str]:
|
|
|
614
625
|
with zipfile.ZipFile(io.BytesIO(zip_data), "r") as nested_zf:
|
|
615
626
|
namelist = nested_zf.namelist()
|
|
616
627
|
|
|
617
|
-
base_name =
|
|
618
|
-
|
|
628
|
+
base_name = (
|
|
629
|
+
zip_name.replace(".plot.zip", "").replace(".plot", "") if zip_name else ""
|
|
630
|
+
)
|
|
631
|
+
dir_prefix = f"{base_name}.plot/"
|
|
619
632
|
|
|
620
633
|
files = []
|
|
621
634
|
for name in namelist:
|
|
622
|
-
if name.startswith(
|
|
623
|
-
rel = name[len(
|
|
635
|
+
if name.startswith(dir_prefix):
|
|
636
|
+
rel = name[len(dir_prefix) :]
|
|
624
637
|
if rel and not rel.endswith("/"):
|
|
625
638
|
files.append(rel)
|
|
626
639
|
elif not name.endswith("/"):
|
|
@@ -636,16 +649,16 @@ def resolve(
|
|
|
636
649
|
"""Load a nested bundle's data.
|
|
637
650
|
|
|
638
651
|
Transparently handles:
|
|
639
|
-
- Standalone .
|
|
640
|
-
- Nested .
|
|
641
|
-
- Nested .
|
|
652
|
+
- Standalone .plot or .plot.zip
|
|
653
|
+
- Nested .plot inside .figure
|
|
654
|
+
- Nested .plot inside .figure.zip
|
|
642
655
|
|
|
643
656
|
Args:
|
|
644
657
|
bundle_path: Path to bundle, e.g.:
|
|
645
|
-
- "Figure1.
|
|
646
|
-
- "Figure1.
|
|
647
|
-
- "A.
|
|
648
|
-
- "A.
|
|
658
|
+
- "Figure1.figure/A.plot"
|
|
659
|
+
- "Figure1.figure.zip/A.plot"
|
|
660
|
+
- "A.plot"
|
|
661
|
+
- "A.plot.zip"
|
|
649
662
|
extract_to: If provided, extract ZIP contents to this directory.
|
|
650
663
|
|
|
651
664
|
Returns:
|
|
@@ -677,7 +690,7 @@ def resolve(
|
|
|
677
690
|
try:
|
|
678
691
|
result["spec"] = get_json(f"{bundle_path}/spec.json")
|
|
679
692
|
except NestedBundleNotFoundError:
|
|
680
|
-
bundle_name = Path(bundle_path).stem.replace(".
|
|
693
|
+
bundle_name = Path(bundle_path).stem.replace(".plot", "")
|
|
681
694
|
try:
|
|
682
695
|
result["spec"] = get_json(f"{bundle_path}/{bundle_name}.json")
|
|
683
696
|
except NestedBundleNotFoundError:
|
|
@@ -696,7 +709,7 @@ def resolve(
|
|
|
696
709
|
csv_bytes = get_file(f"{bundle_path}/data.csv")
|
|
697
710
|
result["data"] = pd.read_csv(io.BytesIO(csv_bytes))
|
|
698
711
|
except (NestedBundleNotFoundError, ImportError):
|
|
699
|
-
bundle_name = Path(bundle_path).stem.replace(".
|
|
712
|
+
bundle_name = Path(bundle_path).stem.replace(".plot", "")
|
|
700
713
|
try:
|
|
701
714
|
import io
|
|
702
715
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# Timestamp:
|
|
3
|
-
# File: /home/ywatanabe/proj/scitex-code/src/scitex/
|
|
2
|
+
# Timestamp: 2026-01-07
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/bundle/_saver.py
|
|
4
4
|
|
|
5
|
-
"""
|
|
5
|
+
"""Bundle saving utilities.
|
|
6
6
|
|
|
7
7
|
Bundle structure (IDENTICAL for all kinds):
|
|
8
8
|
bundle.zip/
|
|
@@ -38,14 +38,14 @@ from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
|
38
38
|
from ._storage import Storage, get_storage
|
|
39
39
|
|
|
40
40
|
if TYPE_CHECKING:
|
|
41
|
-
from ._dataclasses import DataInfo,
|
|
42
|
-
from
|
|
43
|
-
from
|
|
41
|
+
from ._dataclasses import DataInfo, Spec
|
|
42
|
+
from .kinds._plot._dataclasses import Encoding, Theme
|
|
43
|
+
from .kinds._stats._dataclasses import Stats
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
def save_bundle_components(
|
|
47
47
|
path: Path,
|
|
48
|
-
|
|
48
|
+
spec: Optional["Spec"] = None,
|
|
49
49
|
encoding: Optional["Encoding"] = None,
|
|
50
50
|
theme: Optional["Theme"] = None,
|
|
51
51
|
stats: Optional["Stats"] = None,
|
|
@@ -58,7 +58,7 @@ def save_bundle_components(
|
|
|
58
58
|
|
|
59
59
|
Args:
|
|
60
60
|
path: Bundle path (directory or ZIP)
|
|
61
|
-
|
|
61
|
+
spec: Spec metadata (saved to canonical/spec.json)
|
|
62
62
|
encoding: Encoding specification (saved to canonical/encoding.json)
|
|
63
63
|
theme: Theme specification (saved to canonical/theme.json)
|
|
64
64
|
stats: Statistics (saved to payload/stats.json for kind=stats)
|
|
@@ -74,15 +74,15 @@ def save_bundle_components(
|
|
|
74
74
|
# Use .keep files as directory markers for ZIP compatibility
|
|
75
75
|
files["canonical/.keep"] = ""
|
|
76
76
|
files["payload/.keep"] = ""
|
|
77
|
-
|
|
77
|
+
# NOTE: Don't write empty payload/data.csv - it may already contain data
|
|
78
78
|
files["artifacts/.keep"] = ""
|
|
79
79
|
files["artifacts/exports/.keep"] = ""
|
|
80
80
|
files["artifacts/cache/.keep"] = ""
|
|
81
81
|
files["children/.keep"] = ""
|
|
82
82
|
|
|
83
83
|
# canonical/ - Source of truth
|
|
84
|
-
if
|
|
85
|
-
files["canonical/spec.json"] = json.dumps(
|
|
84
|
+
if spec:
|
|
85
|
+
files["canonical/spec.json"] = json.dumps(spec.to_dict(), indent=2)
|
|
86
86
|
|
|
87
87
|
if encoding:
|
|
88
88
|
files["canonical/encoding.json"] = encoding.to_json()
|
|
@@ -99,7 +99,7 @@ def save_bundle_components(
|
|
|
99
99
|
|
|
100
100
|
# Write files, preserving any existing children/ files
|
|
101
101
|
# For ZIP, we need to merge with existing content
|
|
102
|
-
if hasattr(storage,
|
|
102
|
+
if hasattr(storage, "write_all_preserve"):
|
|
103
103
|
storage.write_all_preserve(files)
|
|
104
104
|
else:
|
|
105
105
|
# Fallback: write each file individually (preserves existing)
|
|
@@ -186,7 +186,6 @@ def generate_hitmap(
|
|
|
186
186
|
import io
|
|
187
187
|
|
|
188
188
|
import matplotlib.pyplot as plt
|
|
189
|
-
import numpy as np
|
|
190
189
|
|
|
191
190
|
# Create hitmap figure with same dimensions
|
|
192
191
|
fig_size = figure.get_size_inches()
|
|
@@ -224,7 +223,7 @@ def generate_hitmap(
|
|
|
224
223
|
plt.close(hitmap_fig)
|
|
225
224
|
return png_bytes, svg_bytes
|
|
226
225
|
|
|
227
|
-
except Exception
|
|
226
|
+
except Exception:
|
|
228
227
|
# If hitmap generation fails, return None (non-critical)
|
|
229
228
|
return None, None
|
|
230
229
|
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
# File: /home/ywatanabe/proj/scitex-code/src/scitex/fsb/_bundle/_storage.py
|
|
4
4
|
|
|
5
5
|
"""
|
|
6
|
-
Storage Abstraction for
|
|
6
|
+
Storage Abstraction for SciTeX Bundles.
|
|
7
7
|
|
|
8
8
|
Provides a unified interface for reading/writing bundle contents
|
|
9
9
|
regardless of whether the bundle is a ZIP file or directory.
|
|
10
10
|
|
|
11
11
|
Usage:
|
|
12
12
|
storage = get_storage(Path("bundle.zip")) # or Path("bundle/")
|
|
13
|
-
data = storage.read("
|
|
13
|
+
data = storage.read("spec.json")
|
|
14
14
|
storage.write("encoding.json", json_bytes)
|
|
15
15
|
"""
|
|
16
16
|
|
|
@@ -22,7 +22,7 @@ from typing import List, Optional, Union
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class Storage(ABC):
|
|
25
|
-
"""Abstract storage interface for
|
|
25
|
+
"""Abstract storage interface for SciTeX bundles."""
|
|
26
26
|
|
|
27
27
|
def __init__(self, path: Path):
|
|
28
28
|
self._path = path
|
scitex/io/bundle/_types.py
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
# File: /home/ywatanabe/proj/scitex-code/src/scitex/io/bundle/_types.py
|
|
2
|
+
# Timestamp: 2026-01-07
|
|
3
|
+
# File: src/scitex/io/bundle/_types.py
|
|
5
4
|
|
|
6
5
|
"""
|
|
7
6
|
SciTeX Bundle Types and Constants.
|
|
8
7
|
|
|
9
8
|
Defines bundle types, extensions, and error classes used across the bundle module.
|
|
9
|
+
|
|
10
|
+
Extension formats:
|
|
11
|
+
- ZIP: .figure.zip, .plot.zip, .stats.zip
|
|
12
|
+
- Directory: .figure/, .plot/, .stats/
|
|
10
13
|
"""
|
|
11
14
|
|
|
12
15
|
from typing import Tuple
|
|
@@ -18,18 +21,28 @@ __all__ = [
|
|
|
18
21
|
"BundleNotFoundError",
|
|
19
22
|
"NestedBundleNotFoundError",
|
|
20
23
|
"EXTENSIONS",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
+
"DIR_EXTENSIONS",
|
|
25
|
+
"FIGURE",
|
|
26
|
+
"PLOT",
|
|
27
|
+
"STATS",
|
|
24
28
|
]
|
|
25
29
|
|
|
26
|
-
#
|
|
27
|
-
|
|
30
|
+
# =============================================================================
|
|
31
|
+
# ZIP extensions
|
|
32
|
+
# =============================================================================
|
|
33
|
+
EXTENSIONS: Tuple[str, ...] = (".figure.zip", ".plot.zip", ".stats.zip")
|
|
34
|
+
|
|
35
|
+
# =============================================================================
|
|
36
|
+
# Directory extensions
|
|
37
|
+
# =============================================================================
|
|
38
|
+
DIR_EXTENSIONS: Tuple[str, ...] = (".figure", ".plot", ".stats")
|
|
28
39
|
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
# =============================================================================
|
|
41
|
+
# Bundle type constants
|
|
42
|
+
# =============================================================================
|
|
43
|
+
FIGURE = "figure"
|
|
44
|
+
PLOT = "plot"
|
|
45
|
+
STATS = "stats"
|
|
33
46
|
|
|
34
47
|
|
|
35
48
|
class BundleType:
|
|
@@ -38,13 +51,25 @@ class BundleType:
|
|
|
38
51
|
Usage:
|
|
39
52
|
from scitex.io.bundle import BundleType
|
|
40
53
|
|
|
41
|
-
if bundle_type == BundleType.
|
|
54
|
+
if bundle_type == BundleType.FIGURE:
|
|
42
55
|
...
|
|
43
56
|
"""
|
|
44
57
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
FIGURE = "figure"
|
|
59
|
+
PLOT = "plot"
|
|
60
|
+
STATS = "stats"
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def normalize(cls, bundle_type: str) -> str:
|
|
64
|
+
"""Normalize bundle type to standard name.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
bundle_type: Bundle type string.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Normalized type ('figure', 'plot', 'stats').
|
|
71
|
+
"""
|
|
72
|
+
return bundle_type.lower()
|
|
48
73
|
|
|
49
74
|
|
|
50
75
|
class BundleError(Exception):
|