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,498 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-08 04:22:00 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/browser/interaction/handle_popups.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
__FILE__ = __file__
|
|
9
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
10
|
+
# ----------------------------------------
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
Universal popup handler for browser automation.
|
|
14
|
+
|
|
15
|
+
Detects and closes various types of popups including:
|
|
16
|
+
- Cookie consent banners
|
|
17
|
+
- Newsletter/subscription modals
|
|
18
|
+
- AI assistant promotions
|
|
19
|
+
- Authentication prompts
|
|
20
|
+
- General modal dialogs
|
|
21
|
+
|
|
22
|
+
This is a universal utility that works across any website.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import asyncio
|
|
26
|
+
from typing import Dict, List, Optional, Tuple
|
|
27
|
+
from playwright.async_api import Page, ElementHandle
|
|
28
|
+
|
|
29
|
+
from scitex import logging
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PopupHandler:
|
|
35
|
+
"""Handle various types of popups on web pages."""
|
|
36
|
+
|
|
37
|
+
# Common selectors for different popup types
|
|
38
|
+
COOKIE_SELECTORS = [
|
|
39
|
+
'button#onetrust-accept-btn-handler',
|
|
40
|
+
'button#onetrust-pc-btn-handler',
|
|
41
|
+
'button[id*="accept-cookie"]',
|
|
42
|
+
'button[id*="accept-all"]',
|
|
43
|
+
'button[aria-label*="accept cookie"]',
|
|
44
|
+
'button[aria-label*="Accept cookie"]',
|
|
45
|
+
'button:has-text("Accept all")',
|
|
46
|
+
'button:has-text("Accept All")',
|
|
47
|
+
'button:has-text("I agree")',
|
|
48
|
+
'button:has-text("I Agree")',
|
|
49
|
+
'button:has-text("Accept")',
|
|
50
|
+
'.cookie-notice button.accept',
|
|
51
|
+
'[class*="cookie"] button[class*="accept"]',
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
CLOSE_SELECTORS = [
|
|
55
|
+
'button[aria-label="Close"]',
|
|
56
|
+
'button[aria-label="close"]',
|
|
57
|
+
'button[aria-label*="Close"]',
|
|
58
|
+
'button[aria-label*="close"]',
|
|
59
|
+
'button[aria-label*="dismiss"]',
|
|
60
|
+
'button[aria-label*="Dismiss"]',
|
|
61
|
+
'button.close',
|
|
62
|
+
'button.close-button',
|
|
63
|
+
'button.modal-close',
|
|
64
|
+
'button.popup-close',
|
|
65
|
+
'button.dialog-close',
|
|
66
|
+
'a.close',
|
|
67
|
+
'a.close-button',
|
|
68
|
+
'span.close',
|
|
69
|
+
'[class*="close-button"]',
|
|
70
|
+
'[class*="close-icon"]',
|
|
71
|
+
'svg[class*="close"]',
|
|
72
|
+
'button:has-text("No thanks")',
|
|
73
|
+
'button:has-text("No Thanks")',
|
|
74
|
+
'button:has-text("Maybe later")',
|
|
75
|
+
'button:has-text("Maybe Later")',
|
|
76
|
+
'button:has-text("Skip")',
|
|
77
|
+
'button:has-text("Dismiss")',
|
|
78
|
+
'button:has-text("Not now")',
|
|
79
|
+
'button:has-text("Not Now")',
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
MODAL_SELECTORS = [
|
|
83
|
+
'.modal',
|
|
84
|
+
'.overlay',
|
|
85
|
+
'[role="dialog"]',
|
|
86
|
+
'.popup',
|
|
87
|
+
'#onetrust-banner-sdk',
|
|
88
|
+
'.onetrust-pc-dark-filter',
|
|
89
|
+
'[class*="modal"]',
|
|
90
|
+
'[class*="popup"]',
|
|
91
|
+
'[class*="overlay"]',
|
|
92
|
+
'[class*="dialog"]',
|
|
93
|
+
'[class*="banner"]',
|
|
94
|
+
'div[aria-modal="true"]',
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
def __init__(self, page: Page):
|
|
98
|
+
"""Initialize popup handler with a page."""
|
|
99
|
+
self.page = page
|
|
100
|
+
self.handled_popups = []
|
|
101
|
+
|
|
102
|
+
async def detect_popups(self) -> List[Dict]:
|
|
103
|
+
"""
|
|
104
|
+
Detect all visible popups on the page.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
List of detected popups with their details
|
|
108
|
+
"""
|
|
109
|
+
detected = []
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
# Check for visible modal/popup elements
|
|
113
|
+
popup_info = await self.page.evaluate('''
|
|
114
|
+
() => {
|
|
115
|
+
const modalSelectors = %s;
|
|
116
|
+
const found = [];
|
|
117
|
+
|
|
118
|
+
for (const selector of modalSelectors) {
|
|
119
|
+
try {
|
|
120
|
+
const elements = document.querySelectorAll(selector);
|
|
121
|
+
for (const el of elements) {
|
|
122
|
+
const style = window.getComputedStyle(el);
|
|
123
|
+
const rect = el.getBoundingClientRect();
|
|
124
|
+
|
|
125
|
+
// Check if element is visible
|
|
126
|
+
if (style.display !== 'none' &&
|
|
127
|
+
style.visibility !== 'hidden' &&
|
|
128
|
+
rect.width > 0 &&
|
|
129
|
+
rect.height > 0 &&
|
|
130
|
+
style.opacity !== '0') {
|
|
131
|
+
|
|
132
|
+
// Get text preview
|
|
133
|
+
let text = el.innerText || el.textContent || '';
|
|
134
|
+
text = text.substring(0, 200).trim();
|
|
135
|
+
|
|
136
|
+
// Try to identify popup type
|
|
137
|
+
let type = 'unknown';
|
|
138
|
+
const lowerText = text.toLowerCase();
|
|
139
|
+
if (lowerText.includes('cookie') || lowerText.includes('privacy')) {
|
|
140
|
+
type = 'cookie';
|
|
141
|
+
} else if (lowerText.includes('subscribe') || lowerText.includes('newsletter')) {
|
|
142
|
+
type = 'newsletter';
|
|
143
|
+
} else if (lowerText.includes('sign in') || lowerText.includes('login')) {
|
|
144
|
+
type = 'auth';
|
|
145
|
+
} else if (lowerText.includes('ai') || lowerText.includes('assistant')) {
|
|
146
|
+
type = 'ai_promotion';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
found.push({
|
|
150
|
+
selector: selector,
|
|
151
|
+
type: type,
|
|
152
|
+
text: text,
|
|
153
|
+
zIndex: style.zIndex || '0',
|
|
154
|
+
position: {
|
|
155
|
+
top: rect.top,
|
|
156
|
+
left: rect.left,
|
|
157
|
+
width: rect.width,
|
|
158
|
+
height: rect.height
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch (e) {
|
|
164
|
+
// Ignore selector errors
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Sort by z-index (highest first)
|
|
169
|
+
found.sort((a, b) => {
|
|
170
|
+
const zA = parseInt(a.zIndex) || 0;
|
|
171
|
+
const zB = parseInt(b.zIndex) || 0;
|
|
172
|
+
return zB - zA;
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return found;
|
|
176
|
+
}
|
|
177
|
+
''' % str(self.MODAL_SELECTORS))
|
|
178
|
+
|
|
179
|
+
detected = popup_info
|
|
180
|
+
|
|
181
|
+
if detected:
|
|
182
|
+
logger.debug(f"Detected {len(detected)} popup(s)")
|
|
183
|
+
for popup in detected[:3]: # Log first 3
|
|
184
|
+
logger.debug(f" - Type: {popup['type']}, Text preview: {popup['text'][:50]}...")
|
|
185
|
+
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.debug(f"Error detecting popups: {e}")
|
|
188
|
+
|
|
189
|
+
return detected
|
|
190
|
+
|
|
191
|
+
async def handle_cookie_popup(self) -> bool:
|
|
192
|
+
"""
|
|
193
|
+
Handle cookie consent popups.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
True if handled, False otherwise
|
|
197
|
+
"""
|
|
198
|
+
for selector in self.COOKIE_SELECTORS:
|
|
199
|
+
try:
|
|
200
|
+
button = await self.page.query_selector(selector)
|
|
201
|
+
if button and await button.is_visible():
|
|
202
|
+
# IMPORTANT: Skip SciTeX manual control buttons
|
|
203
|
+
is_scitex_control = await button.get_attribute('data-scitex-no-auto-click')
|
|
204
|
+
if is_scitex_control:
|
|
205
|
+
logger.debug(f"Skipping SciTeX control button: {selector}")
|
|
206
|
+
continue
|
|
207
|
+
|
|
208
|
+
await button.click()
|
|
209
|
+
logger.success(f"Accepted cookies with selector: {selector}")
|
|
210
|
+
await self.page.wait_for_timeout(1000)
|
|
211
|
+
self.handled_popups.append(('cookie', selector))
|
|
212
|
+
return True
|
|
213
|
+
except Exception as e:
|
|
214
|
+
logger.debug(f"Cookie selector {selector} failed: {e}")
|
|
215
|
+
continue
|
|
216
|
+
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
async def close_popup(self, popup_info: Optional[Dict] = None) -> bool:
|
|
220
|
+
"""
|
|
221
|
+
Close a popup using various strategies.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
popup_info: Optional popup information from detect_popups
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
True if closed, False otherwise
|
|
228
|
+
"""
|
|
229
|
+
# Try close buttons
|
|
230
|
+
for selector in self.CLOSE_SELECTORS:
|
|
231
|
+
try:
|
|
232
|
+
button = await self.page.query_selector(selector)
|
|
233
|
+
if button and await button.is_visible():
|
|
234
|
+
# IMPORTANT: Skip SciTeX manual control buttons
|
|
235
|
+
is_scitex_control = await button.get_attribute('data-scitex-no-auto-click')
|
|
236
|
+
if is_scitex_control:
|
|
237
|
+
logger.debug(f"Skipping SciTeX control button: {selector}")
|
|
238
|
+
continue
|
|
239
|
+
|
|
240
|
+
await button.click()
|
|
241
|
+
logger.success(f"Closed popup with selector: {selector}")
|
|
242
|
+
await self.page.wait_for_timeout(500)
|
|
243
|
+
self.handled_popups.append(('close', selector))
|
|
244
|
+
return True
|
|
245
|
+
except Exception:
|
|
246
|
+
continue
|
|
247
|
+
|
|
248
|
+
# Try ESC key as fallback
|
|
249
|
+
try:
|
|
250
|
+
await self.page.keyboard.press('Escape')
|
|
251
|
+
await self.page.wait_for_timeout(500)
|
|
252
|
+
|
|
253
|
+
# Check if popup is gone
|
|
254
|
+
if popup_info:
|
|
255
|
+
still_visible = await self.page.evaluate(
|
|
256
|
+
f'!!document.querySelector("{popup_info["selector"]}")'
|
|
257
|
+
)
|
|
258
|
+
if not still_visible:
|
|
259
|
+
logger.success("Closed popup with ESC key")
|
|
260
|
+
self.handled_popups.append(('escape', popup_info['selector']))
|
|
261
|
+
return True
|
|
262
|
+
except Exception as e:
|
|
263
|
+
logger.debug(f"ESC key failed: {e}")
|
|
264
|
+
|
|
265
|
+
return False
|
|
266
|
+
|
|
267
|
+
async def handle_all_popups(self, max_attempts: int = 3, delay_ms: int = 1000) -> int:
|
|
268
|
+
"""
|
|
269
|
+
Detect and handle all popups on the page.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
max_attempts: Maximum number of attempts to clear popups
|
|
273
|
+
delay_ms: Delay between attempts in milliseconds
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Number of popups handled
|
|
277
|
+
"""
|
|
278
|
+
total_handled = 0
|
|
279
|
+
|
|
280
|
+
for attempt in range(max_attempts):
|
|
281
|
+
# Detect popups
|
|
282
|
+
popups = await self.detect_popups()
|
|
283
|
+
|
|
284
|
+
if not popups:
|
|
285
|
+
if attempt == 0:
|
|
286
|
+
logger.debug("No popups detected")
|
|
287
|
+
break
|
|
288
|
+
|
|
289
|
+
logger.debug(f"Attempt {attempt + 1}: Found {len(popups)} popup(s)")
|
|
290
|
+
|
|
291
|
+
# Handle each popup
|
|
292
|
+
for popup in popups:
|
|
293
|
+
handled = False
|
|
294
|
+
|
|
295
|
+
# Try cookie handling first if it's a cookie popup
|
|
296
|
+
if popup['type'] == 'cookie':
|
|
297
|
+
handled = await self.handle_cookie_popup()
|
|
298
|
+
|
|
299
|
+
# Otherwise try to close it
|
|
300
|
+
if not handled:
|
|
301
|
+
handled = await self.close_popup(popup)
|
|
302
|
+
|
|
303
|
+
if handled:
|
|
304
|
+
total_handled += 1
|
|
305
|
+
await self.page.wait_for_timeout(delay_ms)
|
|
306
|
+
else:
|
|
307
|
+
logger.warning(f"Could not handle popup: {popup['type']}")
|
|
308
|
+
|
|
309
|
+
# Small delay before next detection
|
|
310
|
+
await self.page.wait_for_timeout(500)
|
|
311
|
+
|
|
312
|
+
if total_handled > 0:
|
|
313
|
+
logger.success(f"Successfully handled {total_handled} popup(s)")
|
|
314
|
+
|
|
315
|
+
return total_handled
|
|
316
|
+
|
|
317
|
+
async def wait_and_handle_popups(self, timeout_ms: int = 5000) -> int:
|
|
318
|
+
"""
|
|
319
|
+
Wait for popups to appear and handle them.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
timeout_ms: Maximum time to wait for popups
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Number of popups handled
|
|
326
|
+
"""
|
|
327
|
+
start_time = asyncio.get_event_loop().time()
|
|
328
|
+
total_handled = 0
|
|
329
|
+
|
|
330
|
+
while (asyncio.get_event_loop().time() - start_time) * 1000 < timeout_ms:
|
|
331
|
+
popups = await self.detect_popups()
|
|
332
|
+
|
|
333
|
+
if popups:
|
|
334
|
+
for popup in popups:
|
|
335
|
+
if popup['type'] == 'cookie':
|
|
336
|
+
if await self.handle_cookie_popup():
|
|
337
|
+
total_handled += 1
|
|
338
|
+
elif await self.close_popup(popup):
|
|
339
|
+
total_handled += 1
|
|
340
|
+
|
|
341
|
+
if total_handled > 0:
|
|
342
|
+
break
|
|
343
|
+
|
|
344
|
+
await self.page.wait_for_timeout(500)
|
|
345
|
+
|
|
346
|
+
return total_handled
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
async def close_popups_async(
|
|
350
|
+
page: Page,
|
|
351
|
+
handle_cookies: bool = True,
|
|
352
|
+
close_others: bool = True,
|
|
353
|
+
max_attempts: int = 3,
|
|
354
|
+
wait_first: bool = True,
|
|
355
|
+
wait_ms: int = 2000
|
|
356
|
+
) -> Tuple[int, List]:
|
|
357
|
+
"""
|
|
358
|
+
Convenience function to handle all popups on a page.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
page: Playwright page object
|
|
362
|
+
handle_cookies: Whether to accept cookie popups
|
|
363
|
+
close_others: Whether to close other popups
|
|
364
|
+
max_attempts: Maximum attempts to clear popups
|
|
365
|
+
wait_first: Whether to wait for popups to appear first
|
|
366
|
+
wait_ms: Time to wait for popups to appear
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
Tuple of (number handled, list of handled popups)
|
|
370
|
+
"""
|
|
371
|
+
handler = PopupHandler(page)
|
|
372
|
+
|
|
373
|
+
# Wait for popups to appear if requested
|
|
374
|
+
if wait_first:
|
|
375
|
+
await page.wait_for_timeout(wait_ms)
|
|
376
|
+
|
|
377
|
+
# Handle all popups
|
|
378
|
+
total = await handler.handle_all_popups(max_attempts=max_attempts)
|
|
379
|
+
|
|
380
|
+
return total, handler.handled_popups
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
async def ensure_no_popups_async(page: Page, check_interval_ms: int = 1000) -> bool:
|
|
384
|
+
"""
|
|
385
|
+
Ensure no popups are blocking the page.
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
page: Playwright page object
|
|
389
|
+
check_interval_ms: Interval to check for popups
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
True if page is clear of popups
|
|
393
|
+
"""
|
|
394
|
+
handler = PopupHandler(page)
|
|
395
|
+
|
|
396
|
+
# Check multiple times
|
|
397
|
+
for _ in range(3):
|
|
398
|
+
popups = await handler.detect_popups()
|
|
399
|
+
if popups:
|
|
400
|
+
await handler.handle_all_popups(max_attempts=1)
|
|
401
|
+
await page.wait_for_timeout(check_interval_ms)
|
|
402
|
+
else:
|
|
403
|
+
return True
|
|
404
|
+
|
|
405
|
+
# Final check
|
|
406
|
+
final_popups = await handler.detect_popups()
|
|
407
|
+
return len(final_popups) == 0
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def main(args):
|
|
411
|
+
"""Demonstrate PopupHandler functionality."""
|
|
412
|
+
import asyncio
|
|
413
|
+
from playwright.async_api import async_playwright
|
|
414
|
+
|
|
415
|
+
async def demo():
|
|
416
|
+
async with async_playwright() as p:
|
|
417
|
+
browser = await p.chromium.launch(headless=False)
|
|
418
|
+
page = await browser.new_page()
|
|
419
|
+
|
|
420
|
+
logger.debug("PopupHandler: Starting demo")
|
|
421
|
+
|
|
422
|
+
# Navigate to a page with popups
|
|
423
|
+
await page.goto("https://www.springer.com", timeout=30000)
|
|
424
|
+
await asyncio.sleep(2)
|
|
425
|
+
|
|
426
|
+
# Demonstrate popup handling
|
|
427
|
+
handler = PopupHandler(page)
|
|
428
|
+
|
|
429
|
+
logger.debug("Detecting popups...")
|
|
430
|
+
popups = await handler.detect_popups()
|
|
431
|
+
logger.debug(f"Found {len(popups)} popup(s)")
|
|
432
|
+
|
|
433
|
+
logger.debug("Handling popups...")
|
|
434
|
+
handled = await handler.handle_all_popups()
|
|
435
|
+
logger.success(f"Successfully handled {handled} popup(s)")
|
|
436
|
+
|
|
437
|
+
# Verify page is clear
|
|
438
|
+
is_clear = await ensure_no_popups_async(page)
|
|
439
|
+
if is_clear:
|
|
440
|
+
logger.success("Page is clear of popups")
|
|
441
|
+
else:
|
|
442
|
+
logger.warning("Some popups may still be present")
|
|
443
|
+
|
|
444
|
+
logger.success("PopupHandler demonstration complete")
|
|
445
|
+
|
|
446
|
+
await asyncio.sleep(2)
|
|
447
|
+
await browser.close()
|
|
448
|
+
|
|
449
|
+
asyncio.run(demo())
|
|
450
|
+
return 0
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def parse_args():
|
|
454
|
+
"""Parse command line arguments."""
|
|
455
|
+
import argparse
|
|
456
|
+
parser = argparse.ArgumentParser(description="Popup handler demo")
|
|
457
|
+
return parser.parse_args()
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def run_main() -> None:
|
|
461
|
+
"""Initialize scitex framework, run main function, and cleanup."""
|
|
462
|
+
global CONFIG, CC, sys, plt, rng
|
|
463
|
+
|
|
464
|
+
import sys
|
|
465
|
+
|
|
466
|
+
import matplotlib.pyplot as plt
|
|
467
|
+
|
|
468
|
+
import scitex as stx
|
|
469
|
+
|
|
470
|
+
args = parse_args()
|
|
471
|
+
|
|
472
|
+
CONFIG, sys.stdout, sys.stderr, plt, CC, rng = stx.session.start(
|
|
473
|
+
sys,
|
|
474
|
+
plt,
|
|
475
|
+
args=args,
|
|
476
|
+
file=__FILE__,
|
|
477
|
+
sdir_suffix=None,
|
|
478
|
+
verbose=False,
|
|
479
|
+
agg=True,
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
exit_status = main(args)
|
|
483
|
+
|
|
484
|
+
stx.session.close(
|
|
485
|
+
CONFIG,
|
|
486
|
+
verbose=False,
|
|
487
|
+
notify=False,
|
|
488
|
+
message="",
|
|
489
|
+
exit_status=exit_status,
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
if __name__ == "__main__":
|
|
494
|
+
run_main()
|
|
495
|
+
|
|
496
|
+
# python -m scitex.browser.interaction.close_popups
|
|
497
|
+
|
|
498
|
+
# EOF
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-08 04:13:32 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/browser/interaction/fill_with_fallbacks.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
__FILE__ = (
|
|
9
|
+
"./src/scitex/browser/interaction/fill_with_fallbacks.py"
|
|
10
|
+
)
|
|
11
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
12
|
+
# ----------------------------------------
|
|
13
|
+
|
|
14
|
+
__FILE__ = __file__
|
|
15
|
+
|
|
16
|
+
from playwright.async_api import Page
|
|
17
|
+
|
|
18
|
+
from scitex import logging
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# 1. Main entry point
|
|
24
|
+
# ---------------------------------------
|
|
25
|
+
async def fill_with_fallbacks_async(
|
|
26
|
+
page: Page, selector: str, value: str, method: str = "auto", verbose: bool = False
|
|
27
|
+
) -> bool:
|
|
28
|
+
"""Fill element using multiple fallback methods.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
page: Playwright page object
|
|
32
|
+
selector: CSS selector for the element
|
|
33
|
+
value: Value to fill
|
|
34
|
+
method: Fill method ("auto", "playwright", "type", "js")
|
|
35
|
+
verbose: Enable visual feedback via popup system (default False)
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
bool: True if fill successful, False otherwise
|
|
39
|
+
"""
|
|
40
|
+
from ..debugging import browser_logger
|
|
41
|
+
|
|
42
|
+
if method == "auto":
|
|
43
|
+
methods_order = ["playwright", "type", "js"]
|
|
44
|
+
else:
|
|
45
|
+
methods_order = [method]
|
|
46
|
+
|
|
47
|
+
methods = {
|
|
48
|
+
"playwright": _fill_with_playwright,
|
|
49
|
+
"type": _fill_with_typing,
|
|
50
|
+
"js": _fill_with_js,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if verbose:
|
|
54
|
+
await browser_logger.debug(
|
|
55
|
+
page, f"Attempting fill: {selector}", verbose=verbose
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
for method_name in methods_order:
|
|
59
|
+
if method_name in methods:
|
|
60
|
+
success = await methods[method_name](page, selector, value)
|
|
61
|
+
if success:
|
|
62
|
+
logger.debug(f"Fill successful with {method_name}: {selector}")
|
|
63
|
+
if verbose:
|
|
64
|
+
await browser_logger.debug(
|
|
65
|
+
page, f"✓ Fill successful ({method_name}): {selector}", verbose=verbose
|
|
66
|
+
)
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
logger.error(f"All fill methods failed for {selector}")
|
|
70
|
+
if verbose:
|
|
71
|
+
await browser_logger.debug(
|
|
72
|
+
page, f"✗ All fill methods failed: {selector}", verbose=verbose
|
|
73
|
+
)
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# 2. Helper functions
|
|
78
|
+
# ---------------------------------------
|
|
79
|
+
async def _fill_with_playwright(page: Page, selector: str, value: str) -> bool:
|
|
80
|
+
try:
|
|
81
|
+
await page.fill(selector, value, timeout=5000)
|
|
82
|
+
return True
|
|
83
|
+
except Exception:
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def _fill_with_typing(page: Page, selector: str, value: str) -> bool:
|
|
88
|
+
try:
|
|
89
|
+
await page.click(selector, timeout=5000)
|
|
90
|
+
await page.keyboard.press("Control+a")
|
|
91
|
+
await page.type(selector, value, delay=50)
|
|
92
|
+
return True
|
|
93
|
+
except Exception:
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
async def _fill_with_js(page: Page, selector: str, value: str) -> bool:
|
|
98
|
+
try:
|
|
99
|
+
result = await page.evaluate(
|
|
100
|
+
"""(selector, value) => {
|
|
101
|
+
const element = document.querySelector(selector);
|
|
102
|
+
if (element) {
|
|
103
|
+
element.value = value;
|
|
104
|
+
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
105
|
+
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
106
|
+
return 'success';
|
|
107
|
+
}
|
|
108
|
+
return 'element not found';
|
|
109
|
+
}""",
|
|
110
|
+
selector,
|
|
111
|
+
value,
|
|
112
|
+
)
|
|
113
|
+
return result == "success"
|
|
114
|
+
except Exception:
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def main(args):
|
|
119
|
+
"""Demonstrate fill_with_fallbacks functionality."""
|
|
120
|
+
import asyncio
|
|
121
|
+
from playwright.async_api import async_playwright
|
|
122
|
+
from ..debugging import browser_logger
|
|
123
|
+
|
|
124
|
+
async def demo():
|
|
125
|
+
async with async_playwright() as p:
|
|
126
|
+
browser = await p.chromium.launch(headless=False)
|
|
127
|
+
page = await browser.new_page()
|
|
128
|
+
|
|
129
|
+
await browser_logger.debug(
|
|
130
|
+
page, "Fill with Fallbacks: Starting demo", verbose=True
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Navigate to a page with input fields
|
|
134
|
+
await page.goto("https://www.google.com", timeout=30000)
|
|
135
|
+
|
|
136
|
+
await browser_logger.debug(
|
|
137
|
+
page, "Testing fill with fallbacks...", verbose=True
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Try to fill the search box
|
|
141
|
+
success = await fill_with_fallbacks_async(
|
|
142
|
+
page,
|
|
143
|
+
"textarea[name='q']", # Google search box
|
|
144
|
+
"SciTeX browser automation",
|
|
145
|
+
verbose=True
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
if success:
|
|
149
|
+
logger.success("Fill demonstration completed successfully")
|
|
150
|
+
else:
|
|
151
|
+
logger.warning("Fill demonstration: no input element found")
|
|
152
|
+
|
|
153
|
+
await browser_logger.debug(
|
|
154
|
+
page, "✓ Demo complete", verbose=True
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
await asyncio.sleep(2)
|
|
158
|
+
await browser.close()
|
|
159
|
+
|
|
160
|
+
asyncio.run(demo())
|
|
161
|
+
return 0
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def parse_args():
|
|
165
|
+
"""Parse command line arguments."""
|
|
166
|
+
import argparse
|
|
167
|
+
parser = argparse.ArgumentParser(description="Fill with fallbacks demo")
|
|
168
|
+
return parser.parse_args()
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def run_main() -> None:
|
|
172
|
+
"""Initialize scitex framework, run main function, and cleanup."""
|
|
173
|
+
global CONFIG, CC, sys, plt, rng
|
|
174
|
+
|
|
175
|
+
import sys
|
|
176
|
+
|
|
177
|
+
import matplotlib.pyplot as plt
|
|
178
|
+
|
|
179
|
+
import scitex as stx
|
|
180
|
+
|
|
181
|
+
args = parse_args()
|
|
182
|
+
|
|
183
|
+
CONFIG, sys.stdout, sys.stderr, plt, CC, rng = stx.session.start(
|
|
184
|
+
sys,
|
|
185
|
+
plt,
|
|
186
|
+
args=args,
|
|
187
|
+
file=__FILE__,
|
|
188
|
+
sdir_suffix=None,
|
|
189
|
+
verbose=False,
|
|
190
|
+
agg=True,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
exit_status = main(args)
|
|
194
|
+
|
|
195
|
+
stx.session.close(
|
|
196
|
+
CONFIG,
|
|
197
|
+
verbose=False,
|
|
198
|
+
notify=False,
|
|
199
|
+
message="",
|
|
200
|
+
exit_status=exit_status,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if __name__ == "__main__":
|
|
205
|
+
run_main()
|
|
206
|
+
|
|
207
|
+
# python -m scitex.browser.interaction.fill_with_fallbacks
|
|
208
|
+
|
|
209
|
+
# EOF
|