scitex 2.8.1__py3-none-any.whl → 2.10.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scitex/__init__.py +15 -7
- scitex/__version__.py +1 -2
- scitex/_install_guide.py +250 -0
- scitex/_optional_deps.py +206 -39
- scitex/ai/_gen_ai/_Groq.py +2 -4
- scitex/ai/_gen_ai/_OpenAI.py +5 -2
- scitex/ai/_gen_ai/_Perplexity.py +20 -6
- scitex/audio/__init__.py +24 -15
- scitex/audio/_cross_process_lock.py +139 -0
- scitex/audio/_mcp_handlers.py +256 -0
- scitex/audio/_mcp_tool_schemas.py +203 -0
- scitex/audio/engines/elevenlabs_engine.py +5 -2
- scitex/audio/mcp_server.py +98 -457
- scitex/bridge/__init__.py +30 -19
- scitex/bridge/_figrecipe.py +245 -0
- scitex/bridge/_helpers.py +2 -1
- scitex/bridge/_plt_vis.py +23 -10
- scitex/bridge/_stats_plt.py +18 -5
- scitex/bridge/_stats_vis.py +16 -2
- scitex/browser/__init__.py +84 -44
- scitex/browser/automation/__init__.py +5 -1
- scitex/browser/core/BrowserMixin.py +17 -4
- scitex/browser/core/__init__.py +11 -2
- scitex/browser/remote/CaptchaHandler.py +1 -1
- scitex/browser/remote/ZenRowsAPIClient.py +1 -1
- scitex/capture/grid.py +487 -0
- scitex/capture/mcp_handlers.py +401 -0
- scitex/capture/mcp_tool_defs.py +192 -0
- scitex/capture/mcp_tools.py +241 -0
- scitex/capture/mcp_utils.py +30 -0
- scitex/cli/convert.py +421 -0
- scitex/cli/main.py +6 -4
- scitex/datetime/__init__.py +46 -0
- scitex/datetime/_linspace.py +100 -0
- scitex/datetime/_normalize_timestamp.py +306 -0
- scitex/db/_delete_duplicates.py +4 -4
- scitex/db/_sqlite3/_delete_duplicates.py +11 -2
- scitex/dev/plt/__init__.py +61 -62
- scitex/dev/plt/demo_plotters/__init__.py +0 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axhspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvline.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_axvspan.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_contourf.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_eventplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill.py +30 -0
- scitex/dev/plt/demo_plotters/plot_mpl_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hexbin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_hist2d.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_imshow.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pcolormesh.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_pie.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_plot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_quiver.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_scatter.py +28 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stackplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_mpl_stem.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_step.py +29 -0
- scitex/dev/plt/demo_plotters/plot_mpl_violinplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_barplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_boxplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_sns_histplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_kdeplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_lineplot.py +31 -0
- scitex/dev/plt/demo_plotters/plot_sns_scatterplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_stripplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_swarmplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_sns_violinplot.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_bar.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_barh.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_box.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_boxplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_conf_mat.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_contour.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_ecdf.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_errorbar.py +30 -0
- scitex/dev/plt/demo_plotters/plot_stx_fill_between.py +31 -0
- scitex/dev/plt/demo_plotters/plot_stx_fillv.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_heatmap.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_image.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_imshow.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_joyplot.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_kde.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_line.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_ci.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_mean_std.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_median_iqr.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_raster.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_rectangle.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_scatter.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_shaded_line.py +29 -0
- scitex/dev/plt/demo_plotters/plot_stx_violin.py +28 -0
- scitex/dev/plt/demo_plotters/plot_stx_violinplot.py +28 -0
- scitex/dev/plt/mpl/get_dir_ax.py +46 -0
- scitex/dev/plt/mpl/get_signatures.py +176 -0
- scitex/dev/plt/mpl/get_signatures_details.py +522 -0
- scitex/dict/_pop_keys.py +1 -7
- scitex/dsp/__init__.py +15 -10
- scitex/dsp/add_noise.py +5 -2
- scitex/dsp/example.py +35 -22
- scitex/dsp/filt.py +8 -3
- scitex/dsp/reference.py +3 -2
- scitex/dsp/utils/__init__.py +2 -1
- scitex/dsp/utils/_differential_bandpass_filters.py +14 -4
- scitex/dt/__init__.py +39 -2
- scitex/errors.py +82 -521
- scitex/fig/__init__.py +4 -4
- scitex/fig/editor/edit/panel_loader.py +1 -1
- scitex/fig/io/_bundle.py +7 -7
- scitex/fts/README.md +262 -0
- scitex/fts/TODO.md +66 -0
- scitex/fts/__init__.py +90 -0
- scitex/fts/_bundle/README_IN_BUNDLE.md +102 -0
- scitex/fts/_bundle/_FTS.py +657 -0
- scitex/fts/_bundle/__init__.py +38 -0
- scitex/fts/_bundle/_children.py +216 -0
- scitex/fts/_bundle/_conversion/__init__.py +15 -0
- scitex/fts/_bundle/_conversion/_bundle2dict.py +44 -0
- scitex/fts/_bundle/_conversion/_dict2bundle.py +50 -0
- scitex/fts/_bundle/_dataclasses/_Axes.py +57 -0
- scitex/fts/_bundle/_dataclasses/_BBox.py +54 -0
- scitex/fts/_bundle/_dataclasses/_ColumnDef.py +72 -0
- scitex/fts/_bundle/_dataclasses/_DataFormat.py +40 -0
- scitex/fts/_bundle/_dataclasses/_DataInfo.py +135 -0
- scitex/fts/_bundle/_dataclasses/_DataSource.py +44 -0
- scitex/fts/_bundle/_dataclasses/_Node.py +319 -0
- scitex/fts/_bundle/_dataclasses/_NodeRefs.py +45 -0
- scitex/fts/_bundle/_dataclasses/_SizeMM.py +38 -0
- scitex/fts/_bundle/_dataclasses/__init__.py +35 -0
- scitex/fts/_bundle/_extractors/__init__.py +32 -0
- scitex/fts/_bundle/_extractors/_extract_bar.py +131 -0
- scitex/fts/_bundle/_extractors/_extract_line.py +71 -0
- scitex/fts/_bundle/_extractors/_extract_scatter.py +79 -0
- scitex/fts/_bundle/_loader.py +134 -0
- scitex/fts/_bundle/_mpl_helpers.py +389 -0
- scitex/fts/_bundle/_saver.py +269 -0
- scitex/fts/_bundle/_storage.py +200 -0
- scitex/fts/_bundle/_utils/__init__.py +55 -0
- scitex/fts/_bundle/_utils/_const.py +26 -0
- scitex/fts/_bundle/_utils/_errors.py +73 -0
- scitex/fts/_bundle/_utils/_generate.py +21 -0
- scitex/fts/_bundle/_utils/_types.py +76 -0
- scitex/fts/_bundle/_validation.py +434 -0
- scitex/fts/_bundle/_zipbundle.py +165 -0
- scitex/fts/_fig/__init__.py +22 -0
- scitex/fts/_fig/_backend/__init__.py +53 -0
- scitex/fts/_fig/_backend/_export.py +165 -0
- scitex/fts/_fig/_backend/_parser.py +188 -0
- scitex/fts/_fig/_backend/_render.py +538 -0
- scitex/fts/_fig/_composite.py +345 -0
- scitex/fts/_fig/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_fig/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_fig/_dataclasses/_Theme.py +441 -0
- scitex/fts/_fig/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_fig/_dataclasses/__init__.py +47 -0
- scitex/fts/_fig/_editor/__init__.py +14 -0
- scitex/fts/_fig/_editor/_cui/__init__.py +33 -0
- scitex/fts/_fig/_editor/_cui/_backend_detector.py +39 -0
- scitex/fts/_fig/_editor/_cui/_bundle_resolver.py +366 -0
- scitex/fts/_fig/_editor/_cui/_editor_launcher.py +175 -0
- scitex/fts/_fig/_editor/_cui/_manual_handler.py +52 -0
- scitex/fts/_fig/_editor/_cui/_panel_loader.py +246 -0
- scitex/fts/_fig/_editor/_cui/_path_resolver.py +66 -0
- scitex/fts/_fig/_editor/_defaults.py +300 -0
- scitex/fts/_fig/_editor/_gui/__init__.py +11 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/__init__.py +20 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_bbox.py +1339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_core.py +1688 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_plotter.py +664 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_renderer.py +853 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/_utils.py +79 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/reset.css +41 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/typography.css +16 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/base/variables.css +85 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/buttons.css +217 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/context-menu.css +93 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/dropdown.css +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/forms.css +112 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/modal.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/components/sections.css +212 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/canvas.css +176 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/element-inspector.css +190 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/loading.css +59 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/overlay.css +45 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/panel-grid.css +95 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/selection.css +101 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/features/statistics.css +138 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/index.css +31 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/container.css +7 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/controls.css +56 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/css/layout/preview.css +78 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/axis.js +314 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/basic.js +107 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/alignment/distribute.js +54 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/canvas.js +172 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/dragging.js +258 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/resize.js +48 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/canvas/selection.js +71 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/api.js +288 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/state.js +143 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/core/utils.js +245 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/dev/element-inspector.js +992 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/bbox.js +339 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/element-drag.js +286 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/overlay.js +371 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/editor/preview.js +293 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/main.js +426 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/context-menu.js +152 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/shortcuts/keyboard.js +265 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/controls.js +184 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/download.js +57 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/help.js +100 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/static/js/ui/theme.js +34 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/__init__.py +124 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_html.py +851 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_scripts.py +4932 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor/templates/_styles.py +1657 -0
- scitex/fts/_fig/_editor/_gui/_flask_editor.py +36 -0
- scitex/fts/_fig/_models/_Annotations.py +115 -0
- scitex/fts/_fig/_models/_Axes.py +152 -0
- scitex/fts/_fig/_models/_Figure.py +138 -0
- scitex/fts/_fig/_models/_Guides.py +104 -0
- scitex/fts/_fig/_models/_Plot.py +123 -0
- scitex/fts/_fig/_models/_Styles.py +245 -0
- scitex/fts/_fig/_models/__init__.py +80 -0
- scitex/fts/_fig/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_fig/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_fig/_models/_plot_types/_box.py +38 -0
- scitex/fts/_fig/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_fig/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_fig/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_image.py +61 -0
- scitex/fts/_fig/_models/_plot_types/_line.py +57 -0
- scitex/fts/_fig/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_fig/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_fig/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_fig/_utils/__init__.py +129 -0
- scitex/fts/_fig/_utils/_auto_layout.py +127 -0
- scitex/fts/_fig/_utils/_calc_bounds.py +111 -0
- scitex/fts/_fig/_utils/_const_sizes.py +48 -0
- scitex/fts/_fig/_utils/_convert_coords.py +77 -0
- scitex/fts/_fig/_utils/_get_template.py +178 -0
- scitex/fts/_fig/_utils/_normalize.py +73 -0
- scitex/fts/_fig/_utils/_plot_layout.py +397 -0
- scitex/fts/_fig/_utils/_validate.py +197 -0
- scitex/fts/_kinds/__init__.py +45 -0
- scitex/fts/_kinds/_figure/__init__.py +19 -0
- scitex/fts/_kinds/_figure/_composite.py +345 -0
- scitex/fts/_kinds/_plot/__init__.py +25 -0
- scitex/fts/_kinds/_plot/_backend/__init__.py +53 -0
- scitex/fts/_kinds/_plot/_backend/_export.py +165 -0
- scitex/fts/_kinds/_plot/_backend/_parser.py +188 -0
- scitex/fts/_kinds/_plot/_backend/_render.py +538 -0
- scitex/fts/_kinds/_plot/_dataclasses/_ChannelEncoding.py +46 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Encoding.py +82 -0
- scitex/fts/_kinds/_plot/_dataclasses/_Theme.py +441 -0
- scitex/fts/_kinds/_plot/_dataclasses/_TraceEncoding.py +52 -0
- scitex/fts/_kinds/_plot/_dataclasses/__init__.py +47 -0
- scitex/fts/_kinds/_plot/_models/_Annotations.py +115 -0
- scitex/fts/_kinds/_plot/_models/_Axes.py +152 -0
- scitex/fts/_kinds/_plot/_models/_Figure.py +138 -0
- scitex/fts/_kinds/_plot/_models/_Guides.py +104 -0
- scitex/fts/_kinds/_plot/_models/_Plot.py +123 -0
- scitex/fts/_kinds/_plot/_models/_Styles.py +245 -0
- scitex/fts/_kinds/_plot/_models/__init__.py +80 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/__init__.py +156 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_bar.py +43 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_box.py +38 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_distribution.py +36 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_errorbar.py +60 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_histogram.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_image.py +61 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_line.py +57 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_scatter.py +30 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_seaborn.py +121 -0
- scitex/fts/_kinds/_plot/_models/_plot_types/_violin.py +36 -0
- scitex/fts/_kinds/_plot/_utils/__init__.py +129 -0
- scitex/fts/_kinds/_plot/_utils/_auto_layout.py +127 -0
- scitex/fts/_kinds/_plot/_utils/_calc_bounds.py +111 -0
- scitex/fts/_kinds/_plot/_utils/_const_sizes.py +48 -0
- scitex/fts/_kinds/_plot/_utils/_convert_coords.py +77 -0
- scitex/fts/_kinds/_plot/_utils/_get_template.py +178 -0
- scitex/fts/_kinds/_plot/_utils/_normalize.py +73 -0
- scitex/fts/_kinds/_plot/_utils/_plot_layout.py +397 -0
- scitex/fts/_kinds/_plot/_utils/_validate.py +197 -0
- scitex/fts/_kinds/_shape/__init__.py +141 -0
- scitex/fts/_kinds/_stats/__init__.py +56 -0
- scitex/fts/_kinds/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_kinds/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_kinds/_table/__init__.py +72 -0
- scitex/fts/_kinds/_table/_latex/__init__.py +93 -0
- scitex/fts/_kinds/_table/_latex/_editor/__init__.py +11 -0
- scitex/fts/_kinds/_table/_latex/_editor/_app.py +725 -0
- scitex/fts/_kinds/_table/_latex/_export.py +279 -0
- scitex/fts/_kinds/_table/_latex/_figure_exporter.py +153 -0
- scitex/fts/_kinds/_table/_latex/_stats_formatter.py +274 -0
- scitex/fts/_kinds/_table/_latex/_table_exporter.py +362 -0
- scitex/fts/_kinds/_table/_latex/_utils.py +369 -0
- scitex/fts/_kinds/_table/_latex/_validator.py +445 -0
- scitex/fts/_kinds/_text/__init__.py +77 -0
- scitex/fts/_schemas/data_info.schema.json +75 -0
- scitex/fts/_schemas/encoding.schema.json +90 -0
- scitex/fts/_schemas/node.schema.json +145 -0
- scitex/fts/_schemas/render_manifest.schema.json +62 -0
- scitex/fts/_schemas/stats.schema.json +132 -0
- scitex/fts/_schemas/theme.schema.json +141 -0
- scitex/fts/_stats/__init__.py +48 -0
- scitex/fts/_stats/_dataclasses/_Stats.py +423 -0
- scitex/fts/_stats/_dataclasses/__init__.py +48 -0
- scitex/fts/_tables/__init__.py +65 -0
- scitex/fts/_tables/_latex/__init__.py +93 -0
- scitex/fts/_tables/_latex/_editor/__init__.py +11 -0
- scitex/fts/_tables/_latex/_editor/_app.py +725 -0
- scitex/fts/_tables/_latex/_export.py +279 -0
- scitex/fts/_tables/_latex/_figure_exporter.py +153 -0
- scitex/fts/_tables/_latex/_stats_formatter.py +274 -0
- scitex/fts/_tables/_latex/_table_exporter.py +362 -0
- scitex/fts/_tables/_latex/_utils.py +369 -0
- scitex/fts/_tables/_latex/_validator.py +445 -0
- scitex/gen/__init__.py +66 -25
- scitex/gen/misc.py +28 -0
- scitex/io/__init__.py +47 -32
- scitex/io/_load.py +87 -36
- scitex/io/_load_modules/__init__.py +10 -7
- scitex/io/_load_modules/_pandas.py +6 -1
- scitex/io/_save.py +299 -1556
- scitex/io/_save_modules/__init__.py +76 -19
- scitex/io/_save_modules/_figure_utils.py +90 -0
- scitex/io/_save_modules/_image_csv.py +497 -0
- scitex/io/_save_modules/_legends.py +91 -0
- scitex/io/_save_modules/_pltz_bundle.py +356 -0
- scitex/io/_save_modules/_pltz_stx.py +536 -0
- scitex/io/_save_modules/_stx_bundle.py +104 -0
- scitex/io/_save_modules/_symlink.py +96 -0
- scitex/io/_save_modules/_yaml.py +1 -1
- scitex/io/_save_modules/_zarr.py +64 -18
- scitex/io/bundle/README.md +212 -0
- scitex/io/bundle/__init__.py +110 -0
- scitex/io/{_bundle.py → bundle/_core.py} +168 -97
- scitex/io/bundle/_nested.py +713 -0
- scitex/io/bundle/_types.py +74 -0
- scitex/io/{_zip_bundle.py → bundle/_zip.py} +93 -45
- scitex/io/utils/h5_to_zarr.py +1 -1
- scitex/logging/__init__.py +108 -13
- scitex/logging/_errors.py +508 -0
- scitex/logging/_formatters.py +30 -6
- scitex/logging/_warnings.py +261 -0
- scitex/plt/__init__.py +4 -1
- scitex/plt/_figrecipe.py +236 -0
- scitex/plt/_subplots/_AxisWrapper.py +6 -0
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +112 -1
- scitex/plt/_subplots/_FigWrapper.py +15 -0
- scitex/plt/_subplots/_SubplotsWrapper.py +125 -489
- scitex/plt/_subplots/_export_as_csv.py +11 -0
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +2 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_pcolormesh.py +66 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stackplot.py +62 -0
- scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +208 -0
- scitex/plt/_subplots/_fonts.py +71 -0
- scitex/plt/_subplots/_mm_layout.py +282 -0
- scitex/plt/gallery/__init__.py +99 -2
- scitex/plt/styles/_plot_postprocess.py +3 -1
- scitex/plt/utils/_configure_mpl.py +16 -19
- scitex/repro/_RandomStateManager.py +13 -8
- scitex/resource/__init__.py +19 -1
- scitex/resource/_utils/_get_env_info.py +13 -25
- scitex/schema/__init__.py +149 -160
- scitex/schema/_encoding.py +273 -0
- scitex/schema/_figure_elements.py +406 -0
- scitex/schema/_theme.py +360 -0
- scitex/schema/_validation.py +0 -98
- scitex/scholar/__init__.py +56 -14
- scitex/scholar/auth/ScholarAuthManager.py +1 -1
- scitex/scholar/auth/__init__.py +11 -2
- scitex/scholar/auth/providers/BaseAuthenticator.py +1 -1
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +1 -1
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +1 -1
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +1 -1
- scitex/scholar/config/ScholarConfig.py +1 -1
- scitex/scholar/core/Scholar.py +1 -1
- scitex/session/_decorator.py +18 -16
- scitex/session/_lifecycle.py +9 -11
- scitex/session/template.py +9 -8
- scitex/sh/test_sh.py +72 -0
- scitex/sh/test_sh_simple.py +61 -0
- scitex/stats/__init__.py +221 -97
- scitex/stats/_schema.py +21 -22
- scitex/stats/descriptive/_circular.py +212 -351
- scitex/stats/descriptive/_describe.py +81 -132
- scitex/stats/descriptive/_nan.py +205 -433
- scitex/stats/descriptive/_real.py +127 -141
- scitex/str/_format_plot_text.py +5 -5
- scitex/str/_latex.py +26 -84
- scitex/str/_latex_fallback.py +53 -47
- scitex/web/_search_pubmed.py +5 -4
- scitex/writer/tests/test_diff_between.py +451 -0
- scitex/writer/tests/test_document_section.py +311 -0
- scitex/writer/tests/test_document_workflow.py +393 -0
- scitex/writer/tests/test_writer.py +361 -0
- scitex/writer/tests/test_writer_integration.py +303 -0
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/METADATA +364 -181
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/RECORD +412 -97
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/ARCHITECTURE_EXAMPLE.md +0 -905
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/BULLETIN_BOARD_EXAMPLE.md +0 -99
- scitex/scholar/docs/to_claude/guidelines/examples/mgmt/PROJECT_DESCRIPTION_EXAMPLE.md +0 -96
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/WHEEL +0 -0
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.8.1.dist-info → scitex-2.10.0.dist-info}/licenses/LICENSE +0 -0
scitex/cli/main.py
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
2
|
"""
|
|
4
3
|
SciTeX CLI Main Entry Point
|
|
5
4
|
"""
|
|
6
5
|
|
|
7
6
|
import os
|
|
8
7
|
import sys
|
|
8
|
+
|
|
9
9
|
import click
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
from . import cloud, config, convert, scholar, security, web, writer
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
|
|
@@ -39,6 +40,7 @@ def cli():
|
|
|
39
40
|
# Add command groups
|
|
40
41
|
cli.add_command(cloud.cloud)
|
|
41
42
|
cli.add_command(config.config)
|
|
43
|
+
cli.add_command(convert.convert)
|
|
42
44
|
cli.add_command(scholar.scholar)
|
|
43
45
|
cli.add_command(security.security)
|
|
44
46
|
cli.add_command(web.web)
|
|
@@ -130,7 +132,7 @@ def completion(shell, show):
|
|
|
130
132
|
|
|
131
133
|
# Check if already installed (and not commented out)
|
|
132
134
|
if os.path.exists(rc_file):
|
|
133
|
-
with open(rc_file
|
|
135
|
+
with open(rc_file) as f:
|
|
134
136
|
for line in f:
|
|
135
137
|
# Check if the line exists and is not commented
|
|
136
138
|
stripped = line.strip()
|
|
@@ -149,7 +151,7 @@ def completion(shell, show):
|
|
|
149
151
|
os.makedirs(os.path.dirname(rc_file), exist_ok=True)
|
|
150
152
|
|
|
151
153
|
with open(rc_file, "a") as f:
|
|
152
|
-
f.write(
|
|
154
|
+
f.write("\n# SciTeX tab completion\n")
|
|
153
155
|
f.write(f"{eval_line}\n")
|
|
154
156
|
|
|
155
157
|
click.secho(f"Successfully installed tab completion to {rc_file}", fg="green")
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-01-05 14:30:00 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/datetime/__init__.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Scitex datetime module.
|
|
7
|
+
|
|
8
|
+
Provides utilities for datetime operations including:
|
|
9
|
+
- linspace: Create linearly spaced datetime arrays
|
|
10
|
+
- normalize_timestamp: Standardize timestamps to consistent format
|
|
11
|
+
- to_datetime: Convert various formats to datetime objects
|
|
12
|
+
- validate_timestamp_format: Validate timestamp string format
|
|
13
|
+
- format_for_filename: Format timestamps for filenames
|
|
14
|
+
- format_for_display: Format timestamps for display
|
|
15
|
+
- get_time_delta_seconds: Calculate time differences
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from ._linspace import linspace
|
|
19
|
+
from ._normalize_timestamp import (
|
|
20
|
+
ALTERNATIVE_FORMATS,
|
|
21
|
+
STANDARD_FORMAT,
|
|
22
|
+
format_for_display,
|
|
23
|
+
format_for_filename,
|
|
24
|
+
get_time_delta_seconds,
|
|
25
|
+
normalize_timestamp,
|
|
26
|
+
parse_patient_recording_start_format,
|
|
27
|
+
to_datetime,
|
|
28
|
+
validate_timestamp_format,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
# Core functions
|
|
33
|
+
"linspace",
|
|
34
|
+
"normalize_timestamp",
|
|
35
|
+
"to_datetime",
|
|
36
|
+
# Formatting functions
|
|
37
|
+
"format_for_filename",
|
|
38
|
+
"format_for_display",
|
|
39
|
+
"validate_timestamp_format",
|
|
40
|
+
# Utility functions
|
|
41
|
+
"get_time_delta_seconds",
|
|
42
|
+
"parse_patient_recording_start_format",
|
|
43
|
+
# Constants
|
|
44
|
+
"STANDARD_FORMAT",
|
|
45
|
+
"ALTERNATIVE_FORMATS",
|
|
46
|
+
]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-01-05 14:30:00 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/datetime/_linspace.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Datetime linspace utility for creating evenly spaced datetime arrays.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import datetime
|
|
10
|
+
from datetime import timedelta
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def linspace(
|
|
17
|
+
start_dt: datetime.datetime,
|
|
18
|
+
end_dt: datetime.datetime,
|
|
19
|
+
n_samples: Optional[int] = None,
|
|
20
|
+
sampling_rate: Optional[float] = None,
|
|
21
|
+
) -> np.ndarray:
|
|
22
|
+
"""
|
|
23
|
+
Create a linearly spaced array between two datetime objects.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
start_dt : datetime.datetime
|
|
28
|
+
Starting datetime object
|
|
29
|
+
end_dt : datetime.datetime
|
|
30
|
+
Ending datetime object
|
|
31
|
+
n_samples : int, optional
|
|
32
|
+
Number of samples to create (mutually exclusive with sampling_rate)
|
|
33
|
+
sampling_rate : float, optional
|
|
34
|
+
Sampling rate in Hz (mutually exclusive with n_samples)
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
np.ndarray
|
|
39
|
+
Array of datetime objects evenly spaced between start_dt and end_dt
|
|
40
|
+
|
|
41
|
+
Raises
|
|
42
|
+
------
|
|
43
|
+
TypeError
|
|
44
|
+
If start_dt or end_dt is not a datetime object
|
|
45
|
+
ValueError
|
|
46
|
+
If start_dt >= end_dt, or if both/neither n_samples and sampling_rate provided
|
|
47
|
+
|
|
48
|
+
Examples
|
|
49
|
+
--------
|
|
50
|
+
>>> import datetime
|
|
51
|
+
>>> start = datetime.datetime(2023, 1, 1, 0, 0, 0)
|
|
52
|
+
>>> end = datetime.datetime(2023, 1, 1, 0, 0, 10)
|
|
53
|
+
>>> result = linspace(start, end, n_samples=11)
|
|
54
|
+
>>> len(result)
|
|
55
|
+
11
|
|
56
|
+
"""
|
|
57
|
+
# Type checking
|
|
58
|
+
if not isinstance(start_dt, datetime.datetime):
|
|
59
|
+
raise TypeError(f"start_dt must be a datetime object, got {type(start_dt)}")
|
|
60
|
+
|
|
61
|
+
if not isinstance(end_dt, datetime.datetime):
|
|
62
|
+
raise TypeError(f"end_dt must be a datetime object, got {type(end_dt)}")
|
|
63
|
+
|
|
64
|
+
if n_samples is not None and not isinstance(n_samples, (int, float)):
|
|
65
|
+
raise TypeError(f"n_samples must be a number, got {type(n_samples)}")
|
|
66
|
+
|
|
67
|
+
if sampling_rate is not None and not isinstance(sampling_rate, (int, float)):
|
|
68
|
+
raise TypeError(f"sampling_rate must be a number, got {type(sampling_rate)}")
|
|
69
|
+
|
|
70
|
+
if start_dt >= end_dt:
|
|
71
|
+
raise ValueError("start_dt must be earlier than end_dt")
|
|
72
|
+
|
|
73
|
+
duration_seconds = (end_dt - start_dt).total_seconds()
|
|
74
|
+
|
|
75
|
+
if n_samples is not None and sampling_rate is not None:
|
|
76
|
+
raise ValueError("Provide either n_samples or sampling_rate, not both")
|
|
77
|
+
|
|
78
|
+
if n_samples is None and sampling_rate is None:
|
|
79
|
+
raise ValueError("Either n_samples or sampling_rate must be provided")
|
|
80
|
+
|
|
81
|
+
if sampling_rate is not None:
|
|
82
|
+
if sampling_rate <= 0:
|
|
83
|
+
raise ValueError("sampling_rate must be positive")
|
|
84
|
+
n_samples = int(duration_seconds * sampling_rate) + 1
|
|
85
|
+
else:
|
|
86
|
+
if n_samples <= 0:
|
|
87
|
+
raise ValueError("n_samples must be positive")
|
|
88
|
+
|
|
89
|
+
# Create linear space in seconds
|
|
90
|
+
seconds_array = np.linspace(0, duration_seconds, n_samples)
|
|
91
|
+
|
|
92
|
+
# Convert to datetime objects
|
|
93
|
+
datetime_array = np.array(
|
|
94
|
+
[start_dt + timedelta(seconds=float(sec)) for sec in seconds_array]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return datetime_array
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# EOF
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: "2026-01-05 14:30:00 (ywatanabe)"
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/datetime/_normalize_timestamp.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Timestamp Standardization Utilities
|
|
7
|
+
|
|
8
|
+
Functionality:
|
|
9
|
+
- Standardizes timestamps to consistent format defined in CONFIG.FORMATS.TIMESTAMP
|
|
10
|
+
- Handles various input formats (datetime objects, strings, timestamps)
|
|
11
|
+
- Provides UTC normalization
|
|
12
|
+
- Ensures consistent timestamp formatting across the codebase
|
|
13
|
+
|
|
14
|
+
Input formats supported:
|
|
15
|
+
- datetime objects (with or without timezone)
|
|
16
|
+
- Unix timestamps (int/float)
|
|
17
|
+
- Various string formats
|
|
18
|
+
|
|
19
|
+
Output:
|
|
20
|
+
- Standardized timestamp strings in format: "%Y-%m-%d %H:%M:%S.%f"
|
|
21
|
+
- UTC normalized timestamps
|
|
22
|
+
- Validation utilities
|
|
23
|
+
|
|
24
|
+
Prerequisites:
|
|
25
|
+
- CONFIG.FORMATS.TIMESTAMP for standard format
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import re
|
|
31
|
+
from datetime import datetime, timezone
|
|
32
|
+
from typing import Union
|
|
33
|
+
|
|
34
|
+
# Default standard format
|
|
35
|
+
DEFAULT_FORMAT = "%Y-%m-%d %H:%M:%S"
|
|
36
|
+
|
|
37
|
+
# Try to get standard format from config, fallback to default
|
|
38
|
+
try:
|
|
39
|
+
import scitex as stx
|
|
40
|
+
|
|
41
|
+
CONFIG = stx.io.load_configs()
|
|
42
|
+
STANDARD_FORMAT = (
|
|
43
|
+
getattr(getattr(CONFIG, "FORMATS", None), "TIMESTAMP", None) or DEFAULT_FORMAT
|
|
44
|
+
)
|
|
45
|
+
except Exception:
|
|
46
|
+
STANDARD_FORMAT = DEFAULT_FORMAT
|
|
47
|
+
|
|
48
|
+
# Common alternative formats to try when parsing
|
|
49
|
+
ALTERNATIVE_FORMATS = [
|
|
50
|
+
"%Y-%m-%dT%H:%M:%S.%f",
|
|
51
|
+
"%Y-%m-%dT%H:%M:%S", # ISO 8601 with T (no microseconds)
|
|
52
|
+
"%Y-%m-%d %H:%M:%S.%f",
|
|
53
|
+
"%Y-%m-%d %H:%M:%S",
|
|
54
|
+
"%Y/%m/%d %H:%M:%S.%f",
|
|
55
|
+
"%Y/%m/%d %H:%M:%S",
|
|
56
|
+
"%d-%m-%Y %H:%M:%S.%f",
|
|
57
|
+
"%d-%m-%Y %H:%M:%S",
|
|
58
|
+
"%d/%m/%Y %H:%M:%S.%f",
|
|
59
|
+
"%d/%m/%Y %H:%M:%S",
|
|
60
|
+
"%d/%m/%Y, %H:%M:%S", # Format used in REC_START
|
|
61
|
+
"%Y%m%d %H:%M:%S.%f",
|
|
62
|
+
"%Y%m%d %H:%M:%S",
|
|
63
|
+
"%Y-%m-%d_%H:%M:%S.%f",
|
|
64
|
+
"%Y-%m-%d_%H:%M:%S",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def normalize_timestamp(
|
|
69
|
+
timestamp: Union[datetime, str, int, float],
|
|
70
|
+
return_as: str = "str",
|
|
71
|
+
normalize_utc: bool = True,
|
|
72
|
+
) -> Union[str, datetime, float]:
|
|
73
|
+
"""
|
|
74
|
+
Standardize any timestamp format to requested output type.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
timestamp : datetime, str, int, or float
|
|
79
|
+
Timestamp in any supported format
|
|
80
|
+
return_as : str
|
|
81
|
+
Output format: "str" (default), "datetime", or "timestamp"
|
|
82
|
+
normalize_utc : bool
|
|
83
|
+
If True, normalize to UTC timezone
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
str, datetime, or float
|
|
88
|
+
Standardized timestamp in requested format:
|
|
89
|
+
- "str": String in CONFIG.FORMATS.TIMESTAMP format
|
|
90
|
+
- "datetime": datetime object
|
|
91
|
+
- "timestamp": Unix timestamp (float)
|
|
92
|
+
|
|
93
|
+
Examples
|
|
94
|
+
--------
|
|
95
|
+
>>> from datetime import datetime
|
|
96
|
+
>>> dt = datetime(2010, 6, 18, 10, 15, 0)
|
|
97
|
+
>>> normalize_timestamp(dt, return_as="str", normalize_utc=False)
|
|
98
|
+
'2010-06-18 10:15:00'
|
|
99
|
+
"""
|
|
100
|
+
# Convert to datetime object
|
|
101
|
+
dt = to_datetime(timestamp)
|
|
102
|
+
|
|
103
|
+
# Normalize to UTC if requested
|
|
104
|
+
if normalize_utc:
|
|
105
|
+
if dt.tzinfo is None:
|
|
106
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
107
|
+
else:
|
|
108
|
+
dt = dt.astimezone(timezone.utc)
|
|
109
|
+
|
|
110
|
+
# Return in requested format
|
|
111
|
+
if return_as == "str":
|
|
112
|
+
return dt.strftime(STANDARD_FORMAT)
|
|
113
|
+
elif return_as == "datetime":
|
|
114
|
+
return dt
|
|
115
|
+
elif return_as == "timestamp":
|
|
116
|
+
return dt.timestamp()
|
|
117
|
+
else:
|
|
118
|
+
raise ValueError(
|
|
119
|
+
f"return_as must be 'str', 'datetime', or 'timestamp', got: {return_as}"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def to_datetime(timestamp: Union[datetime, str, int, float]) -> datetime:
|
|
124
|
+
"""
|
|
125
|
+
Convert various timestamp formats to datetime object.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
timestamp : datetime, str, int, or float
|
|
130
|
+
Timestamp in any supported format
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
datetime
|
|
135
|
+
Datetime object
|
|
136
|
+
|
|
137
|
+
Raises
|
|
138
|
+
------
|
|
139
|
+
ValueError
|
|
140
|
+
If string format cannot be parsed
|
|
141
|
+
TypeError
|
|
142
|
+
If timestamp type is not supported
|
|
143
|
+
"""
|
|
144
|
+
# Already datetime
|
|
145
|
+
if isinstance(timestamp, datetime):
|
|
146
|
+
return timestamp
|
|
147
|
+
|
|
148
|
+
# Unix timestamp (int/float)
|
|
149
|
+
elif isinstance(timestamp, (int, float)):
|
|
150
|
+
return datetime.fromtimestamp(timestamp, tz=timezone.utc)
|
|
151
|
+
|
|
152
|
+
# String format
|
|
153
|
+
elif isinstance(timestamp, str):
|
|
154
|
+
# Handle nanosecond precision by truncating to microseconds
|
|
155
|
+
if "." in timestamp and len(timestamp.split(".")[-1]) > 6:
|
|
156
|
+
parts = timestamp.split(".")
|
|
157
|
+
# Keep only first 6 digits of fractional seconds
|
|
158
|
+
truncated_microseconds = parts[-1][:6]
|
|
159
|
+
# Handle cases where there might be additional text after microseconds
|
|
160
|
+
if not truncated_microseconds.isdigit():
|
|
161
|
+
# Extract just the digit portion
|
|
162
|
+
digits = re.match(r"(\d+)", parts[-1])
|
|
163
|
+
if digits:
|
|
164
|
+
truncated_microseconds = digits.group(1)[:6]
|
|
165
|
+
timestamp = ".".join(parts[:-1] + [truncated_microseconds])
|
|
166
|
+
|
|
167
|
+
# Try parsing with various formats
|
|
168
|
+
for fmt in ALTERNATIVE_FORMATS:
|
|
169
|
+
try:
|
|
170
|
+
return datetime.strptime(timestamp, fmt)
|
|
171
|
+
except ValueError:
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
# If no format matched, raise error
|
|
175
|
+
raise ValueError(
|
|
176
|
+
f"Could not parse timestamp string: {timestamp}. "
|
|
177
|
+
f"Tried formats: {ALTERNATIVE_FORMATS}"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
else:
|
|
181
|
+
raise TypeError(
|
|
182
|
+
f"timestamp must be datetime, str, int, or float, got: {type(timestamp)}"
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def validate_timestamp_format(timestamp_str: str) -> bool:
|
|
187
|
+
"""
|
|
188
|
+
Validate that a timestamp string matches the standard format.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
timestamp_str : str
|
|
193
|
+
Timestamp string to validate
|
|
194
|
+
|
|
195
|
+
Returns
|
|
196
|
+
-------
|
|
197
|
+
bool
|
|
198
|
+
True if string matches standard format
|
|
199
|
+
"""
|
|
200
|
+
try:
|
|
201
|
+
datetime.strptime(timestamp_str, STANDARD_FORMAT)
|
|
202
|
+
return True
|
|
203
|
+
except (ValueError, TypeError):
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def format_for_filename(timestamp: Union[datetime, str]) -> str:
|
|
208
|
+
"""
|
|
209
|
+
Format timestamp for use in filenames (no spaces or colons).
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
timestamp : datetime or str
|
|
214
|
+
Timestamp to format
|
|
215
|
+
|
|
216
|
+
Returns
|
|
217
|
+
-------
|
|
218
|
+
str
|
|
219
|
+
Filename-safe timestamp string (YYYYMMDD_HHMMSS)
|
|
220
|
+
|
|
221
|
+
Examples
|
|
222
|
+
--------
|
|
223
|
+
>>> from datetime import datetime
|
|
224
|
+
>>> dt = datetime(2010, 6, 18, 10, 15, 0)
|
|
225
|
+
>>> format_for_filename(dt)
|
|
226
|
+
'20100618_101500'
|
|
227
|
+
"""
|
|
228
|
+
dt = to_datetime(timestamp)
|
|
229
|
+
return dt.strftime("%Y%m%d_%H%M%S")
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def format_for_display(timestamp: Union[datetime, str]) -> str:
|
|
233
|
+
"""
|
|
234
|
+
Format timestamp for human-readable display.
|
|
235
|
+
|
|
236
|
+
Parameters
|
|
237
|
+
----------
|
|
238
|
+
timestamp : datetime or str
|
|
239
|
+
Timestamp to format
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
str
|
|
244
|
+
Human-readable timestamp string
|
|
245
|
+
|
|
246
|
+
Examples
|
|
247
|
+
--------
|
|
248
|
+
>>> from datetime import datetime
|
|
249
|
+
>>> dt = datetime(2010, 6, 18, 10, 15, 0)
|
|
250
|
+
>>> format_for_display(dt)
|
|
251
|
+
'2010-06-18 10:15:00'
|
|
252
|
+
"""
|
|
253
|
+
dt = to_datetime(timestamp)
|
|
254
|
+
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def parse_patient_recording_start_format(
|
|
258
|
+
patient_recording_start_str: str,
|
|
259
|
+
) -> datetime:
|
|
260
|
+
"""
|
|
261
|
+
Parse recording start time from CONFIG.PATIENTS.REC_START format.
|
|
262
|
+
|
|
263
|
+
Parameters
|
|
264
|
+
----------
|
|
265
|
+
patient_recording_start_str : str
|
|
266
|
+
Recording start time string in format "DD/MM/YYYY, HH:MM:SS"
|
|
267
|
+
|
|
268
|
+
Returns
|
|
269
|
+
-------
|
|
270
|
+
datetime
|
|
271
|
+
Parsed datetime object
|
|
272
|
+
|
|
273
|
+
Examples
|
|
274
|
+
--------
|
|
275
|
+
>>> parse_patient_recording_start_format("10/06/2010, 07:40:34")
|
|
276
|
+
datetime.datetime(2010, 6, 10, 7, 40, 34)
|
|
277
|
+
"""
|
|
278
|
+
REC_START_FORMAT = "%d/%m/%Y, %H:%M:%S"
|
|
279
|
+
return datetime.strptime(patient_recording_start_str, REC_START_FORMAT)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def get_time_delta_seconds(
|
|
283
|
+
start: Union[datetime, str], end: Union[datetime, str]
|
|
284
|
+
) -> float:
|
|
285
|
+
"""
|
|
286
|
+
Calculate time difference in seconds between two timestamps.
|
|
287
|
+
|
|
288
|
+
Parameters
|
|
289
|
+
----------
|
|
290
|
+
start : datetime or str
|
|
291
|
+
Start timestamp
|
|
292
|
+
end : datetime or str
|
|
293
|
+
End timestamp
|
|
294
|
+
|
|
295
|
+
Returns
|
|
296
|
+
-------
|
|
297
|
+
float
|
|
298
|
+
Time difference in seconds
|
|
299
|
+
"""
|
|
300
|
+
start_dt = to_datetime(start)
|
|
301
|
+
end_dt = to_datetime(end)
|
|
302
|
+
delta = end_dt - start_dt
|
|
303
|
+
return delta.total_seconds()
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# EOF
|
scitex/db/_delete_duplicates.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
2
|
# Timestamp: "2025-07-12 12:00:00 (ywatanabe)"
|
|
4
3
|
# File: ./src/scitex/db/_delete_duplicates.py
|
|
5
4
|
# ----------------------------------------
|
|
@@ -15,7 +14,8 @@ The actual implementation has been moved to _sqlite3._delete_duplicates
|
|
|
15
14
|
as it is SQLite3-specific.
|
|
16
15
|
"""
|
|
17
16
|
|
|
18
|
-
from scitex.
|
|
17
|
+
from scitex.logging import warn_deprecated
|
|
18
|
+
|
|
19
19
|
from ._sqlite3._delete_duplicates import delete_sqlite3_duplicates
|
|
20
20
|
|
|
21
21
|
|
|
@@ -28,8 +28,8 @@ def delete_duplicates(*args, **kwargs):
|
|
|
28
28
|
Use scitex.db._sqlite3.delete_sqlite3_duplicates() instead.
|
|
29
29
|
"""
|
|
30
30
|
warn_deprecated(
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
old_name="scitex.db.delete_duplicates",
|
|
32
|
+
new_name="scitex.db._sqlite3.delete_sqlite3_duplicates",
|
|
33
33
|
version="3.0.0",
|
|
34
34
|
)
|
|
35
35
|
return delete_sqlite3_duplicates(*args, **kwargs)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
2
|
# Timestamp: "2025-07-16 14:00:04 (ywatanabe)"
|
|
4
3
|
# File: /ssh:sp:/home/ywatanabe/proj/scitex_repo/src/scitex/db/_sqlite3/_delete_duplicates.py
|
|
5
4
|
# ----------------------------------------
|
|
@@ -177,7 +176,17 @@ def _delete_entry(
|
|
|
177
176
|
cursor, duplicated_row, table_name, dry_run
|
|
178
177
|
)
|
|
179
178
|
if is_verified:
|
|
180
|
-
|
|
179
|
+
# Construct proper DELETE query (delete only one matching row)
|
|
180
|
+
columns = list(duplicated_row.index)
|
|
181
|
+
where_conditions = " AND ".join([f"{col} = ?" for col in columns])
|
|
182
|
+
delete_query = f"""
|
|
183
|
+
DELETE FROM {table_name}
|
|
184
|
+
WHERE rowid IN (
|
|
185
|
+
SELECT rowid FROM {table_name}
|
|
186
|
+
WHERE {where_conditions}
|
|
187
|
+
LIMIT 1
|
|
188
|
+
)
|
|
189
|
+
"""
|
|
181
190
|
if dry_run:
|
|
182
191
|
print(f"[DRY RUN] Would delete entry:\n{duplicated_row}")
|
|
183
192
|
else:
|