scitex 2.0.0__py2.py3-none-any.whl → 2.1.0__py2.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 +53 -15
- scitex/__main__.py +72 -26
- scitex/__version__.py +1 -1
- scitex/_sh.py +145 -23
- scitex/ai/__init__.py +30 -16
- scitex/ai/_gen_ai/_Anthropic.py +5 -7
- scitex/ai/_gen_ai/_BaseGenAI.py +2 -2
- scitex/ai/_gen_ai/_DeepSeek.py +10 -2
- scitex/ai/_gen_ai/_Google.py +2 -2
- scitex/ai/_gen_ai/_Llama.py +2 -2
- scitex/ai/_gen_ai/_OpenAI.py +2 -2
- scitex/ai/_gen_ai/_PARAMS.py +51 -65
- scitex/ai/_gen_ai/_Perplexity.py +2 -2
- scitex/ai/_gen_ai/__init__.py +25 -14
- scitex/ai/_gen_ai/_format_output_func.py +4 -4
- scitex/ai/classification/{classifier_server.py → Classifier.py} +5 -5
- scitex/ai/classification/CrossValidationExperiment.py +374 -0
- scitex/ai/classification/__init__.py +43 -4
- scitex/ai/classification/reporters/_BaseClassificationReporter.py +281 -0
- scitex/ai/classification/reporters/_ClassificationReporter.py +773 -0
- scitex/ai/classification/reporters/_MultiClassificationReporter.py +406 -0
- scitex/ai/classification/reporters/_SingleClassificationReporter.py +1834 -0
- scitex/ai/classification/reporters/__init__.py +11 -0
- scitex/ai/classification/reporters/reporter_utils/_Plotter.py +1028 -0
- scitex/ai/classification/reporters/reporter_utils/__init__.py +80 -0
- scitex/ai/classification/reporters/reporter_utils/aggregation.py +457 -0
- scitex/ai/classification/reporters/reporter_utils/data_models.py +313 -0
- scitex/ai/classification/reporters/reporter_utils/reporting.py +1056 -0
- scitex/ai/classification/reporters/reporter_utils/storage.py +221 -0
- scitex/ai/classification/reporters/reporter_utils/validation.py +395 -0
- scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +568 -0
- scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +688 -0
- scitex/ai/classification/timeseries/_TimeSeriesMetadata.py +139 -0
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +1716 -0
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +1685 -0
- scitex/ai/classification/timeseries/_TimeSeriesStrategy.py +84 -0
- scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +610 -0
- scitex/ai/classification/timeseries/__init__.py +39 -0
- scitex/ai/classification/timeseries/_normalize_timestamp.py +436 -0
- scitex/ai/clustering/_umap.py +2 -2
- scitex/ai/feature_extraction/vit.py +1 -0
- scitex/ai/feature_selection/__init__.py +30 -0
- scitex/ai/feature_selection/feature_selection.py +364 -0
- scitex/ai/loss/multi_task_loss.py +1 -1
- scitex/ai/metrics/__init__.py +51 -4
- scitex/ai/metrics/_calc_bacc.py +61 -0
- scitex/ai/metrics/_calc_bacc_from_conf_mat.py +38 -0
- scitex/ai/metrics/_calc_clf_report.py +78 -0
- scitex/ai/metrics/_calc_conf_mat.py +93 -0
- scitex/ai/metrics/_calc_feature_importance.py +183 -0
- scitex/ai/metrics/_calc_mcc.py +61 -0
- scitex/ai/metrics/_calc_pre_rec_auc.py +116 -0
- scitex/ai/metrics/_calc_roc_auc.py +110 -0
- scitex/ai/metrics/_calc_seizure_prediction_metrics.py +490 -0
- scitex/ai/metrics/{silhoute_score_block.py → _calc_silhouette_score.py} +15 -8
- scitex/ai/metrics/_normalize_labels.py +83 -0
- scitex/ai/plt/__init__.py +47 -8
- scitex/ai/plt/{_conf_mat.py → _plot_conf_mat.py} +158 -87
- scitex/ai/plt/_plot_feature_importance.py +323 -0
- scitex/ai/plt/_plot_learning_curve.py +345 -0
- scitex/ai/plt/_plot_optuna_study.py +225 -0
- scitex/ai/plt/_plot_pre_rec_curve.py +290 -0
- scitex/ai/plt/_plot_roc_curve.py +255 -0
- scitex/ai/training/{learning_curve_logger.py → _LearningCurveLogger.py} +197 -213
- scitex/ai/training/__init__.py +2 -2
- scitex/ai/utils/grid_search.py +3 -3
- scitex/benchmark/__init__.py +52 -0
- scitex/benchmark/benchmark.py +400 -0
- scitex/benchmark/monitor.py +370 -0
- scitex/benchmark/profiler.py +297 -0
- scitex/browser/__init__.py +48 -0
- scitex/browser/automation/CookieHandler.py +216 -0
- scitex/browser/automation/__init__.py +7 -0
- scitex/browser/collaboration/__init__.py +55 -0
- scitex/browser/collaboration/auth_helpers.py +94 -0
- scitex/browser/collaboration/collaborative_agent.py +136 -0
- scitex/browser/collaboration/credential_manager.py +188 -0
- scitex/browser/collaboration/interactive_panel.py +400 -0
- scitex/browser/collaboration/persistent_browser.py +170 -0
- scitex/browser/collaboration/shared_session.py +383 -0
- scitex/browser/collaboration/standard_interactions.py +246 -0
- scitex/browser/collaboration/visual_feedback.py +181 -0
- scitex/browser/core/BrowserMixin.py +326 -0
- scitex/browser/core/ChromeProfileManager.py +446 -0
- scitex/browser/core/__init__.py +9 -0
- scitex/browser/debugging/__init__.py +18 -0
- scitex/browser/debugging/_browser_logger.py +657 -0
- scitex/browser/debugging/_highlight_element.py +143 -0
- scitex/browser/debugging/_show_grid.py +154 -0
- scitex/browser/interaction/__init__.py +24 -0
- scitex/browser/interaction/click_center.py +149 -0
- scitex/browser/interaction/click_with_fallbacks.py +206 -0
- scitex/browser/interaction/close_popups.py +498 -0
- scitex/browser/interaction/fill_with_fallbacks.py +209 -0
- scitex/browser/pdf/__init__.py +14 -0
- scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +200 -0
- scitex/browser/pdf/detect_chrome_pdf_viewer.py +198 -0
- scitex/browser/remote/CaptchaHandler.py +434 -0
- scitex/browser/remote/ZenRowsAPIClient.py +347 -0
- scitex/browser/remote/ZenRowsBrowserManager.py +570 -0
- scitex/browser/remote/__init__.py +11 -0
- scitex/browser/stealth/HumanBehavior.py +344 -0
- scitex/browser/stealth/StealthManager.py +1008 -0
- scitex/browser/stealth/__init__.py +9 -0
- scitex/browser/template.py +122 -0
- scitex/capture/__init__.py +110 -0
- scitex/capture/__main__.py +25 -0
- scitex/capture/capture.py +848 -0
- scitex/capture/cli.py +233 -0
- scitex/capture/gif.py +344 -0
- scitex/capture/mcp_server.py +961 -0
- scitex/capture/session.py +70 -0
- scitex/capture/utils.py +705 -0
- scitex/cli/__init__.py +17 -0
- scitex/cli/cloud.py +447 -0
- scitex/cli/main.py +42 -0
- scitex/cli/scholar.py +280 -0
- scitex/context/_suppress_output.py +5 -3
- scitex/db/__init__.py +30 -3
- scitex/db/__main__.py +75 -0
- scitex/db/_check_health.py +381 -0
- scitex/db/_delete_duplicates.py +25 -386
- scitex/db/_inspect.py +335 -114
- scitex/db/_inspect_optimized.py +301 -0
- scitex/db/{_PostgreSQL.py → _postgresql/_PostgreSQL.py} +3 -3
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BackupMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BatchMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BlobMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_ConnectionMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_MaintenanceMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_QueryMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_SchemaMixin.py +1 -1
- scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_TransactionMixin.py +1 -1
- scitex/db/_postgresql/__init__.py +6 -0
- scitex/db/_sqlite3/_SQLite3.py +210 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin.py +581 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin_v01-need-_hash-col.py +517 -0
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_BatchMixin.py +1 -1
- scitex/db/_sqlite3/_SQLite3Mixins/_BlobMixin.py +281 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin.py +548 -0
- scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin_v01-indentation-issues.py +583 -0
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_ConnectionMixin.py +29 -13
- scitex/db/_sqlite3/_SQLite3Mixins/_GitMixin.py +583 -0
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_ImportExportMixin.py +1 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_IndexMixin.py +1 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_MaintenanceMixin.py +2 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_QueryMixin.py +37 -10
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_RowMixin.py +46 -6
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_TableMixin.py +56 -10
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_TransactionMixin.py +1 -1
- scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/__init__.py +14 -2
- scitex/db/_sqlite3/__init__.py +7 -0
- scitex/db/_sqlite3/_delete_duplicates.py +274 -0
- scitex/decorators/__init__.py +2 -0
- scitex/decorators/_cache_disk.py +13 -5
- scitex/decorators/_cache_disk_async.py +49 -0
- scitex/decorators/_deprecated.py +175 -10
- scitex/decorators/_timeout.py +1 -1
- scitex/dev/_analyze_code_flow.py +2 -2
- scitex/dict/_DotDict.py +73 -15
- scitex/dict/_DotDict_v01-not-handling-recursive-instantiations.py +442 -0
- scitex/dict/_DotDict_v02-not-serializing-Path-object.py +446 -0
- scitex/dict/__init__.py +2 -0
- scitex/dict/_flatten.py +27 -0
- scitex/dsp/_crop.py +2 -2
- scitex/dsp/_demo_sig.py +2 -2
- scitex/dsp/_detect_ripples.py +2 -2
- scitex/dsp/_hilbert.py +2 -2
- scitex/dsp/_listen.py +6 -6
- scitex/dsp/_modulation_index.py +2 -2
- scitex/dsp/_pac.py +1 -1
- scitex/dsp/_psd.py +2 -2
- scitex/dsp/_resample.py +2 -1
- scitex/dsp/_time.py +3 -2
- scitex/dsp/_wavelet.py +3 -2
- scitex/dsp/add_noise.py +2 -2
- scitex/dsp/example.py +1 -0
- scitex/dsp/filt.py +10 -9
- scitex/dsp/template.py +3 -2
- scitex/dsp/utils/_differential_bandpass_filters.py +1 -1
- scitex/dsp/utils/pac.py +2 -2
- scitex/dt/_normalize_timestamp.py +432 -0
- scitex/errors.py +572 -0
- scitex/gen/_DimHandler.py +2 -2
- scitex/gen/__init__.py +37 -7
- scitex/gen/_deprecated_close.py +80 -0
- scitex/gen/_deprecated_start.py +26 -0
- scitex/gen/_detect_environment.py +152 -0
- scitex/gen/_detect_notebook_path.py +169 -0
- scitex/gen/_embed.py +6 -2
- scitex/gen/_get_notebook_path.py +257 -0
- scitex/gen/_less.py +1 -1
- scitex/gen/_list_packages.py +2 -2
- scitex/gen/_norm.py +44 -9
- scitex/gen/_norm_cache.py +269 -0
- scitex/gen/_src.py +3 -5
- scitex/gen/_title_case.py +3 -3
- scitex/io/__init__.py +28 -6
- scitex/io/_glob.py +13 -7
- scitex/io/_load.py +108 -21
- scitex/io/_load_cache.py +303 -0
- scitex/io/_load_configs.py +40 -15
- scitex/io/{_H5Explorer.py → _load_modules/_H5Explorer.py} +80 -17
- scitex/io/_load_modules/_ZarrExplorer.py +114 -0
- scitex/io/_load_modules/_bibtex.py +207 -0
- scitex/io/_load_modules/_hdf5.py +53 -178
- scitex/io/_load_modules/_json.py +5 -3
- scitex/io/_load_modules/_pdf.py +871 -16
- scitex/io/_load_modules/_sqlite3.py +15 -0
- scitex/io/_load_modules/_txt.py +41 -12
- scitex/io/_load_modules/_yaml.py +4 -3
- scitex/io/_load_modules/_zarr.py +126 -0
- scitex/io/_save.py +429 -171
- scitex/io/_save_modules/__init__.py +6 -0
- scitex/io/_save_modules/_bibtex.py +194 -0
- scitex/io/_save_modules/_csv.py +8 -4
- scitex/io/_save_modules/_excel.py +174 -15
- scitex/io/_save_modules/_hdf5.py +251 -226
- scitex/io/_save_modules/_image.py +1 -3
- scitex/io/_save_modules/_json.py +49 -4
- scitex/io/_save_modules/_listed_dfs_as_csv.py +1 -3
- scitex/io/_save_modules/_listed_scalars_as_csv.py +1 -3
- scitex/io/_save_modules/_tex.py +277 -0
- scitex/io/_save_modules/_yaml.py +42 -3
- scitex/io/_save_modules/_zarr.py +160 -0
- scitex/io/utils/__init__.py +20 -0
- scitex/io/utils/h5_to_zarr.py +616 -0
- scitex/linalg/_geometric_median.py +6 -2
- scitex/{gen/_tee.py → logging/_Tee.py} +43 -84
- scitex/logging/__init__.py +122 -0
- scitex/logging/_config.py +158 -0
- scitex/logging/_context.py +103 -0
- scitex/logging/_formatters.py +128 -0
- scitex/logging/_handlers.py +64 -0
- scitex/logging/_levels.py +35 -0
- scitex/logging/_logger.py +163 -0
- scitex/logging/_print_capture.py +95 -0
- scitex/ml/__init__.py +69 -0
- scitex/{ai/genai/anthropic.py → ml/_gen_ai/_Anthropic.py} +13 -19
- scitex/{ai/genai/base_genai.py → ml/_gen_ai/_BaseGenAI.py} +5 -5
- scitex/{ai/genai/deepseek.py → ml/_gen_ai/_DeepSeek.py} +11 -16
- scitex/{ai/genai/google.py → ml/_gen_ai/_Google.py} +7 -15
- scitex/{ai/genai/groq.py → ml/_gen_ai/_Groq.py} +1 -8
- scitex/{ai/genai/llama.py → ml/_gen_ai/_Llama.py} +3 -16
- scitex/{ai/genai/openai.py → ml/_gen_ai/_OpenAI.py} +3 -3
- scitex/{ai/genai/params.py → ml/_gen_ai/_PARAMS.py} +51 -65
- scitex/{ai/genai/perplexity.py → ml/_gen_ai/_Perplexity.py} +3 -14
- scitex/ml/_gen_ai/__init__.py +43 -0
- scitex/{ai/genai/calc_cost.py → ml/_gen_ai/_calc_cost.py} +1 -1
- scitex/{ai/genai/format_output_func.py → ml/_gen_ai/_format_output_func.py} +4 -4
- scitex/{ai/genai/genai_factory.py → ml/_gen_ai/_genai_factory.py} +8 -8
- scitex/ml/activation/__init__.py +8 -0
- scitex/ml/activation/_define.py +11 -0
- scitex/{ai/classifier_server.py → ml/classification/Classifier.py} +5 -5
- scitex/ml/classification/CrossValidationExperiment.py +374 -0
- scitex/ml/classification/__init__.py +46 -0
- scitex/ml/classification/reporters/_BaseClassificationReporter.py +281 -0
- scitex/ml/classification/reporters/_ClassificationReporter.py +773 -0
- scitex/ml/classification/reporters/_MultiClassificationReporter.py +406 -0
- scitex/ml/classification/reporters/_SingleClassificationReporter.py +1834 -0
- scitex/ml/classification/reporters/__init__.py +11 -0
- scitex/ml/classification/reporters/reporter_utils/_Plotter.py +1028 -0
- scitex/ml/classification/reporters/reporter_utils/__init__.py +80 -0
- scitex/ml/classification/reporters/reporter_utils/aggregation.py +457 -0
- scitex/ml/classification/reporters/reporter_utils/data_models.py +313 -0
- scitex/ml/classification/reporters/reporter_utils/reporting.py +1056 -0
- scitex/ml/classification/reporters/reporter_utils/storage.py +221 -0
- scitex/ml/classification/reporters/reporter_utils/validation.py +395 -0
- scitex/ml/classification/timeseries/_TimeSeriesBlockingSplit.py +568 -0
- scitex/ml/classification/timeseries/_TimeSeriesCalendarSplit.py +688 -0
- scitex/ml/classification/timeseries/_TimeSeriesMetadata.py +139 -0
- scitex/ml/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +1716 -0
- scitex/ml/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +1685 -0
- scitex/ml/classification/timeseries/_TimeSeriesStrategy.py +84 -0
- scitex/ml/classification/timeseries/_TimeSeriesStratifiedSplit.py +610 -0
- scitex/ml/classification/timeseries/__init__.py +39 -0
- scitex/ml/classification/timeseries/_normalize_timestamp.py +436 -0
- scitex/ml/clustering/__init__.py +11 -0
- scitex/ml/clustering/_pca.py +115 -0
- scitex/ml/clustering/_umap.py +376 -0
- scitex/ml/feature_extraction/__init__.py +56 -0
- scitex/ml/feature_extraction/vit.py +149 -0
- scitex/ml/feature_selection/__init__.py +30 -0
- scitex/ml/feature_selection/feature_selection.py +364 -0
- scitex/ml/loss/_L1L2Losses.py +34 -0
- scitex/ml/loss/__init__.py +12 -0
- scitex/ml/loss/multi_task_loss.py +47 -0
- scitex/ml/metrics/__init__.py +56 -0
- scitex/ml/metrics/_calc_bacc.py +61 -0
- scitex/ml/metrics/_calc_bacc_from_conf_mat.py +38 -0
- scitex/ml/metrics/_calc_clf_report.py +78 -0
- scitex/ml/metrics/_calc_conf_mat.py +93 -0
- scitex/ml/metrics/_calc_feature_importance.py +183 -0
- scitex/ml/metrics/_calc_mcc.py +61 -0
- scitex/ml/metrics/_calc_pre_rec_auc.py +116 -0
- scitex/ml/metrics/_calc_roc_auc.py +110 -0
- scitex/ml/metrics/_calc_seizure_prediction_metrics.py +490 -0
- scitex/ml/metrics/_calc_silhouette_score.py +503 -0
- scitex/ml/metrics/_normalize_labels.py +83 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/__init__.py +0 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/__init__.py +3 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger.py +207 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger2020.py +238 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger913A.py +215 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/rangerqh.py +184 -0
- scitex/ml/optim/Ranger_Deep_Learning_Optimizer/setup.py +24 -0
- scitex/ml/optim/__init__.py +13 -0
- scitex/ml/optim/_get_set.py +31 -0
- scitex/ml/optim/_optimizers.py +71 -0
- scitex/ml/plt/__init__.py +60 -0
- scitex/ml/plt/_plot_conf_mat.py +663 -0
- scitex/ml/plt/_plot_feature_importance.py +323 -0
- scitex/ml/plt/_plot_learning_curve.py +345 -0
- scitex/ml/plt/_plot_optuna_study.py +225 -0
- scitex/ml/plt/_plot_pre_rec_curve.py +290 -0
- scitex/ml/plt/_plot_roc_curve.py +255 -0
- scitex/ml/sk/__init__.py +11 -0
- scitex/ml/sk/_clf.py +58 -0
- scitex/ml/sk/_to_sktime.py +100 -0
- scitex/ml/sklearn/__init__.py +26 -0
- scitex/ml/sklearn/clf.py +58 -0
- scitex/ml/sklearn/to_sktime.py +100 -0
- scitex/{ai/training/early_stopping.py → ml/training/_EarlyStopping.py} +1 -2
- scitex/{ai → ml/training}/_LearningCurveLogger.py +198 -242
- scitex/ml/training/__init__.py +7 -0
- scitex/ml/utils/__init__.py +22 -0
- scitex/ml/utils/_check_params.py +50 -0
- scitex/ml/utils/_default_dataset.py +46 -0
- scitex/ml/utils/_format_samples_for_sktime.py +26 -0
- scitex/ml/utils/_label_encoder.py +134 -0
- scitex/ml/utils/_merge_labels.py +22 -0
- scitex/ml/utils/_sliding_window_data_augmentation.py +11 -0
- scitex/ml/utils/_under_sample.py +51 -0
- scitex/ml/utils/_verify_n_gpus.py +16 -0
- scitex/ml/utils/grid_search.py +148 -0
- scitex/nn/_BNet.py +15 -9
- scitex/nn/_Filters.py +2 -2
- scitex/nn/_ModulationIndex.py +2 -2
- scitex/nn/_PAC.py +1 -1
- scitex/nn/_Spectrogram.py +12 -3
- scitex/nn/__init__.py +9 -10
- scitex/path/__init__.py +18 -0
- scitex/path/_clean.py +4 -0
- scitex/path/_find.py +9 -4
- scitex/path/_symlink.py +348 -0
- scitex/path/_version.py +4 -3
- scitex/pd/__init__.py +2 -0
- scitex/pd/_get_unique.py +99 -0
- scitex/plt/__init__.py +114 -5
- scitex/plt/_subplots/_AxesWrapper.py +1 -3
- scitex/plt/_subplots/_AxisWrapper.py +7 -3
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +47 -13
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +160 -2
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +26 -4
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +322 -0
- scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +1 -0
- scitex/plt/_subplots/_FigWrapper.py +62 -6
- scitex/plt/_subplots/_export_as_csv.py +43 -27
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +5 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +81 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_bar.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_barh.py +20 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_boxplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_contour.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_errorbar.py +35 -18
- scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_fill.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_fill_between.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_hist.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +15 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_conf_mat.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_ecdf.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_fillv.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_heatmap.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_image.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_joyplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_line.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_mean_ci.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_mean_std.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_median_iqr.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_raster.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_rectangle.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +35 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter_hist.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_shaded_line.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_violin.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_scatter.py +6 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_barplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_boxplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_heatmap.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_histplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_kdeplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_scatterplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_stripplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_swarmplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_violinplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_text.py +60 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_violin.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_violinplot.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +1 -3
- scitex/plt/_subplots/_export_as_csv_formatters.py +56 -59
- scitex/plt/ax/_style/_hide_spines.py +1 -3
- scitex/plt/ax/_style/_rotate_labels.py +180 -76
- scitex/plt/ax/_style/_rotate_labels_v01.py +248 -0
- scitex/plt/ax/_style/_set_meta.py +11 -4
- scitex/plt/ax/_style/_set_supxyt.py +3 -3
- scitex/plt/ax/_style/_set_xyt.py +3 -3
- scitex/plt/ax/_style/_share_axes.py +2 -2
- scitex/plt/color/__init__.py +4 -4
- scitex/plt/color/{_get_colors_from_cmap.py → _get_colors_from_conf_matap.py} +7 -7
- scitex/plt/utils/_configure_mpl.py +99 -86
- scitex/plt/utils/_histogram_utils.py +1 -3
- scitex/plt/utils/_is_valid_axis.py +1 -3
- scitex/plt/utils/_scitex_config.py +1 -0
- scitex/repro/__init__.py +75 -0
- scitex/{reproduce → repro}/_gen_ID.py +1 -1
- scitex/{reproduce → repro}/_gen_timestamp.py +1 -1
- scitex/repro_rng/_RandomStateManager.py +590 -0
- scitex/repro_rng/_RandomStateManager_v01-no-verbose-options.py +414 -0
- scitex/repro_rng/__init__.py +39 -0
- scitex/reproduce/__init__.py +25 -13
- scitex/reproduce/_hash_array.py +22 -0
- scitex/resource/_get_processor_usages.py +4 -4
- scitex/resource/_get_specs.py +2 -2
- scitex/resource/_log_processor_usages.py +2 -2
- scitex/rng/_RandomStateManager.py +590 -0
- scitex/rng/_RandomStateManager_v01-no-verbose-options.py +414 -0
- scitex/rng/__init__.py +39 -0
- scitex/scholar/__init__.py +309 -19
- scitex/scholar/__main__.py +319 -0
- scitex/scholar/auth/ScholarAuthManager.py +308 -0
- scitex/scholar/auth/__init__.py +12 -0
- scitex/scholar/auth/core/AuthenticationGateway.py +473 -0
- scitex/scholar/auth/core/BrowserAuthenticator.py +386 -0
- scitex/scholar/auth/core/StrategyResolver.py +309 -0
- scitex/scholar/auth/core/__init__.py +16 -0
- scitex/scholar/auth/gateway/_OpenURLLinkFinder.py +120 -0
- scitex/scholar/auth/gateway/_OpenURLResolver.py +209 -0
- scitex/scholar/auth/gateway/__init__.py +38 -0
- scitex/scholar/auth/gateway/_resolve_functions.py +101 -0
- scitex/scholar/auth/providers/BaseAuthenticator.py +166 -0
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +484 -0
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +619 -0
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +686 -0
- scitex/scholar/auth/providers/__init__.py +18 -0
- scitex/scholar/auth/session/AuthCacheManager.py +189 -0
- scitex/scholar/auth/session/SessionManager.py +159 -0
- scitex/scholar/auth/session/__init__.py +11 -0
- scitex/scholar/auth/sso/BaseSSOAutomator.py +373 -0
- scitex/scholar/auth/sso/OpenAthensSSOAutomator.py +378 -0
- scitex/scholar/auth/sso/SSOAutomator.py +180 -0
- scitex/scholar/auth/sso/UniversityOfMelbourneSSOAutomator.py +380 -0
- scitex/scholar/auth/sso/__init__.py +15 -0
- scitex/scholar/browser/ScholarBrowserManager.py +705 -0
- scitex/scholar/browser/__init__.py +38 -0
- scitex/scholar/browser/utils/__init__.py +13 -0
- scitex/scholar/browser/utils/click_and_wait.py +205 -0
- scitex/scholar/browser/utils/close_unwanted_pages.py +140 -0
- scitex/scholar/browser/utils/wait_redirects.py +732 -0
- scitex/scholar/config/PublisherRules.py +132 -0
- scitex/scholar/config/ScholarConfig.py +126 -0
- scitex/scholar/config/__init__.py +17 -0
- scitex/scholar/core/Paper.py +627 -0
- scitex/scholar/core/Papers.py +722 -0
- scitex/scholar/core/Scholar.py +1975 -0
- scitex/scholar/core/__init__.py +9 -0
- scitex/scholar/impact_factor/ImpactFactorEngine.py +204 -0
- scitex/scholar/impact_factor/__init__.py +20 -0
- scitex/scholar/impact_factor/estimation/ImpactFactorEstimationEngine.py +0 -0
- scitex/scholar/impact_factor/estimation/__init__.py +40 -0
- scitex/scholar/impact_factor/estimation/build_database.py +0 -0
- scitex/scholar/impact_factor/estimation/core/__init__.py +28 -0
- scitex/scholar/impact_factor/estimation/core/cache_manager.py +523 -0
- scitex/scholar/impact_factor/estimation/core/calculator.py +355 -0
- scitex/scholar/impact_factor/estimation/core/journal_matcher.py +428 -0
- scitex/scholar/integration/__init__.py +59 -0
- scitex/scholar/integration/base.py +502 -0
- scitex/scholar/integration/mendeley/__init__.py +22 -0
- scitex/scholar/integration/mendeley/exporter.py +166 -0
- scitex/scholar/integration/mendeley/importer.py +236 -0
- scitex/scholar/integration/mendeley/linker.py +79 -0
- scitex/scholar/integration/mendeley/mapper.py +212 -0
- scitex/scholar/integration/zotero/__init__.py +27 -0
- scitex/scholar/integration/zotero/__main__.py +264 -0
- scitex/scholar/integration/zotero/exporter.py +351 -0
- scitex/scholar/integration/zotero/importer.py +372 -0
- scitex/scholar/integration/zotero/linker.py +415 -0
- scitex/scholar/integration/zotero/mapper.py +286 -0
- scitex/scholar/metadata_engines/ScholarEngine.py +588 -0
- scitex/scholar/metadata_engines/__init__.py +21 -0
- scitex/scholar/metadata_engines/individual/ArXivEngine.py +397 -0
- scitex/scholar/metadata_engines/individual/CrossRefEngine.py +274 -0
- scitex/scholar/metadata_engines/individual/CrossRefLocalEngine.py +263 -0
- scitex/scholar/metadata_engines/individual/OpenAlexEngine.py +350 -0
- scitex/scholar/metadata_engines/individual/PubMedEngine.py +329 -0
- scitex/scholar/metadata_engines/individual/SemanticScholarEngine.py +438 -0
- scitex/scholar/metadata_engines/individual/URLDOIEngine.py +410 -0
- scitex/scholar/metadata_engines/individual/_BaseDOIEngine.py +487 -0
- scitex/scholar/metadata_engines/individual/__init__.py +7 -0
- scitex/scholar/metadata_engines/utils/_PubMedConverter.py +469 -0
- scitex/scholar/metadata_engines/utils/_URLDOIExtractor.py +283 -0
- scitex/scholar/metadata_engines/utils/__init__.py +30 -0
- scitex/scholar/metadata_engines/utils/_metadata2bibtex.py +103 -0
- scitex/scholar/metadata_engines/utils/_standardize_metadata.py +376 -0
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +579 -0
- scitex/scholar/pdf_download/__init__.py +5 -0
- scitex/scholar/pdf_download/strategies/__init__.py +38 -0
- scitex/scholar/pdf_download/strategies/chrome_pdf_viewer.py +376 -0
- scitex/scholar/pdf_download/strategies/direct_download.py +131 -0
- scitex/scholar/pdf_download/strategies/manual_download_fallback.py +167 -0
- scitex/scholar/pdf_download/strategies/manual_download_utils.py +996 -0
- scitex/scholar/pdf_download/strategies/response_body.py +207 -0
- scitex/scholar/pipelines/ScholarPipelineBibTeX.py +364 -0
- scitex/scholar/pipelines/ScholarPipelineParallel.py +478 -0
- scitex/scholar/pipelines/ScholarPipelineSingle.py +767 -0
- scitex/scholar/pipelines/__init__.py +49 -0
- scitex/scholar/storage/BibTeXHandler.py +1018 -0
- scitex/scholar/storage/PaperIO.py +468 -0
- scitex/scholar/storage/ScholarLibrary.py +182 -0
- scitex/scholar/storage/_DeduplicationManager.py +548 -0
- scitex/scholar/storage/_LibraryCacheManager.py +724 -0
- scitex/scholar/storage/_LibraryManager.py +1835 -0
- scitex/scholar/storage/__init__.py +28 -0
- scitex/scholar/url_finder/ScholarURLFinder.py +379 -0
- scitex/scholar/url_finder/__init__.py +7 -0
- scitex/scholar/url_finder/strategies/__init__.py +33 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_direct_links.py +261 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_dropdown.py +67 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_href.py +204 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_navigation.py +256 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_publisher_patterns.py +165 -0
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_zotero_translators.py +163 -0
- scitex/scholar/url_finder/strategies/find_supplementary_urls_by_href.py +70 -0
- scitex/scholar/utils/__init__.py +22 -0
- scitex/scholar/utils/bibtex/__init__.py +9 -0
- scitex/scholar/utils/bibtex/_parse_bibtex.py +71 -0
- scitex/scholar/utils/cleanup/__init__.py +8 -0
- scitex/scholar/utils/cleanup/_cleanup_scholar_processes.py +96 -0
- scitex/scholar/utils/cleanup/cleanup_old_extractions.py +117 -0
- scitex/scholar/utils/text/_TextNormalizer.py +407 -0
- scitex/scholar/utils/text/__init__.py +9 -0
- scitex/scholar/zotero/__init__.py +38 -0
- scitex/session/__init__.py +51 -0
- scitex/session/_lifecycle.py +736 -0
- scitex/session/_manager.py +102 -0
- scitex/session/template.py +122 -0
- scitex/stats/__init__.py +30 -26
- scitex/stats/correct/__init__.py +21 -0
- scitex/stats/correct/_correct_bonferroni.py +551 -0
- scitex/stats/correct/_correct_fdr.py +634 -0
- scitex/stats/correct/_correct_holm.py +548 -0
- scitex/stats/correct/_correct_sidak.py +499 -0
- scitex/stats/descriptive/__init__.py +85 -0
- scitex/stats/descriptive/_circular.py +540 -0
- scitex/stats/descriptive/_describe.py +219 -0
- scitex/stats/descriptive/_nan.py +518 -0
- scitex/stats/descriptive/_real.py +189 -0
- scitex/stats/effect_sizes/__init__.py +41 -0
- scitex/stats/effect_sizes/_cliffs_delta.py +325 -0
- scitex/stats/effect_sizes/_cohens_d.py +342 -0
- scitex/stats/effect_sizes/_epsilon_squared.py +315 -0
- scitex/stats/effect_sizes/_eta_squared.py +302 -0
- scitex/stats/effect_sizes/_prob_superiority.py +296 -0
- scitex/stats/posthoc/__init__.py +19 -0
- scitex/stats/posthoc/_dunnett.py +463 -0
- scitex/stats/posthoc/_games_howell.py +383 -0
- scitex/stats/posthoc/_tukey_hsd.py +367 -0
- scitex/stats/power/__init__.py +19 -0
- scitex/stats/power/_power.py +433 -0
- scitex/stats/template.py +119 -0
- scitex/stats/utils/__init__.py +62 -0
- scitex/stats/utils/_effect_size.py +985 -0
- scitex/stats/utils/_formatters.py +270 -0
- scitex/stats/utils/_normalizers.py +927 -0
- scitex/stats/utils/_power.py +433 -0
- scitex/stats_v01/_EffectSizeCalculator.py +488 -0
- scitex/stats_v01/_StatisticalValidator.py +411 -0
- scitex/stats_v01/__init__.py +60 -0
- scitex/stats_v01/_additional_tests.py +415 -0
- scitex/{stats → stats_v01}/_p2stars.py +19 -5
- scitex/stats_v01/_two_sample_tests.py +141 -0
- scitex/stats_v01/desc/__init__.py +83 -0
- scitex/stats_v01/desc/_circular.py +540 -0
- scitex/stats_v01/desc/_describe.py +219 -0
- scitex/stats_v01/desc/_nan.py +518 -0
- scitex/{stats/desc/_nan.py → stats_v01/desc/_nan_v01-20250920_145731.py} +23 -12
- scitex/stats_v01/desc/_real.py +189 -0
- scitex/stats_v01/tests/__corr_test_optimized.py +221 -0
- scitex/stats_v01/tests/_corr_test_optimized.py +179 -0
- scitex/str/__init__.py +1 -3
- scitex/str/_clean_path.py +6 -2
- scitex/str/_latex_fallback.py +267 -160
- scitex/str/_parse.py +44 -36
- scitex/str/_printc.py +1 -3
- scitex/template/__init__.py +87 -0
- scitex/template/_create_project.py +267 -0
- scitex/template/create_pip_project.py +80 -0
- scitex/template/create_research.py +80 -0
- scitex/template/create_singularity.py +80 -0
- scitex/units.py +291 -0
- scitex/utils/_compress_hdf5.py +14 -3
- scitex/utils/_email.py +21 -2
- scitex/utils/_grid.py +6 -4
- scitex/utils/_notify.py +13 -10
- scitex/utils/_verify_scitex_format.py +589 -0
- scitex/utils/_verify_scitex_format_v01.py +370 -0
- scitex/utils/template.py +122 -0
- scitex/web/_search_pubmed.py +62 -16
- scitex-2.1.0.dist-info/LICENSE +21 -0
- scitex-2.1.0.dist-info/METADATA +677 -0
- scitex-2.1.0.dist-info/RECORD +919 -0
- {scitex-2.0.0.dist-info → scitex-2.1.0.dist-info}/WHEEL +1 -1
- scitex-2.1.0.dist-info/entry_points.txt +3 -0
- scitex/ai/__Classifiers.py +0 -101
- scitex/ai/classification/classification_reporter.py +0 -1137
- scitex/ai/classification/classifiers.py +0 -101
- scitex/ai/classification_reporter.py +0 -1161
- scitex/ai/genai/__init__.py +0 -277
- scitex/ai/genai/anthropic_provider.py +0 -320
- scitex/ai/genai/anthropic_refactored.py +0 -109
- scitex/ai/genai/auth_manager.py +0 -200
- scitex/ai/genai/base_provider.py +0 -291
- scitex/ai/genai/chat_history.py +0 -307
- scitex/ai/genai/cost_tracker.py +0 -276
- scitex/ai/genai/deepseek_provider.py +0 -251
- scitex/ai/genai/google_provider.py +0 -228
- scitex/ai/genai/groq_provider.py +0 -248
- scitex/ai/genai/image_processor.py +0 -250
- scitex/ai/genai/llama_provider.py +0 -214
- scitex/ai/genai/mock_provider.py +0 -127
- scitex/ai/genai/model_registry.py +0 -304
- scitex/ai/genai/openai_provider.py +0 -293
- scitex/ai/genai/perplexity_provider.py +0 -205
- scitex/ai/genai/provider_base.py +0 -302
- scitex/ai/genai/provider_factory.py +0 -370
- scitex/ai/genai/response_handler.py +0 -235
- scitex/ai/layer/_Pass.py +0 -21
- scitex/ai/layer/__init__.py +0 -10
- scitex/ai/layer/_switch.py +0 -8
- scitex/ai/metrics/_bACC.py +0 -51
- scitex/ai/plt/_learning_curve.py +0 -194
- scitex/ai/plt/_optuna_study.py +0 -111
- scitex/ai/plt/aucs/__init__.py +0 -2
- scitex/ai/plt/aucs/example.py +0 -60
- scitex/ai/plt/aucs/pre_rec_auc.py +0 -223
- scitex/ai/plt/aucs/roc_auc.py +0 -246
- scitex/ai/sampling/undersample.py +0 -29
- scitex/db/_SQLite3.py +0 -2136
- scitex/db/_SQLite3Mixins/_BlobMixin.py +0 -229
- scitex/gen/_close.py +0 -222
- scitex/gen/_start.py +0 -451
- scitex/general/__init__.py +0 -5
- scitex/io/_load_modules/_db.py +0 -24
- scitex/life/__init__.py +0 -10
- scitex/life/_monitor_rain.py +0 -49
- scitex/reproduce/_fix_seeds.py +0 -45
- scitex/res/__init__.py +0 -5
- scitex/scholar/_local_search.py +0 -454
- scitex/scholar/_paper.py +0 -244
- scitex/scholar/_pdf_downloader.py +0 -325
- scitex/scholar/_search.py +0 -393
- scitex/scholar/_vector_search.py +0 -370
- scitex/scholar/_web_sources.py +0 -457
- scitex/stats/desc/__init__.py +0 -40
- scitex-2.0.0.dist-info/METADATA +0 -307
- scitex-2.0.0.dist-info/RECORD +0 -572
- scitex-2.0.0.dist-info/licenses/LICENSE +0 -7
- /scitex/ai/{act → activation}/__init__.py +0 -0
- /scitex/ai/{act → activation}/_define.py +0 -0
- /scitex/ai/{early_stopping.py → training/_EarlyStopping.py} +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_ImportExportMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_IndexMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_RowMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_TableMixin.py +0 -0
- /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/__init__.py +0 -0
- /scitex/{stats → stats_v01}/_calc_partial_corr.py +0 -0
- /scitex/{stats → stats_v01}/_corr_test_multi.py +0 -0
- /scitex/{stats → stats_v01}/_corr_test_wrapper.py +0 -0
- /scitex/{stats → stats_v01}/_describe_wrapper.py +0 -0
- /scitex/{stats → stats_v01}/_multiple_corrections.py +0 -0
- /scitex/{stats → stats_v01}/_nan_stats.py +0 -0
- /scitex/{stats → stats_v01}/_p2stars_wrapper.py +0 -0
- /scitex/{stats → stats_v01}/_statistical_tests.py +0 -0
- /scitex/{stats/desc/_describe.py → stats_v01/desc/_describe_v01-20250920_145731.py} +0 -0
- /scitex/{stats/desc/_real.py → stats_v01/desc/_real_v01-20250920_145731.py} +0 -0
- /scitex/{stats → stats_v01}/multiple/__init__.py +0 -0
- /scitex/{stats → stats_v01}/multiple/_bonferroni_correction.py +0 -0
- /scitex/{stats → stats_v01}/multiple/_fdr_correction.py +0 -0
- /scitex/{stats → stats_v01}/multiple/_multicompair.py +0 -0
- /scitex/{stats → stats_v01}/tests/__corr_test.py +0 -0
- /scitex/{stats → stats_v01}/tests/__corr_test_multi.py +0 -0
- /scitex/{stats → stats_v01}/tests/__corr_test_single.py +0 -0
- /scitex/{stats → stats_v01}/tests/__init__.py +0 -0
- /scitex/{stats → stats_v01}/tests/_brunner_munzel_test.py +0 -0
- /scitex/{stats → stats_v01}/tests/_nocorrelation_test.py +0 -0
- /scitex/{stats → stats_v01}/tests/_smirnov_grubbs.py +0 -0
- {scitex-2.0.0.dist-info → scitex-2.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-09-11 08:00:00 (ywatanabe)"
|
|
4
|
+
# File: /data/gpfs/projects/punim2354/ywatanabe/SciTeX-Code/src/scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
__FILE__ = __file__
|
|
9
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
10
|
+
# ----------------------------------------
|
|
11
|
+
|
|
12
|
+
import time
|
|
13
|
+
from scitex import logging
|
|
14
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class _ColumnMixin:
|
|
20
|
+
"""Efficient column operations for SQLite3 databases.
|
|
21
|
+
|
|
22
|
+
Handles drop, rename, reorder, add, and modify operations
|
|
23
|
+
with automatic version detection and optimization.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def get_sqlite_version(self) -> Tuple[int, int, int]:
|
|
27
|
+
"""Get SQLite version as tuple (major, minor, patch)."""
|
|
28
|
+
cursor = self.cursor
|
|
29
|
+
cursor.execute("SELECT sqlite_version()")
|
|
30
|
+
version_str = cursor.fetchone()[0]
|
|
31
|
+
parts = version_str.split(".")
|
|
32
|
+
return tuple(int(p) for p in parts[:3])
|
|
33
|
+
|
|
34
|
+
def supports_drop_column(self) -> bool:
|
|
35
|
+
"""Check if SQLite supports DROP COLUMN (3.35.0+)."""
|
|
36
|
+
version = self.get_sqlite_version()
|
|
37
|
+
return version >= (3, 35, 0)
|
|
38
|
+
|
|
39
|
+
def supports_rename_column(self) -> bool:
|
|
40
|
+
"""Check if SQLite supports RENAME COLUMN (3.25.0+)."""
|
|
41
|
+
version = self.get_sqlite_version()
|
|
42
|
+
return version >= (3, 25, 0)
|
|
43
|
+
|
|
44
|
+
def get_column_info(self, table_name: str) -> List[Dict[str, Any]]:
|
|
45
|
+
"""Get detailed information about table columns."""
|
|
46
|
+
cursor = self.cursor
|
|
47
|
+
cursor.execute(f"PRAGMA table_info({table_name})")
|
|
48
|
+
columns = []
|
|
49
|
+
for row in cursor.fetchall():
|
|
50
|
+
columns.append({
|
|
51
|
+
"cid": row[0],
|
|
52
|
+
"name": row[1],
|
|
53
|
+
"type": row[2],
|
|
54
|
+
"notnull": row[3],
|
|
55
|
+
"default": row[4],
|
|
56
|
+
"pk": row[5],
|
|
57
|
+
})
|
|
58
|
+
return columns
|
|
59
|
+
|
|
60
|
+
def column_exists(self, table_name: str, column_name: str) -> bool:
|
|
61
|
+
"""Check if a column exists in the table."""
|
|
62
|
+
columns = self.get_column_info(table_name)
|
|
63
|
+
return any(col["name"] == column_name for col in columns)
|
|
64
|
+
|
|
65
|
+
# ----------------------------------------
|
|
66
|
+
# DROP Operations
|
|
67
|
+
# ----------------------------------------
|
|
68
|
+
|
|
69
|
+
def drop_column(
|
|
70
|
+
self,
|
|
71
|
+
table_name: str,
|
|
72
|
+
column_name: str,
|
|
73
|
+
force_recreate: bool = False,
|
|
74
|
+
progress_callback: Optional[callable] = None,
|
|
75
|
+
) -> bool:
|
|
76
|
+
"""Drop a column from table, using native or recreation method.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
table_name: Name of the table
|
|
80
|
+
column_name: Name of column to drop
|
|
81
|
+
force_recreate: Force table recreation even if native DROP is available
|
|
82
|
+
progress_callback: Function to call with progress updates
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
True if successful, False otherwise
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
# Check if column exists
|
|
89
|
+
if not self.column_exists(table_name, column_name):
|
|
90
|
+
logger.warning(f"Column {column_name} does not exist in {table_name}")
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
# Use native DROP if available and not forced to recreate
|
|
94
|
+
if self.supports_drop_column() and not force_recreate:
|
|
95
|
+
return self._drop_column_native(table_name, column_name)
|
|
96
|
+
else:
|
|
97
|
+
return self._drop_column_recreate(table_name, column_name, progress_callback)
|
|
98
|
+
|
|
99
|
+
except Exception as e:
|
|
100
|
+
logger.error(f"Failed to drop column {column_name} from {table_name}: {e}")
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
def _drop_column_native(self, table_name: str, column_name: str) -> bool:
|
|
104
|
+
"""Drop column using native SQLite ALTER TABLE DROP COLUMN."""
|
|
105
|
+
try:
|
|
106
|
+
cursor = self.cursor
|
|
107
|
+
cursor.execute(f"ALTER TABLE {table_name} DROP COLUMN {column_name}")
|
|
108
|
+
self.conn.commit()
|
|
109
|
+
logger.info(f"Dropped column {column_name} from {table_name} (native)")
|
|
110
|
+
return True
|
|
111
|
+
except Exception as e:
|
|
112
|
+
self.conn.rollback()
|
|
113
|
+
logger.error(f"Native drop failed: {e}")
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
def _drop_column_recreate(
|
|
117
|
+
self,
|
|
118
|
+
table_name: str,
|
|
119
|
+
column_name: str,
|
|
120
|
+
progress_callback: Optional[callable] = None,
|
|
121
|
+
) -> bool:
|
|
122
|
+
"""Drop column by recreating the table (for older SQLite versions)."""
|
|
123
|
+
try:
|
|
124
|
+
cursor = self.cursor
|
|
125
|
+
# Start transaction
|
|
126
|
+
cursor.execute("BEGIN EXCLUSIVE TRANSACTION")
|
|
127
|
+
|
|
128
|
+
# Get columns to keep
|
|
129
|
+
columns = self.get_column_info(table_name)
|
|
130
|
+
keep_columns = [col for col in columns if col["name"] != column_name]
|
|
131
|
+
|
|
132
|
+
if not keep_columns:
|
|
133
|
+
raise ValueError("Cannot drop all columns from table")
|
|
134
|
+
|
|
135
|
+
# Create column list for new table
|
|
136
|
+
column_names = [col["name"] for col in keep_columns]
|
|
137
|
+
column_defs = []
|
|
138
|
+
for col in keep_columns:
|
|
139
|
+
col_def = f"{col['name']} {col['type']}"
|
|
140
|
+
if col["notnull"]:
|
|
141
|
+
col_def += " NOT NULL"
|
|
142
|
+
if col["default"] is not None:
|
|
143
|
+
col_def += f" DEFAULT {col['default']}"
|
|
144
|
+
if col["pk"]:
|
|
145
|
+
col_def += " PRIMARY KEY"
|
|
146
|
+
column_defs.append(col_def)
|
|
147
|
+
|
|
148
|
+
# Get row count for progress
|
|
149
|
+
cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
|
|
150
|
+
total_rows = cursor.fetchone()[0]
|
|
151
|
+
|
|
152
|
+
if progress_callback:
|
|
153
|
+
progress_callback(0, total_rows, "Creating temporary table")
|
|
154
|
+
|
|
155
|
+
# Create temporary table
|
|
156
|
+
temp_table = f"{table_name}_temp_{int(time.time())}"
|
|
157
|
+
cursor.execute(f"""
|
|
158
|
+
CREATE TABLE {temp_table} (
|
|
159
|
+
{', '.join(column_defs)}
|
|
160
|
+
)
|
|
161
|
+
""")
|
|
162
|
+
|
|
163
|
+
# Copy data in batches for progress reporting
|
|
164
|
+
batch_size = 10000
|
|
165
|
+
offset = 0
|
|
166
|
+
|
|
167
|
+
while offset < total_rows:
|
|
168
|
+
cursor.execute(f"""
|
|
169
|
+
INSERT INTO {temp_table} ({', '.join(column_names)})
|
|
170
|
+
SELECT {', '.join(column_names)}
|
|
171
|
+
FROM {table_name}
|
|
172
|
+
LIMIT {batch_size} OFFSET {offset}
|
|
173
|
+
""")
|
|
174
|
+
|
|
175
|
+
offset += batch_size
|
|
176
|
+
if progress_callback:
|
|
177
|
+
progress_callback(min(offset, total_rows), total_rows, "Copying data")
|
|
178
|
+
|
|
179
|
+
# Get indexes, triggers, and views
|
|
180
|
+
cursor.execute(f"""
|
|
181
|
+
SELECT sql FROM sqlite_master
|
|
182
|
+
WHERE type IN ('index', 'trigger', 'view')
|
|
183
|
+
AND tbl_name = '{table_name}'
|
|
184
|
+
AND sql IS NOT NULL
|
|
185
|
+
""")
|
|
186
|
+
dependencies = cursor.fetchall()
|
|
187
|
+
|
|
188
|
+
# Drop old table
|
|
189
|
+
cursor.execute(f"DROP TABLE {table_name}")
|
|
190
|
+
|
|
191
|
+
# Rename temp table
|
|
192
|
+
cursor.execute(f"ALTER TABLE {temp_table} RENAME TO {table_name}")
|
|
193
|
+
|
|
194
|
+
# Recreate indexes, triggers, and views
|
|
195
|
+
for dep in dependencies:
|
|
196
|
+
sql = dep[0]
|
|
197
|
+
# Skip if it references the dropped column
|
|
198
|
+
if column_name not in sql:
|
|
199
|
+
try:
|
|
200
|
+
cursor.execute(sql)
|
|
201
|
+
except Exception as e:
|
|
202
|
+
logger.warning(f"Could not recreate dependency: {e}")
|
|
203
|
+
|
|
204
|
+
# Commit transaction
|
|
205
|
+
self.conn.commit()
|
|
206
|
+
|
|
207
|
+
if progress_callback:
|
|
208
|
+
progress_callback(total_rows, total_rows, "Complete")
|
|
209
|
+
|
|
210
|
+
logger.info(f"Dropped column {column_name} from {table_name} (recreate)")
|
|
211
|
+
return True
|
|
212
|
+
|
|
213
|
+
except Exception as e:
|
|
214
|
+
self.conn.rollback()
|
|
215
|
+
logger.error(f"Table recreation failed: {e}")
|
|
216
|
+
return False
|
|
217
|
+
|
|
218
|
+
# ----------------------------------------
|
|
219
|
+
# RENAME Operations
|
|
220
|
+
# ----------------------------------------
|
|
221
|
+
|
|
222
|
+
def rename_column(self, table_name: str, old_name: str, new_name: str) -> bool:
|
|
223
|
+
"""Rename a column in the table.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
table_name: Name of the table
|
|
227
|
+
old_name: Current column name
|
|
228
|
+
new_name: New column name
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
True if successful, False otherwise
|
|
232
|
+
"""
|
|
233
|
+
try:
|
|
234
|
+
if not self.column_exists(table_name, old_name):
|
|
235
|
+
logger.error(f"Column {old_name} does not exist in {table_name}")
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
if self.column_exists(table_name, new_name):
|
|
239
|
+
logger.error(f"Column {new_name} already exists in {table_name}")
|
|
240
|
+
return False
|
|
241
|
+
|
|
242
|
+
if self.supports_rename_column():
|
|
243
|
+
# Use native RENAME COLUMN
|
|
244
|
+
cursor = self.cursor
|
|
245
|
+
cursor.execute(f"""
|
|
246
|
+
ALTER TABLE {table_name}
|
|
247
|
+
RENAME COLUMN {old_name} TO {new_name}
|
|
248
|
+
""")
|
|
249
|
+
self.conn.commit()
|
|
250
|
+
logger.info(f"Renamed column {old_name} to {new_name} in {table_name}")
|
|
251
|
+
return True
|
|
252
|
+
else:
|
|
253
|
+
# Use table recreation for older versions
|
|
254
|
+
return self._rename_column_recreate(table_name, old_name, new_name)
|
|
255
|
+
|
|
256
|
+
except Exception as e:
|
|
257
|
+
self.conn.rollback()
|
|
258
|
+
logger.error(f"Failed to rename column: {e}")
|
|
259
|
+
return False
|
|
260
|
+
|
|
261
|
+
def _rename_column_recreate(self, table_name: str, old_name: str, new_name: str) -> bool:
|
|
262
|
+
"""Rename column by recreating the table."""
|
|
263
|
+
# Similar to drop_column_recreate but with renamed column
|
|
264
|
+
# Implementation would be similar to _drop_column_recreate
|
|
265
|
+
# but keeping all columns with one renamed
|
|
266
|
+
logger.warning("Column rename via recreation not yet implemented")
|
|
267
|
+
return False
|
|
268
|
+
|
|
269
|
+
# ----------------------------------------
|
|
270
|
+
# REORDER/SORT Operations
|
|
271
|
+
# ----------------------------------------
|
|
272
|
+
|
|
273
|
+
def reorder_columns(
|
|
274
|
+
self,
|
|
275
|
+
table_name: str,
|
|
276
|
+
column_order: List[str],
|
|
277
|
+
progress_callback: Optional[callable] = None,
|
|
278
|
+
) -> bool:
|
|
279
|
+
"""Reorder columns in a table to match the specified order.
|
|
280
|
+
|
|
281
|
+
Similar to pandas: df = df[['col3', 'col1', 'col2']]
|
|
282
|
+
|
|
283
|
+
SQLite doesn't support column reordering directly, so this always
|
|
284
|
+
requires table recreation.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
table_name: Name of the table
|
|
288
|
+
column_order: List of column names in desired order
|
|
289
|
+
progress_callback: Function for progress updates
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
True if successful, False otherwise
|
|
293
|
+
"""
|
|
294
|
+
try:
|
|
295
|
+
# Validate all columns exist
|
|
296
|
+
current_columns = self.get_column_info(table_name)
|
|
297
|
+
current_names = {col["name"] for col in current_columns}
|
|
298
|
+
|
|
299
|
+
if set(column_order) != current_names:
|
|
300
|
+
missing = current_names - set(column_order)
|
|
301
|
+
extra = set(column_order) - current_names
|
|
302
|
+
if missing:
|
|
303
|
+
logger.error(f"Missing columns in order: {missing}")
|
|
304
|
+
if extra:
|
|
305
|
+
logger.error(f"Unknown columns in order: {extra}")
|
|
306
|
+
return False
|
|
307
|
+
|
|
308
|
+
# Create column map
|
|
309
|
+
column_map = {col["name"]: col for col in current_columns}
|
|
310
|
+
|
|
311
|
+
cursor = self.cursor
|
|
312
|
+
cursor.execute("BEGIN EXCLUSIVE TRANSACTION")
|
|
313
|
+
|
|
314
|
+
# Build new table definition with reordered columns
|
|
315
|
+
column_defs = []
|
|
316
|
+
for col_name in column_order:
|
|
317
|
+
col = column_map[col_name]
|
|
318
|
+
col_def = f"{col['name']} {col['type']}"
|
|
319
|
+
if col["notnull"]:
|
|
320
|
+
col_def += " NOT NULL"
|
|
321
|
+
if col["default"] is not None:
|
|
322
|
+
col_def += f" DEFAULT {col['default']}"
|
|
323
|
+
if col["pk"]:
|
|
324
|
+
col_def += " PRIMARY KEY"
|
|
325
|
+
column_defs.append(col_def)
|
|
326
|
+
|
|
327
|
+
# Create temp table with new column order
|
|
328
|
+
temp_table = f"{table_name}_reorder_{int(time.time())}"
|
|
329
|
+
cursor.execute(f"""
|
|
330
|
+
CREATE TABLE {temp_table} (
|
|
331
|
+
{', '.join(column_defs)}
|
|
332
|
+
)
|
|
333
|
+
""")
|
|
334
|
+
|
|
335
|
+
# Copy data
|
|
336
|
+
cursor.execute(f"""
|
|
337
|
+
INSERT INTO {temp_table} ({', '.join(column_order)})
|
|
338
|
+
SELECT {', '.join(column_order)} FROM {table_name}
|
|
339
|
+
""")
|
|
340
|
+
|
|
341
|
+
# Swap tables
|
|
342
|
+
cursor.execute(f"DROP TABLE {table_name}")
|
|
343
|
+
cursor.execute(f"ALTER TABLE {temp_table} RENAME TO {table_name}")
|
|
344
|
+
|
|
345
|
+
self.conn.commit()
|
|
346
|
+
logger.info(f"Reordered columns in {table_name}")
|
|
347
|
+
return True
|
|
348
|
+
|
|
349
|
+
except Exception as e:
|
|
350
|
+
self.conn.rollback()
|
|
351
|
+
logger.error(f"Failed to reorder columns: {e}")
|
|
352
|
+
return False
|
|
353
|
+
|
|
354
|
+
def sort_columns(
|
|
355
|
+
self,
|
|
356
|
+
table_name: str,
|
|
357
|
+
alphabetical: bool = True,
|
|
358
|
+
reverse: bool = False,
|
|
359
|
+
key_columns_first: Optional[List[str]] = None,
|
|
360
|
+
progress_callback: Optional[callable] = None,
|
|
361
|
+
) -> bool:
|
|
362
|
+
"""Sort columns in a table alphabetically or by custom criteria.
|
|
363
|
+
|
|
364
|
+
Similar to organizing DataFrame columns systematically.
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
table_name: Name of the table
|
|
368
|
+
alphabetical: Sort columns alphabetically
|
|
369
|
+
reverse: Reverse sort order
|
|
370
|
+
key_columns_first: List of columns to place first (like 'id', 'created_at')
|
|
371
|
+
progress_callback: Function for progress updates
|
|
372
|
+
|
|
373
|
+
Returns:
|
|
374
|
+
True if successful, False otherwise
|
|
375
|
+
"""
|
|
376
|
+
try:
|
|
377
|
+
# Get current columns
|
|
378
|
+
columns = self.get_column_info(table_name)
|
|
379
|
+
column_names = [col["name"] for col in columns]
|
|
380
|
+
|
|
381
|
+
# Determine new order
|
|
382
|
+
if key_columns_first:
|
|
383
|
+
# Put key columns first, then sort the rest
|
|
384
|
+
key_cols = [c for c in key_columns_first if c in column_names]
|
|
385
|
+
other_cols = [c for c in column_names if c not in key_cols]
|
|
386
|
+
if alphabetical:
|
|
387
|
+
other_cols.sort(reverse=reverse)
|
|
388
|
+
new_order = key_cols + other_cols
|
|
389
|
+
else:
|
|
390
|
+
# Just sort all columns
|
|
391
|
+
new_order = (
|
|
392
|
+
sorted(column_names, reverse=reverse)
|
|
393
|
+
if alphabetical
|
|
394
|
+
else column_names
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
# Use reorder_columns to apply the new order
|
|
398
|
+
return self.reorder_columns(table_name, new_order, progress_callback)
|
|
399
|
+
|
|
400
|
+
except Exception as e:
|
|
401
|
+
logger.error(f"Failed to sort columns: {e}")
|
|
402
|
+
return False
|
|
403
|
+
|
|
404
|
+
# ----------------------------------------
|
|
405
|
+
# ADD Operations
|
|
406
|
+
# ----------------------------------------
|
|
407
|
+
|
|
408
|
+
def add_column(
|
|
409
|
+
self,
|
|
410
|
+
table_name: str,
|
|
411
|
+
column_name: str,
|
|
412
|
+
column_type: str,
|
|
413
|
+
default: Any = None,
|
|
414
|
+
not_null: bool = False,
|
|
415
|
+
) -> bool:
|
|
416
|
+
"""Add a new column to the table.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
table_name: Name of the table
|
|
420
|
+
column_name: Name of new column
|
|
421
|
+
column_type: SQLite type (TEXT, INTEGER, REAL, BLOB)
|
|
422
|
+
default: Default value for the column
|
|
423
|
+
not_null: Whether column should be NOT NULL
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
True if successful, False otherwise
|
|
427
|
+
"""
|
|
428
|
+
try:
|
|
429
|
+
if self.column_exists(table_name, column_name):
|
|
430
|
+
logger.warning(f"Column {column_name} already exists in {table_name}")
|
|
431
|
+
return False
|
|
432
|
+
|
|
433
|
+
cursor = self.cursor
|
|
434
|
+
col_def = f"{column_name} {column_type}"
|
|
435
|
+
|
|
436
|
+
if not_null and default is None:
|
|
437
|
+
logger.error("Cannot add NOT NULL column without default value")
|
|
438
|
+
return False
|
|
439
|
+
|
|
440
|
+
if default is not None:
|
|
441
|
+
if isinstance(default, str):
|
|
442
|
+
col_def += f" DEFAULT '{default}'"
|
|
443
|
+
else:
|
|
444
|
+
col_def += f" DEFAULT {default}"
|
|
445
|
+
|
|
446
|
+
if not_null:
|
|
447
|
+
col_def += " NOT NULL"
|
|
448
|
+
|
|
449
|
+
cursor.execute(f"ALTER TABLE {table_name} ADD COLUMN {col_def}")
|
|
450
|
+
self.conn.commit()
|
|
451
|
+
|
|
452
|
+
logger.info(f"Added column {column_name} to {table_name}")
|
|
453
|
+
return True
|
|
454
|
+
|
|
455
|
+
except Exception as e:
|
|
456
|
+
self.conn.rollback()
|
|
457
|
+
logger.error(f"Failed to add column: {e}")
|
|
458
|
+
return False
|
|
459
|
+
|
|
460
|
+
# ----------------------------------------
|
|
461
|
+
# Batch Operations
|
|
462
|
+
# ----------------------------------------
|
|
463
|
+
|
|
464
|
+
def drop_columns(
|
|
465
|
+
self,
|
|
466
|
+
table_name: str,
|
|
467
|
+
column_names: List[str],
|
|
468
|
+
progress_callback: Optional[callable] = None,
|
|
469
|
+
) -> bool:
|
|
470
|
+
"""Drop multiple columns efficiently in one operation.
|
|
471
|
+
|
|
472
|
+
More efficient than dropping columns one by one as it only
|
|
473
|
+
recreates the table once.
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
table_name: Name of the table
|
|
477
|
+
column_names: List of columns to drop
|
|
478
|
+
progress_callback: Function for progress updates
|
|
479
|
+
|
|
480
|
+
Returns:
|
|
481
|
+
True if successful, False otherwise
|
|
482
|
+
"""
|
|
483
|
+
try:
|
|
484
|
+
# Check all columns exist
|
|
485
|
+
for col_name in column_names:
|
|
486
|
+
if not self.column_exists(table_name, col_name):
|
|
487
|
+
logger.error(f"Column {col_name} does not exist in {table_name}")
|
|
488
|
+
return False
|
|
489
|
+
|
|
490
|
+
# If native DROP is supported and only one column, use it
|
|
491
|
+
if len(column_names) == 1 and self.supports_drop_column():
|
|
492
|
+
return self.drop_column(table_name, column_names[0])
|
|
493
|
+
|
|
494
|
+
# Otherwise recreate table without the columns
|
|
495
|
+
cursor = self.cursor
|
|
496
|
+
cursor.execute("BEGIN EXCLUSIVE TRANSACTION")
|
|
497
|
+
|
|
498
|
+
# Get columns to keep
|
|
499
|
+
columns = self.get_column_info(table_name)
|
|
500
|
+
keep_columns = [
|
|
501
|
+
col for col in columns if col["name"] not in column_names
|
|
502
|
+
]
|
|
503
|
+
|
|
504
|
+
if not keep_columns:
|
|
505
|
+
raise ValueError("Cannot drop all columns from table")
|
|
506
|
+
|
|
507
|
+
# Create column definitions
|
|
508
|
+
column_names_keep = [col["name"] for col in keep_columns]
|
|
509
|
+
column_defs = []
|
|
510
|
+
for col in keep_columns:
|
|
511
|
+
col_def = f"{col['name']} {col['type']}"
|
|
512
|
+
if col["notnull"]:
|
|
513
|
+
col_def += " NOT NULL"
|
|
514
|
+
if col["default"] is not None:
|
|
515
|
+
col_def += f" DEFAULT {col['default']}"
|
|
516
|
+
if col["pk"]:
|
|
517
|
+
col_def += " PRIMARY KEY"
|
|
518
|
+
column_defs.append(col_def)
|
|
519
|
+
|
|
520
|
+
# Create temp table
|
|
521
|
+
temp_table = f"{table_name}_drop_{int(time.time())}"
|
|
522
|
+
cursor.execute(f"""
|
|
523
|
+
CREATE TABLE {temp_table} (
|
|
524
|
+
{', '.join(column_defs)}
|
|
525
|
+
)
|
|
526
|
+
""")
|
|
527
|
+
|
|
528
|
+
# Copy data
|
|
529
|
+
cursor.execute(f"""
|
|
530
|
+
INSERT INTO {temp_table} ({', '.join(column_names_keep)})
|
|
531
|
+
SELECT {', '.join(column_names_keep)} FROM {table_name}
|
|
532
|
+
""")
|
|
533
|
+
|
|
534
|
+
# Swap tables
|
|
535
|
+
cursor.execute(f"DROP TABLE {table_name}")
|
|
536
|
+
cursor.execute(f"ALTER TABLE {temp_table} RENAME TO {table_name}")
|
|
537
|
+
|
|
538
|
+
self.conn.commit()
|
|
539
|
+
logger.info(f"Dropped {len(column_names)} columns from {table_name}")
|
|
540
|
+
return True
|
|
541
|
+
|
|
542
|
+
except Exception as e:
|
|
543
|
+
self.conn.rollback()
|
|
544
|
+
logger.error(f"Failed to drop multiple columns: {e}")
|
|
545
|
+
return False
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
# EOF
|