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,216 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-11 00:41:46 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/browser/automation/CookieHandler.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
__FILE__ = (
|
|
9
|
+
"./src/scitex/browser/automation/CookieHandler.py"
|
|
10
|
+
)
|
|
11
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
12
|
+
# ----------------------------------------
|
|
13
|
+
|
|
14
|
+
__FILE__ = __file__
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
|
|
18
|
+
from playwright.async_api import Page
|
|
19
|
+
|
|
20
|
+
from scitex import logging
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CookieAutoAcceptor:
|
|
26
|
+
"""Automatically handles cookie consent banners on web pages."""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self.name = self.__class__.__name__
|
|
30
|
+
self.cookie_texts = [
|
|
31
|
+
"Accept all cookies",
|
|
32
|
+
"Accept All",
|
|
33
|
+
"Accept cookies",
|
|
34
|
+
"Accept",
|
|
35
|
+
"I Accept",
|
|
36
|
+
"OK",
|
|
37
|
+
"Continue",
|
|
38
|
+
"Agree",
|
|
39
|
+
"Continue without an account",
|
|
40
|
+
"Don't ask again",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
self.selectors = [
|
|
44
|
+
"[data-testid*='accept']",
|
|
45
|
+
"[id*='accept']",
|
|
46
|
+
"[class*='accept']",
|
|
47
|
+
"button[aria-label*='Accept']",
|
|
48
|
+
".cookie-banner button:first-of-type",
|
|
49
|
+
"#cookie-banner button:first-of-type",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
async def inject_auto_acceptor_async(self, context):
|
|
53
|
+
"""Inject auto-acceptor script into browser context."""
|
|
54
|
+
logger.warning(f"{self.name}: Use get_auto_acceptor_script instead")
|
|
55
|
+
script = self.get_auto_acceptor_script()
|
|
56
|
+
await context.add_init_script(script)
|
|
57
|
+
logger.debug(f"{self.name}: Injected auto-acceptor script")
|
|
58
|
+
|
|
59
|
+
def get_auto_acceptor_script(
|
|
60
|
+
self,
|
|
61
|
+
):
|
|
62
|
+
return f"""
|
|
63
|
+
(() => {{
|
|
64
|
+
const cookieTexts = {json.dumps(self.cookie_texts)};
|
|
65
|
+
const selectors = {json.dumps(self.selectors)};
|
|
66
|
+
|
|
67
|
+
function acceptCookies() {{
|
|
68
|
+
// Try text-based buttons
|
|
69
|
+
for (const text of cookieTexts) {{
|
|
70
|
+
const buttons = Array.from(document.querySelectorAll('button, a'));
|
|
71
|
+
const match = buttons.find(btn =>
|
|
72
|
+
btn.textContent.trim().toLowerCase() === text.toLowerCase() &&
|
|
73
|
+
!btn.hasAttribute('data-scitex-no-auto-click') && // SKIP SciTeX buttons!
|
|
74
|
+
!btn.closest('[data-scitex-no-auto-click]') && // SKIP if inside SciTeX container!
|
|
75
|
+
btn.id !== 'stop-automation-btn' && // SKIP manual download button by ID!
|
|
76
|
+
!btn.id.includes('scitex') // SKIP any scitex-related button
|
|
77
|
+
);
|
|
78
|
+
if (match && match.offsetParent !== null) {{
|
|
79
|
+
match.click();
|
|
80
|
+
console.log('Auto-accepted cookies:', text);
|
|
81
|
+
return true;
|
|
82
|
+
}}
|
|
83
|
+
}}
|
|
84
|
+
|
|
85
|
+
// Try CSS selectors
|
|
86
|
+
for (const selector of selectors) {{
|
|
87
|
+
try {{
|
|
88
|
+
const elements = document.querySelectorAll(selector);
|
|
89
|
+
for (const elem of elements) {{
|
|
90
|
+
// SKIP SciTeX buttons!
|
|
91
|
+
if (elem.hasAttribute('data-scitex-no-auto-click') ||
|
|
92
|
+
elem.closest('[data-scitex-no-auto-click]') ||
|
|
93
|
+
elem.id === 'stop-automation-btn' || // SKIP by ID!
|
|
94
|
+
elem.id.includes('scitex')) {{ // SKIP any scitex ID
|
|
95
|
+
continue;
|
|
96
|
+
}}
|
|
97
|
+
|
|
98
|
+
if (elem.offsetParent !== null) {{
|
|
99
|
+
elem.click();
|
|
100
|
+
console.log('Auto-accepted cookies:', selector);
|
|
101
|
+
return true;
|
|
102
|
+
}}
|
|
103
|
+
}}
|
|
104
|
+
}} catch (e) {{}}
|
|
105
|
+
}}
|
|
106
|
+
return false;
|
|
107
|
+
}}
|
|
108
|
+
|
|
109
|
+
// Check periodically
|
|
110
|
+
const interval = setInterval(() => {{
|
|
111
|
+
if (acceptCookies()) {{
|
|
112
|
+
clearInterval(interval);
|
|
113
|
+
}}
|
|
114
|
+
}}, 1000);
|
|
115
|
+
|
|
116
|
+
// Stop after 30 seconds
|
|
117
|
+
setTimeout(() => clearInterval(interval), 30000);
|
|
118
|
+
}})();
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
async def check_cookie_banner_exists_async(self, page: Page) -> bool:
|
|
122
|
+
"""Check if a cookie banner is still visible."""
|
|
123
|
+
try:
|
|
124
|
+
exists = await page.locator(
|
|
125
|
+
".cookie-banner, [class*='cookie']"
|
|
126
|
+
).first.is_visible()
|
|
127
|
+
logger.debug(f"{self.name}: Cookie banner exists: {exists}")
|
|
128
|
+
return exists
|
|
129
|
+
except:
|
|
130
|
+
logger.debug(f"{self.name}: Cookie banner not found")
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def main(args):
|
|
135
|
+
"""Demonstrate CookieAutoAcceptor functionality."""
|
|
136
|
+
import asyncio
|
|
137
|
+
|
|
138
|
+
from playwright.async_api import async_playwright
|
|
139
|
+
|
|
140
|
+
from ..debugging import browser_logger
|
|
141
|
+
|
|
142
|
+
async def demo():
|
|
143
|
+
acceptor = CookieAutoAcceptor()
|
|
144
|
+
|
|
145
|
+
async with async_playwright() as p:
|
|
146
|
+
logger.info("Starting demo")
|
|
147
|
+
browser = await p.chromium.launch(headless=False)
|
|
148
|
+
context = await browser.new_context()
|
|
149
|
+
|
|
150
|
+
await context.add_init_script(acceptor.get_auto_acceptor_script())
|
|
151
|
+
|
|
152
|
+
page = await context.new_page()
|
|
153
|
+
|
|
154
|
+
logger.info("Navigating to https://www.springer.com")
|
|
155
|
+
await page.goto("https://www.springer.com", timeout=30000)
|
|
156
|
+
|
|
157
|
+
logger.debug("Waiting for cookie banner detection")
|
|
158
|
+
await asyncio.sleep(5)
|
|
159
|
+
|
|
160
|
+
banner_exists = await acceptor.check_cookie_banner_exists_async(page)
|
|
161
|
+
|
|
162
|
+
logger.success("Auto-acceptor demo complete")
|
|
163
|
+
|
|
164
|
+
await browser.close()
|
|
165
|
+
|
|
166
|
+
asyncio.run(demo())
|
|
167
|
+
return 0
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def parse_args():
|
|
171
|
+
"""Parse command line arguments."""
|
|
172
|
+
import argparse
|
|
173
|
+
|
|
174
|
+
parser = argparse.ArgumentParser(description="CookieAutoAcceptor demo")
|
|
175
|
+
return parser.parse_args()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def run_main() -> None:
|
|
179
|
+
"""Initialize scitex framework, run main function, and cleanup."""
|
|
180
|
+
global CONFIG, CC, sys, plt, rng
|
|
181
|
+
|
|
182
|
+
import sys
|
|
183
|
+
|
|
184
|
+
import matplotlib.pyplot as plt
|
|
185
|
+
|
|
186
|
+
import scitex as stx
|
|
187
|
+
|
|
188
|
+
args = parse_args()
|
|
189
|
+
|
|
190
|
+
CONFIG, sys.stdout, sys.stderr, plt, CC, rng = stx.session.start(
|
|
191
|
+
sys,
|
|
192
|
+
plt,
|
|
193
|
+
args=args,
|
|
194
|
+
file=__FILE__,
|
|
195
|
+
sdir_suffix=None,
|
|
196
|
+
verbose=False,
|
|
197
|
+
agg=True,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
exit_status = main(args)
|
|
201
|
+
|
|
202
|
+
stx.session.close(
|
|
203
|
+
CONFIG,
|
|
204
|
+
verbose=False,
|
|
205
|
+
notify=False,
|
|
206
|
+
message="",
|
|
207
|
+
exit_status=exit_status,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
if __name__ == "__main__":
|
|
212
|
+
run_main()
|
|
213
|
+
|
|
214
|
+
# python -m scitex.browser.automation.CookieHandler
|
|
215
|
+
|
|
216
|
+
# EOF
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-19 05:20:00 (ywatanabe)"
|
|
4
|
+
# File: ./src/scitex/browser/collaboration/__init__.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
"""
|
|
7
|
+
scitex.browser.collaboration - Interactive browser automation for AI-human teams.
|
|
8
|
+
|
|
9
|
+
This is a NEW module that does NOT affect existing scitex.browser functionality.
|
|
10
|
+
|
|
11
|
+
Features:
|
|
12
|
+
- Persistent shared browser sessions
|
|
13
|
+
- Multi-agent coordination
|
|
14
|
+
- AI-human collaboration
|
|
15
|
+
- Authentication handling
|
|
16
|
+
- Content extraction
|
|
17
|
+
|
|
18
|
+
Version: 0.1.0-alpha (experimental)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
__version__ = "0.1.0-alpha"
|
|
22
|
+
__experimental__ = True
|
|
23
|
+
|
|
24
|
+
# Import components
|
|
25
|
+
from .shared_session import SharedBrowserSession, SessionConfig
|
|
26
|
+
from .visual_feedback import VisualFeedback
|
|
27
|
+
from .credential_manager import CredentialManager
|
|
28
|
+
|
|
29
|
+
# Exports
|
|
30
|
+
__all__ = [
|
|
31
|
+
"SharedBrowserSession",
|
|
32
|
+
"SessionConfig",
|
|
33
|
+
"VisualFeedback",
|
|
34
|
+
"CredentialManager",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
# Compatibility check - ensure we don't break existing code
|
|
38
|
+
def _check_compatibility():
|
|
39
|
+
"""Verify existing scitex.browser still works."""
|
|
40
|
+
try:
|
|
41
|
+
from scitex.browser import browser_logger
|
|
42
|
+
from scitex.browser.automation import CookieAutoAcceptor
|
|
43
|
+
from scitex.browser.interaction import click_center_async
|
|
44
|
+
return True
|
|
45
|
+
except ImportError as e:
|
|
46
|
+
raise RuntimeError(
|
|
47
|
+
f"❌ Collaboration module broke existing imports: {e}\n"
|
|
48
|
+
"This should never happen! Please report this bug."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
_check_compatibility()
|
|
52
|
+
|
|
53
|
+
print("✅ scitex.browser.collaboration loaded (experimental)")
|
|
54
|
+
|
|
55
|
+
# EOF
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-19 05:40:00 (ywatanabe)"
|
|
4
|
+
# File: ./src/scitex/browser/collaboration/auth_helpers.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
"""
|
|
7
|
+
Simple authentication helpers for SharedBrowserSession.
|
|
8
|
+
|
|
9
|
+
Start small - just Django login for now.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
from typing import Optional
|
|
14
|
+
from playwright.async_api import Page
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DjangoAuthHelper:
|
|
18
|
+
"""
|
|
19
|
+
Simple Django authentication helper.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
auth = DjangoAuthHelper(
|
|
23
|
+
login_url="http://127.0.0.1:8000/auth/login/",
|
|
24
|
+
username="your_user",
|
|
25
|
+
password="your_pass",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
success = await auth.login(page)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
login_url: str,
|
|
34
|
+
username: Optional[str] = None,
|
|
35
|
+
password: Optional[str] = None,
|
|
36
|
+
username_field: str = "#id_username",
|
|
37
|
+
password_field: str = "#id_password",
|
|
38
|
+
submit_button: str = "button[type='submit']",
|
|
39
|
+
success_indicator: str = "/core/", # URL contains this after login
|
|
40
|
+
):
|
|
41
|
+
self.login_url = login_url
|
|
42
|
+
self.username = username or os.getenv("SCITEX_CLOUD_USERNAME", "")
|
|
43
|
+
self.password = password or os.getenv("SCITEX_CLOUD_PASSWORD", "")
|
|
44
|
+
self.username_field = username_field
|
|
45
|
+
self.password_field = password_field
|
|
46
|
+
self.submit_button = submit_button
|
|
47
|
+
self.success_indicator = success_indicator
|
|
48
|
+
|
|
49
|
+
async def login(self, page: Page) -> bool:
|
|
50
|
+
"""
|
|
51
|
+
Perform Django login.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
True if successful, False otherwise
|
|
55
|
+
"""
|
|
56
|
+
try:
|
|
57
|
+
# Navigate to login page
|
|
58
|
+
await page.goto(self.login_url, wait_until="load")
|
|
59
|
+
|
|
60
|
+
# Fill username
|
|
61
|
+
await page.fill(self.username_field, self.username)
|
|
62
|
+
|
|
63
|
+
# Fill password
|
|
64
|
+
await page.fill(self.password_field, self.password)
|
|
65
|
+
|
|
66
|
+
# Submit
|
|
67
|
+
await page.click(self.submit_button)
|
|
68
|
+
|
|
69
|
+
# Wait for redirect (success indicator in URL)
|
|
70
|
+
await page.wait_for_url(f"**{self.success_indicator}**", timeout=5000)
|
|
71
|
+
|
|
72
|
+
print(f"✅ Logged in as: {self.username}")
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
print(f"❌ Login failed: {e}")
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
async def is_logged_in(self, page: Page) -> bool:
|
|
80
|
+
"""
|
|
81
|
+
Check if currently logged in.
|
|
82
|
+
|
|
83
|
+
Simple check: not on login page.
|
|
84
|
+
"""
|
|
85
|
+
current_url = page.url
|
|
86
|
+
is_logged_in = "login" not in current_url.lower()
|
|
87
|
+
return is_logged_in
|
|
88
|
+
|
|
89
|
+
async def logout(self, page: Page, logout_url: str = "http://127.0.0.1:8000/auth/logout/"):
|
|
90
|
+
"""Logout (navigate to logout URL)."""
|
|
91
|
+
await page.goto(logout_url)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# EOF
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-19 06:10:00 (ywatanabe)"
|
|
4
|
+
# File: ./src/scitex/browser/collaboration/collaborative_agent.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
"""
|
|
7
|
+
Collaborative Agent - Watches browser and responds to user actions.
|
|
8
|
+
|
|
9
|
+
Runs continuously, responds to:
|
|
10
|
+
- User clicks panel buttons
|
|
11
|
+
- User fills panel fields
|
|
12
|
+
- User navigates
|
|
13
|
+
- DOM changes
|
|
14
|
+
|
|
15
|
+
This is the "watching agent" pattern for true collaboration!
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import signal
|
|
20
|
+
from typing import Optional, Callable, Dict
|
|
21
|
+
from playwright.async_api import Page
|
|
22
|
+
|
|
23
|
+
from scitex.browser.debugging import browser_logger
|
|
24
|
+
from .interactive_panel import InteractivePanel
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CollaborativeAgent:
|
|
28
|
+
"""
|
|
29
|
+
Agent that watches browser and responds to user.
|
|
30
|
+
|
|
31
|
+
Runs continuously, reacts to user input in real-time.
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
agent = CollaborativeAgent(page)
|
|
35
|
+
|
|
36
|
+
# Define what to do when user provides email
|
|
37
|
+
@agent.on_input("email")
|
|
38
|
+
async def handle_email(email):
|
|
39
|
+
print(f"Got email: {email}")
|
|
40
|
+
# Automatically navigate to login
|
|
41
|
+
|
|
42
|
+
await agent.run() # Runs forever, watching
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
page: Page,
|
|
48
|
+
panel: InteractivePanel,
|
|
49
|
+
check_interval: float = 0.5,
|
|
50
|
+
):
|
|
51
|
+
self.page = page
|
|
52
|
+
self.panel = panel
|
|
53
|
+
self.check_interval = check_interval
|
|
54
|
+
|
|
55
|
+
self.running = False
|
|
56
|
+
self.handlers: Dict[str, Callable] = {}
|
|
57
|
+
|
|
58
|
+
# State
|
|
59
|
+
self.last_url = None
|
|
60
|
+
self.last_panel_data = {}
|
|
61
|
+
|
|
62
|
+
def on_input(self, key: str):
|
|
63
|
+
"""Decorator to register handler for input."""
|
|
64
|
+
def decorator(func: Callable):
|
|
65
|
+
self.handlers[key] = func
|
|
66
|
+
return func
|
|
67
|
+
return decorator
|
|
68
|
+
|
|
69
|
+
def on_url_change(self, func: Callable):
|
|
70
|
+
"""Decorator to register handler for URL changes."""
|
|
71
|
+
self.handlers["__url_change__"] = func
|
|
72
|
+
return func
|
|
73
|
+
|
|
74
|
+
async def run(self):
|
|
75
|
+
"""
|
|
76
|
+
Run agent - watch browser continuously.
|
|
77
|
+
|
|
78
|
+
Responds to user actions automatically.
|
|
79
|
+
"""
|
|
80
|
+
self.running = True
|
|
81
|
+
|
|
82
|
+
print("🤖 Collaborative agent started")
|
|
83
|
+
print(" Watching browser for user actions...")
|
|
84
|
+
print(" Press Ctrl+C to stop")
|
|
85
|
+
|
|
86
|
+
# Signal handling
|
|
87
|
+
loop = asyncio.get_event_loop()
|
|
88
|
+
|
|
89
|
+
def stop_agent(sig, frame):
|
|
90
|
+
print("\n\n🛑 Stopping agent...")
|
|
91
|
+
self.running = False
|
|
92
|
+
|
|
93
|
+
signal.signal(signal.SIGINT, stop_agent)
|
|
94
|
+
signal.signal(signal.SIGTERM, stop_agent)
|
|
95
|
+
|
|
96
|
+
# Watch loop
|
|
97
|
+
try:
|
|
98
|
+
while self.running:
|
|
99
|
+
await self._check_changes()
|
|
100
|
+
await asyncio.sleep(self.check_interval)
|
|
101
|
+
|
|
102
|
+
except Exception as e:
|
|
103
|
+
print(f"❌ Agent error: {e}")
|
|
104
|
+
|
|
105
|
+
finally:
|
|
106
|
+
print("✅ Agent stopped")
|
|
107
|
+
|
|
108
|
+
async def _check_changes(self):
|
|
109
|
+
"""Check for changes and trigger handlers."""
|
|
110
|
+
|
|
111
|
+
# Check URL change
|
|
112
|
+
current_url = self.page.url
|
|
113
|
+
if current_url != self.last_url:
|
|
114
|
+
self.last_url = current_url
|
|
115
|
+
|
|
116
|
+
if "__url_change__" in self.handlers:
|
|
117
|
+
await self.handlers["__url_change__"](current_url)
|
|
118
|
+
|
|
119
|
+
# Check panel data changes
|
|
120
|
+
current_data = await self.page.evaluate("window.scitexPanel?.data || {}")
|
|
121
|
+
|
|
122
|
+
for key, value in current_data.items():
|
|
123
|
+
# New or changed value
|
|
124
|
+
if key not in self.last_panel_data or self.last_panel_data[key] != value:
|
|
125
|
+
self.last_panel_data[key] = value
|
|
126
|
+
|
|
127
|
+
# Trigger handler if registered
|
|
128
|
+
if key in self.handlers:
|
|
129
|
+
await self.handlers[key](value)
|
|
130
|
+
|
|
131
|
+
async def stop(self):
|
|
132
|
+
"""Stop the agent."""
|
|
133
|
+
self.running = False
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# EOF
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-19 05:50:00 (ywatanabe)"
|
|
4
|
+
# File: ./src/scitex/browser/collaboration/credential_manager.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
"""
|
|
7
|
+
Flexible credential management for browser automation.
|
|
8
|
+
|
|
9
|
+
Safe, clear communication with user.
|
|
10
|
+
Multiple input methods: env vars, terminal, browser.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import getpass
|
|
15
|
+
from typing import Optional, Dict
|
|
16
|
+
from playwright.async_api import Page
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CredentialManager:
|
|
20
|
+
"""
|
|
21
|
+
Flexible credential retrieval.
|
|
22
|
+
|
|
23
|
+
Tries multiple sources in order:
|
|
24
|
+
1. Explicitly provided credentials
|
|
25
|
+
2. Environment variables
|
|
26
|
+
3. Terminal prompt (if terminal available)
|
|
27
|
+
4. Browser prompt (if browser available)
|
|
28
|
+
|
|
29
|
+
Always clearly communicates what it's doing!
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, verbose: bool = True):
|
|
33
|
+
self.verbose = verbose
|
|
34
|
+
self.cache: Dict[str, str] = {} # Session cache (not persistent)
|
|
35
|
+
|
|
36
|
+
async def get_credential(
|
|
37
|
+
self,
|
|
38
|
+
name: str,
|
|
39
|
+
env_var: Optional[str] = None,
|
|
40
|
+
prompt_text: Optional[str] = None,
|
|
41
|
+
page: Optional[Page] = None,
|
|
42
|
+
mask: bool = False, # For passwords
|
|
43
|
+
) -> str:
|
|
44
|
+
"""
|
|
45
|
+
Get credential from best available source.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
name: Credential name (for caching)
|
|
49
|
+
env_var: Environment variable to check
|
|
50
|
+
prompt_text: Text to show in prompt
|
|
51
|
+
page: Playwright page (for browser prompts)
|
|
52
|
+
mask: Whether to mask input (for passwords)
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Credential value
|
|
56
|
+
|
|
57
|
+
Example:
|
|
58
|
+
username = await creds.get_credential(
|
|
59
|
+
name="username",
|
|
60
|
+
env_var="SCITEX_CLOUD_USERNAME",
|
|
61
|
+
prompt_text="Django username",
|
|
62
|
+
page=page,
|
|
63
|
+
)
|
|
64
|
+
"""
|
|
65
|
+
# Check cache first
|
|
66
|
+
if name in self.cache:
|
|
67
|
+
if self.verbose:
|
|
68
|
+
print(f"🔑 Using cached {name}")
|
|
69
|
+
return self.cache[name]
|
|
70
|
+
|
|
71
|
+
# Try environment variable
|
|
72
|
+
if env_var:
|
|
73
|
+
value = os.getenv(env_var)
|
|
74
|
+
if value:
|
|
75
|
+
if self.verbose:
|
|
76
|
+
display_value = "***" if mask else value
|
|
77
|
+
print(f"🔑 Using {name} from ${env_var}: {display_value}")
|
|
78
|
+
self.cache[name] = value
|
|
79
|
+
return value
|
|
80
|
+
|
|
81
|
+
# Try terminal prompt
|
|
82
|
+
if self._is_terminal_available():
|
|
83
|
+
value = await self._prompt_terminal(name, prompt_text, mask)
|
|
84
|
+
if value:
|
|
85
|
+
self.cache[name] = value
|
|
86
|
+
return value
|
|
87
|
+
|
|
88
|
+
# Try browser prompt
|
|
89
|
+
if page:
|
|
90
|
+
value = await self._prompt_browser(page, name, prompt_text)
|
|
91
|
+
if value:
|
|
92
|
+
self.cache[name] = value
|
|
93
|
+
return value
|
|
94
|
+
|
|
95
|
+
raise ValueError(f"Could not get credential: {name}")
|
|
96
|
+
|
|
97
|
+
def _is_terminal_available(self) -> bool:
|
|
98
|
+
"""Check if we can prompt in terminal."""
|
|
99
|
+
try:
|
|
100
|
+
return os.isatty(0) # stdin is a terminal
|
|
101
|
+
except:
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
async def _prompt_terminal(
|
|
105
|
+
self,
|
|
106
|
+
name: str,
|
|
107
|
+
prompt_text: Optional[str],
|
|
108
|
+
mask: bool,
|
|
109
|
+
) -> Optional[str]:
|
|
110
|
+
"""Prompt user in terminal."""
|
|
111
|
+
prompt_text = prompt_text or name
|
|
112
|
+
|
|
113
|
+
print(f"\n🔑 Credential needed: {name}")
|
|
114
|
+
print(f" (No environment variable found)")
|
|
115
|
+
|
|
116
|
+
if mask:
|
|
117
|
+
value = getpass.getpass(f" Enter {prompt_text}: ")
|
|
118
|
+
else:
|
|
119
|
+
value = input(f" Enter {prompt_text}: ")
|
|
120
|
+
|
|
121
|
+
return value if value else None
|
|
122
|
+
|
|
123
|
+
async def _prompt_browser(
|
|
124
|
+
self,
|
|
125
|
+
page: Page,
|
|
126
|
+
name: str,
|
|
127
|
+
prompt_text: Optional[str],
|
|
128
|
+
) -> Optional[str]:
|
|
129
|
+
"""Prompt user in browser window."""
|
|
130
|
+
prompt_text = prompt_text or name
|
|
131
|
+
|
|
132
|
+
print(f"\n🔑 Asking for {name} in browser...")
|
|
133
|
+
|
|
134
|
+
# Wait for page to be ready
|
|
135
|
+
try:
|
|
136
|
+
await page.wait_for_load_state("domcontentloaded", timeout=2000)
|
|
137
|
+
except:
|
|
138
|
+
pass # Continue anyway
|
|
139
|
+
|
|
140
|
+
value = await page.evaluate(f"""
|
|
141
|
+
() => {{
|
|
142
|
+
const response = prompt('🔑 Credential needed: {prompt_text}\\n\\n(You can also set ${name.upper()} environment variable)');
|
|
143
|
+
return response;
|
|
144
|
+
}}
|
|
145
|
+
""")
|
|
146
|
+
|
|
147
|
+
return value if value else None
|
|
148
|
+
|
|
149
|
+
async def get_login_credentials(
|
|
150
|
+
self,
|
|
151
|
+
page: Optional[Page] = None,
|
|
152
|
+
username_env: str = "SCITEX_CLOUD_USERNAME",
|
|
153
|
+
password_env: str = "SCITEX_CLOUD_PASSWORD",
|
|
154
|
+
) -> Dict[str, str]:
|
|
155
|
+
"""
|
|
156
|
+
Get both username and password.
|
|
157
|
+
|
|
158
|
+
Convenient helper for login flows.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
{'username': '...', 'password': '...'}
|
|
162
|
+
"""
|
|
163
|
+
username = await self.get_credential(
|
|
164
|
+
name="username",
|
|
165
|
+
env_var=username_env,
|
|
166
|
+
prompt_text="Username",
|
|
167
|
+
page=page,
|
|
168
|
+
mask=False,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
password = await self.get_credential(
|
|
172
|
+
name="password",
|
|
173
|
+
env_var=password_env,
|
|
174
|
+
prompt_text="Password",
|
|
175
|
+
page=page,
|
|
176
|
+
mask=True,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
return {"username": username, "password": password}
|
|
180
|
+
|
|
181
|
+
def clear_cache(self):
|
|
182
|
+
"""Clear credential cache."""
|
|
183
|
+
self.cache = {}
|
|
184
|
+
if self.verbose:
|
|
185
|
+
print("🔑 Credential cache cleared")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# EOF
|