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,16 @@
|
|
|
1
|
+
"""Core authentication modules."""
|
|
2
|
+
|
|
3
|
+
from .AuthenticationGateway import AuthenticationGateway, URLContext
|
|
4
|
+
from .BrowserAuthenticator import BrowserAuthenticator
|
|
5
|
+
from .StrategyResolver import AuthenticationStrategyResolver, AuthenticationStrategy, AuthenticationMethod
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"AuthenticationGateway",
|
|
9
|
+
"URLContext",
|
|
10
|
+
"BrowserAuthenticator",
|
|
11
|
+
"AuthenticationStrategyResolver",
|
|
12
|
+
"AuthenticationStrategy",
|
|
13
|
+
"AuthenticationMethod",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
# EOF
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-10 02:00:31 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/scholar/auth/gateway/_OpenURLLinkFinder.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
__FILE__ = (
|
|
9
|
+
"./src/scitex/scholar/auth/gateway/_OpenURLLinkFinder.py"
|
|
10
|
+
)
|
|
11
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
12
|
+
# ----------------------------------------
|
|
13
|
+
|
|
14
|
+
__FILE__ = __file__
|
|
15
|
+
|
|
16
|
+
import asyncio
|
|
17
|
+
from typing import List
|
|
18
|
+
|
|
19
|
+
from playwright.async_api import Locator, Page
|
|
20
|
+
|
|
21
|
+
from scitex import logging
|
|
22
|
+
from scitex.browser.debugging import highlight_element_async
|
|
23
|
+
from scitex.scholar import ScholarConfig
|
|
24
|
+
from scitex.scholar.browser.utils import click_and_wait
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class OpenURLLinkFinder:
|
|
30
|
+
"""Finds full-text links on resolver pages by publisher name."""
|
|
31
|
+
|
|
32
|
+
def __init__(self, config: ScholarConfig = None):
|
|
33
|
+
self.name = self.__class__.__name__
|
|
34
|
+
self.config = config or ScholarConfig()
|
|
35
|
+
|
|
36
|
+
async def find_link_elements(self, page: Page, doi: str) -> List[Locator]:
|
|
37
|
+
"""Find and highlight publisher text on the page."""
|
|
38
|
+
try:
|
|
39
|
+
logger.info(f"{self.name}: Finding links from {page.url}")
|
|
40
|
+
|
|
41
|
+
openurl_available_from_patterns = self.config.resolve(
|
|
42
|
+
"openurl_available_from_patterns", None
|
|
43
|
+
)
|
|
44
|
+
seen_hrefs = set()
|
|
45
|
+
found_links = []
|
|
46
|
+
|
|
47
|
+
for pattern in openurl_available_from_patterns:
|
|
48
|
+
publisher = pattern[len("Available from ") :]
|
|
49
|
+
try:
|
|
50
|
+
link_element = page.locator(
|
|
51
|
+
f'a:has-text("{publisher}")'
|
|
52
|
+
).first
|
|
53
|
+
if await link_element.count() > 0:
|
|
54
|
+
href = await link_element.get_attribute("href")
|
|
55
|
+
if href not in seen_hrefs:
|
|
56
|
+
# logger.info(f"Found link elements for: {publisher}")
|
|
57
|
+
await highlight_element_async(link_element, 500)
|
|
58
|
+
found_links.append(
|
|
59
|
+
{
|
|
60
|
+
"publisher": publisher,
|
|
61
|
+
"link_element": link_element,
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
seen_hrefs.add(href)
|
|
65
|
+
except Exception as e:
|
|
66
|
+
logger.debug(
|
|
67
|
+
f"{self.name}: Could not find {publisher}: {e}"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if found_links:
|
|
71
|
+
publishers = [
|
|
72
|
+
found_link.get("publisher") for found_link in found_links
|
|
73
|
+
]
|
|
74
|
+
logger.info(
|
|
75
|
+
f"{self.name}: Found {len(publishers)} link elements for: {', '.join(publishers)}"
|
|
76
|
+
)
|
|
77
|
+
return found_links
|
|
78
|
+
|
|
79
|
+
except Exception as e:
|
|
80
|
+
logger.fail(
|
|
81
|
+
f"{self.name}: Did not find any urls from {page.url}: {e}"
|
|
82
|
+
)
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
|
|
88
|
+
async def main():
|
|
89
|
+
from scitex.scholar import (
|
|
90
|
+
ScholarAuthManager,
|
|
91
|
+
ScholarBrowserManager,
|
|
92
|
+
ScholarURLFinder,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
auth_manager = ScholarAuthManager()
|
|
96
|
+
browser_manager = ScholarBrowserManager(
|
|
97
|
+
auth_manager=auth_manager,
|
|
98
|
+
browser_mode="interactive",
|
|
99
|
+
chrome_profile_name="system",
|
|
100
|
+
)
|
|
101
|
+
browser, context = (
|
|
102
|
+
await browser_manager.get_authenticated_browser_and_context_async()
|
|
103
|
+
)
|
|
104
|
+
page = await context.new_page()
|
|
105
|
+
url = "https://unimelb.hosted.exlibrisgroup.com/sfxlcl41?doi=10.1126/science.aao0702"
|
|
106
|
+
await page.goto(url, wait_until="networkidle", timeout=30_000)
|
|
107
|
+
await page.wait_for_timeout(3000)
|
|
108
|
+
|
|
109
|
+
finder = OpenURLLinkFinder()
|
|
110
|
+
links = await finder.find_link_elements(
|
|
111
|
+
page, "10.1126/science.aao0702"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
result = await click_and_wait(links[0].get("link_element"))
|
|
115
|
+
|
|
116
|
+
asyncio.run(main())
|
|
117
|
+
|
|
118
|
+
# python -m scitex.scholar.auth.gateway._OpenURLLinkFinder
|
|
119
|
+
|
|
120
|
+
# EOF
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-11 07:52:38 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/scholar/auth/gateway/_OpenURLResolver.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
__FILE__ = (
|
|
9
|
+
"./src/scitex/scholar/auth/gateway/_OpenURLResolver.py"
|
|
10
|
+
)
|
|
11
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
12
|
+
# ----------------------------------------
|
|
13
|
+
|
|
14
|
+
__FILE__ = __file__
|
|
15
|
+
|
|
16
|
+
from typing import Optional
|
|
17
|
+
from urllib.parse import quote
|
|
18
|
+
|
|
19
|
+
from playwright.async_api import Locator, Page
|
|
20
|
+
|
|
21
|
+
from scitex import logging
|
|
22
|
+
from scitex.browser.debugging import browser_logger
|
|
23
|
+
from scitex.scholar import ScholarConfig
|
|
24
|
+
from scitex.scholar.browser.utils import click_and_wait
|
|
25
|
+
|
|
26
|
+
from ._OpenURLLinkFinder import OpenURLLinkFinder
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class OpenURLResolver:
|
|
32
|
+
"""Handles OpenURL resolution and authentication flow."""
|
|
33
|
+
|
|
34
|
+
def __init__(self, config: ScholarConfig = None):
|
|
35
|
+
self.name = self.__class__.__name__
|
|
36
|
+
self.config = config or ScholarConfig()
|
|
37
|
+
self.resolver_url = self.config.resolve("openurl_resolver_url")
|
|
38
|
+
self.finder = OpenURLLinkFinder(config=config)
|
|
39
|
+
|
|
40
|
+
async def resolve_doi(self, doi: str, page: Page) -> Optional[str]:
|
|
41
|
+
"""Main entry point: resolve DOI through OpenURL to authenticated URL."""
|
|
42
|
+
openurl_query = self._build_query(doi)
|
|
43
|
+
if not openurl_query:
|
|
44
|
+
return None
|
|
45
|
+
return await self._resolve_query(openurl_query, page, doi)
|
|
46
|
+
|
|
47
|
+
def _build_query(self, doi: str, title: str = None) -> Optional[str]:
|
|
48
|
+
"""Build OpenURL query string."""
|
|
49
|
+
if not self.resolver_url:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
params = [f"doi={doi}"]
|
|
53
|
+
if title:
|
|
54
|
+
params.append(f"atitle={quote(title[:200])}")
|
|
55
|
+
|
|
56
|
+
built_query = f"{self.resolver_url}?{'&'.join(params)}"
|
|
57
|
+
logger.debug(f"{self.name}: Built query URL: {built_query}")
|
|
58
|
+
return built_query
|
|
59
|
+
|
|
60
|
+
async def _resolve_query(
|
|
61
|
+
self, query: str, page: Page, doi: str
|
|
62
|
+
) -> Optional[str]:
|
|
63
|
+
"""Resolve OpenURL query to final authenticated URL with retry."""
|
|
64
|
+
logger.debug(f"{self.name}: Resolving query URL: {query}...")
|
|
65
|
+
|
|
66
|
+
for attempt in range(3):
|
|
67
|
+
try:
|
|
68
|
+
await page.goto(
|
|
69
|
+
query, wait_until="domcontentloaded", timeout=60000
|
|
70
|
+
)
|
|
71
|
+
await browser_logger.debug(
|
|
72
|
+
page,
|
|
73
|
+
f"{self.name}: Loaded resolver page at {page.url[:60]}",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Visual: Waiting for page to stabilize
|
|
77
|
+
await browser_logger.debug(
|
|
78
|
+
page,
|
|
79
|
+
f"{self.name}: Waiting for resolver to load (networkidle)...",
|
|
80
|
+
)
|
|
81
|
+
try:
|
|
82
|
+
await page.wait_for_load_state(
|
|
83
|
+
"networkidle", timeout=15_000
|
|
84
|
+
)
|
|
85
|
+
await browser_logger.debug(
|
|
86
|
+
page, f"{self.name}: ✓ Resolver page ready"
|
|
87
|
+
)
|
|
88
|
+
except Exception:
|
|
89
|
+
await browser_logger.info(
|
|
90
|
+
page, f"{self.name}: Page still loading, continuing..."
|
|
91
|
+
)
|
|
92
|
+
await page.wait_for_timeout(1000)
|
|
93
|
+
|
|
94
|
+
# Visual: Finding publisher links
|
|
95
|
+
await browser_logger.info(
|
|
96
|
+
page, f"{self.name}: Searching for publisher links..."
|
|
97
|
+
)
|
|
98
|
+
found_links = await self.finder.find_link_elements(page, doi)
|
|
99
|
+
|
|
100
|
+
if not found_links:
|
|
101
|
+
await browser_logger.info(
|
|
102
|
+
page, f"{self.name}: No publisher links found"
|
|
103
|
+
)
|
|
104
|
+
await page.wait_for_timeout(2000)
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
# Visual: Found links
|
|
108
|
+
await browser_logger.debug(
|
|
109
|
+
page,
|
|
110
|
+
f"{self.name}: Found {len(found_links)} publisher link(s)",
|
|
111
|
+
)
|
|
112
|
+
await page.wait_for_timeout(1000)
|
|
113
|
+
|
|
114
|
+
# Visual: Try each publisher link
|
|
115
|
+
for i, found_link in enumerate(found_links, 1):
|
|
116
|
+
publisher = found_link.get("publisher")
|
|
117
|
+
link_element = found_link.get("link_element")
|
|
118
|
+
|
|
119
|
+
await browser_logger.debug(
|
|
120
|
+
page,
|
|
121
|
+
f"{self.name}: Clicking {publisher} link ({i}/{len(found_links)})...",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
result = await click_and_wait(
|
|
125
|
+
link_element,
|
|
126
|
+
f"Clicking {publisher} link for {doi[:20]}...",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if result.get("success"):
|
|
130
|
+
final_url = result.get("final_url")
|
|
131
|
+
await browser_logger.info(
|
|
132
|
+
page,
|
|
133
|
+
f"{self.name}: ✓ Landed at {final_url[:60]}",
|
|
134
|
+
)
|
|
135
|
+
await page.wait_for_timeout(2000)
|
|
136
|
+
return final_url
|
|
137
|
+
else:
|
|
138
|
+
await browser_logger.info(
|
|
139
|
+
page,
|
|
140
|
+
f"{self.name}: ✗ {publisher} link failed, trying next...",
|
|
141
|
+
)
|
|
142
|
+
await page.wait_for_timeout(1000)
|
|
143
|
+
|
|
144
|
+
# All links failed
|
|
145
|
+
await browser_logger.info(
|
|
146
|
+
page, f"{self.name}: ✗ All publisher links failed"
|
|
147
|
+
)
|
|
148
|
+
await page.wait_for_timeout(2000)
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
except Exception as e:
|
|
152
|
+
if attempt < 2:
|
|
153
|
+
wait_time = (attempt + 1) * 2
|
|
154
|
+
logger.warning(
|
|
155
|
+
f"OpenURL attempt {attempt + 1} failed: {e}, retrying in {wait_time}s"
|
|
156
|
+
)
|
|
157
|
+
await browser_logger.info(
|
|
158
|
+
page,
|
|
159
|
+
f"{self.name}: ✗ Attempt {attempt + 1} failed, retrying in {wait_time}s...",
|
|
160
|
+
)
|
|
161
|
+
await page.wait_for_timeout(wait_time * 1000)
|
|
162
|
+
continue
|
|
163
|
+
else:
|
|
164
|
+
logger.error(
|
|
165
|
+
f"OpenURL resolution failed after 3 attempts: {e}"
|
|
166
|
+
)
|
|
167
|
+
await browser_logger.info(
|
|
168
|
+
page,
|
|
169
|
+
f"{self.name}: ✗ FAILED after 3 attempts: {str(e)[:80]}",
|
|
170
|
+
)
|
|
171
|
+
await page.wait_for_timeout(2000)
|
|
172
|
+
await browser_logger.info(
|
|
173
|
+
page, f"{self.name}: f{doi} - query not resolved"
|
|
174
|
+
)
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
if __name__ == "__main__":
|
|
179
|
+
import asyncio
|
|
180
|
+
|
|
181
|
+
from scitex.scholar import ScholarAuthManager, ScholarBrowserManager
|
|
182
|
+
|
|
183
|
+
async def main():
|
|
184
|
+
# Initialize browser with authentication
|
|
185
|
+
auth_manager = ScholarAuthManager()
|
|
186
|
+
browser_manager = ScholarBrowserManager(
|
|
187
|
+
auth_manager=auth_manager,
|
|
188
|
+
browser_mode="stealth",
|
|
189
|
+
chrome_profile_name="system",
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
browser, context = (
|
|
193
|
+
await browser_manager.get_authenticated_browser_and_context_async()
|
|
194
|
+
)
|
|
195
|
+
page = await context.new_page()
|
|
196
|
+
|
|
197
|
+
# Test OpenURL resolver
|
|
198
|
+
resolver = OpenURLResolver()
|
|
199
|
+
doi = "10.1126/science.aao0702"
|
|
200
|
+
|
|
201
|
+
resolved_url = await resolver.resolve_doi(doi, page)
|
|
202
|
+
|
|
203
|
+
await browser.close()
|
|
204
|
+
|
|
205
|
+
asyncio.run(main())
|
|
206
|
+
|
|
207
|
+
# python -m scitex.scholar.auth.gateway._OpenURLResolver
|
|
208
|
+
|
|
209
|
+
# EOF
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/scholar/auth/gateway/__init__.py
|
|
4
|
+
# ----------------------------------------
|
|
5
|
+
"""
|
|
6
|
+
Authentication Gateway Module
|
|
7
|
+
|
|
8
|
+
This module contains authentication gateway components that handle
|
|
9
|
+
authentication mechanisms to access content behind paywalls and SSO systems.
|
|
10
|
+
|
|
11
|
+
Components:
|
|
12
|
+
- OpenURLResolver: Resolves DOIs through OpenURL resolvers
|
|
13
|
+
- OpenURLLinkFinder: Finds publisher links on resolver pages
|
|
14
|
+
- resolve_functions: Utility functions for URL resolution
|
|
15
|
+
|
|
16
|
+
These were moved from url/helpers/resolvers as they are authentication
|
|
17
|
+
mechanisms rather than URL discovery mechanisms.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from ._OpenURLResolver import OpenURLResolver
|
|
21
|
+
from ._OpenURLLinkFinder import OpenURLLinkFinder
|
|
22
|
+
from ._resolve_functions import (
|
|
23
|
+
normalize_doi_as_http,
|
|
24
|
+
resolve_publisher_url_by_navigating_to_doi_page,
|
|
25
|
+
extract_doi_from_url,
|
|
26
|
+
resolve_openurl,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"OpenURLResolver",
|
|
31
|
+
"OpenURLLinkFinder",
|
|
32
|
+
"normalize_doi_as_http",
|
|
33
|
+
"resolve_publisher_url_by_navigating_to_doi_page",
|
|
34
|
+
"extract_doi_from_url",
|
|
35
|
+
"resolve_openurl",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# EOF
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-10 03:24:00 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/scholar/auth/gateway/_resolve_functions.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
__FILE__ = (
|
|
9
|
+
"./src/scitex/scholar/auth/gateway/_resolve_functions.py"
|
|
10
|
+
)
|
|
11
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
12
|
+
# ----------------------------------------
|
|
13
|
+
|
|
14
|
+
__FILE__ = __file__
|
|
15
|
+
|
|
16
|
+
import re
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
URL Resolver Functions
|
|
20
|
+
|
|
21
|
+
Simple functions to resolve/convert between different URL types.
|
|
22
|
+
No classes, just functions that do one thing well.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import asyncio
|
|
26
|
+
from typing import Optional
|
|
27
|
+
|
|
28
|
+
from playwright.async_api import Page
|
|
29
|
+
|
|
30
|
+
from scitex import logging
|
|
31
|
+
from scitex.browser.debugging import browser_logger
|
|
32
|
+
|
|
33
|
+
from ._OpenURLResolver import OpenURLResolver
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def resolve_publisher_url_by_navigating_to_doi_page(
|
|
39
|
+
doi: str,
|
|
40
|
+
page: Page,
|
|
41
|
+
func_name="resolve_publisher_url_by_navigating_to_doi_page",
|
|
42
|
+
) -> Optional[str]:
|
|
43
|
+
"""Resolve DOI to publisher URL by following redirects."""
|
|
44
|
+
url_doi = f"https://doi.org/{doi}" if not doi.startswith("http") else doi
|
|
45
|
+
|
|
46
|
+
await browser_logger.info(
|
|
47
|
+
page,
|
|
48
|
+
f"{func_name}: Finding Publisher URL by Navigating to DOI page...",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
logger.info(f"{func_name}: Resolving DOI: {doi}")
|
|
53
|
+
await page.goto(url_doi, wait_until="domcontentloaded", timeout=30000)
|
|
54
|
+
await asyncio.sleep(2)
|
|
55
|
+
url_publisher = page.url
|
|
56
|
+
logger.info(
|
|
57
|
+
f"{func_name}: Resolved Publisher URL by navigation: {doi} -> {url_publisher}"
|
|
58
|
+
)
|
|
59
|
+
return url_publisher
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.error(
|
|
62
|
+
f"{func_name}: Publisher URL not resolved by navigating to {doi}: {e}"
|
|
63
|
+
)
|
|
64
|
+
from pathlib import Path
|
|
65
|
+
|
|
66
|
+
screenshot_dir = Path.home() / ".scitex/scholar/workspace/screenshots"
|
|
67
|
+
await browser_logger.info(
|
|
68
|
+
page,
|
|
69
|
+
f"{func_name}: {doi} - Publisher URL not resolved by navigating",
|
|
70
|
+
take_screenshot=True,
|
|
71
|
+
screenshot_category="Resolve",
|
|
72
|
+
screenshot_dir=screenshot_dir,
|
|
73
|
+
)
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
async def resolve_openurl(openurl_query: str, page: Page) -> Optional[str]:
|
|
78
|
+
"""Resolve OpenURL to final authenticated URL."""
|
|
79
|
+
resolver = OpenURLResolver()
|
|
80
|
+
doi_match = re.search(r"doi=([^&]+)", openurl_query)
|
|
81
|
+
doi = doi_match.group(1) if doi_match else ""
|
|
82
|
+
|
|
83
|
+
return await resolver._resolve_query(openurl_query, page, doi)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def normalize_doi_as_http(doi: str) -> str:
|
|
87
|
+
if doi.startswith("http"):
|
|
88
|
+
return doi
|
|
89
|
+
if doi.startswith("doi:"):
|
|
90
|
+
doi = doi[4:]
|
|
91
|
+
return f"https://doi.org/{doi}"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def extract_doi_from_url(url: str) -> Optional[str]:
|
|
95
|
+
doi_pattern = r"10\.\d{4,}(?:\.\d+)*/[-._;()/:\w]+"
|
|
96
|
+
match = re.search(doi_pattern, url)
|
|
97
|
+
if match:
|
|
98
|
+
return match.group(0)
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
# EOF
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-08-07 13:58:10 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/scholar/auth/library/_BaseAuthenticator.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
__FILE__ = __file__
|
|
9
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
10
|
+
# ----------------------------------------
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
Abstract base class for authenticators.
|
|
14
|
+
|
|
15
|
+
This module provides the base interface that all authenticators
|
|
16
|
+
(OpenAthens, Lean Library, etc.) must implement.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
"""Imports"""
|
|
20
|
+
from abc import ABC, abstractmethod
|
|
21
|
+
from typing import Any, Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
from scitex import logging
|
|
24
|
+
|
|
25
|
+
from scitex.errors import AuthenticationError
|
|
26
|
+
|
|
27
|
+
"""Logger"""
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
"""Classes"""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class BaseAuthenticator(ABC):
|
|
34
|
+
"""
|
|
35
|
+
Abstract base class for authentication providers.
|
|
36
|
+
|
|
37
|
+
All authentication providers (OpenAthens, EZProxy, Shibboleth, etc.)
|
|
38
|
+
should inherit from this class and implement the required methods.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
42
|
+
"""
|
|
43
|
+
Initialize authentication provider.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
config: Authenticator-specific configuration
|
|
47
|
+
"""
|
|
48
|
+
self.config = config or {}
|
|
49
|
+
self.name = self.__class__.__name__.replace("Authentication", "")
|
|
50
|
+
|
|
51
|
+
@abstractmethod
|
|
52
|
+
async def is_authenticate_async(self, verify_live: bool = False) -> bool:
|
|
53
|
+
"""
|
|
54
|
+
Check if currently authenticate_async.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
verify_live: If True, verify with actual request instead of just checking session
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
True if authenticate_async, False otherwise
|
|
61
|
+
"""
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
async def authenticate_async(self, **kwargs) -> dict:
|
|
66
|
+
"""
|
|
67
|
+
Perform authentication and return session data.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
**kwargs: Authenticator-specific authentication parameters
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Dictionary containing session data (e.g., cookies, tokens)
|
|
74
|
+
"""
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
async def get_auth_headers_async(self) -> Dict[str, str]:
|
|
79
|
+
"""
|
|
80
|
+
Get authentication headers for requests.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Dictionary of headers to include in authenticate_async requests
|
|
84
|
+
"""
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
@abstractmethod
|
|
88
|
+
async def get_auth_cookies_async(self) -> List[Dict[str, Any]]:
|
|
89
|
+
"""
|
|
90
|
+
Get authentication cookies for requests.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
List of cookie dictionaries
|
|
94
|
+
"""
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
async def logout_async(self) -> None:
|
|
99
|
+
"""Log out and clear authentication state."""
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
async def get_session_info_async(self) -> Dict[str, Any]:
|
|
104
|
+
"""
|
|
105
|
+
Get information about current session.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Dictionary containing session details (expiry, username, etc.)
|
|
109
|
+
"""
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
def __str__(self) -> str:
|
|
113
|
+
"""String representation of provider."""
|
|
114
|
+
return f"{self.name}Authenticator"
|
|
115
|
+
|
|
116
|
+
def __repr__(self) -> str:
|
|
117
|
+
"""Detailed representation of provider."""
|
|
118
|
+
return f"{self.name}Authenticator(config={self.config})>"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
import asyncio
|
|
123
|
+
|
|
124
|
+
async def main():
|
|
125
|
+
"""Example usage of BaseAuthenticator (through a concrete implementation)."""
|
|
126
|
+
# This is an abstract class, so we'll demonstrate with OpenAthensAuthenticator
|
|
127
|
+
from scitex.scholar.auth import OpenAthensAuthenticator
|
|
128
|
+
|
|
129
|
+
# Initialize authenticator
|
|
130
|
+
auth = OpenAthensAuthenticator(
|
|
131
|
+
institution="University Example",
|
|
132
|
+
username="your_username",
|
|
133
|
+
password="your_password",
|
|
134
|
+
browser_backend="local", # or "zenrows"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
# Check if already authenticate_async
|
|
139
|
+
is_auth = await auth.is_authenticate_async()
|
|
140
|
+
print(f"Initially authenticate_async: {is_auth}")
|
|
141
|
+
|
|
142
|
+
if not is_auth:
|
|
143
|
+
# Perform authentication
|
|
144
|
+
print("Authenticating...")
|
|
145
|
+
success = await auth.authenticate_async()
|
|
146
|
+
print(f"Authentication successful: {success}")
|
|
147
|
+
|
|
148
|
+
# Get authentication headers/cookies for requests
|
|
149
|
+
headers = await auth.get_auth_headers_async()
|
|
150
|
+
cookies = await auth.get_auth_cookies_async()
|
|
151
|
+
print(f"Auth headers: {list(headers.keys())}")
|
|
152
|
+
print(f"Auth cookies: {list(cookies.keys())}")
|
|
153
|
+
|
|
154
|
+
# Get session info
|
|
155
|
+
session_info = await auth.get_session_info_async()
|
|
156
|
+
print(f"Session info: {session_info}")
|
|
157
|
+
|
|
158
|
+
finally:
|
|
159
|
+
# Always cleanup
|
|
160
|
+
await auth.logout_async()
|
|
161
|
+
print("Logged out successfully")
|
|
162
|
+
|
|
163
|
+
# Run the example
|
|
164
|
+
asyncio.run(main())
|
|
165
|
+
|
|
166
|
+
# EOF
|