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,468 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-10-11 23:45:23 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/scholar/storage/PaperIO.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
__FILE__ = (
|
|
9
|
+
"./src/scitex/scholar/storage/PaperIO.py"
|
|
10
|
+
)
|
|
11
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
12
|
+
# ----------------------------------------
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
Functionalities:
|
|
16
|
+
- Provides simple IO interface for Paper objects
|
|
17
|
+
- Handles save/load/check operations for Paper data in MASTER directory
|
|
18
|
+
- Each Paper gets structured directory: MASTER/{8-digit-ID}/
|
|
19
|
+
- Supports incremental data addition (check → process → save pattern)
|
|
20
|
+
- All operations work with Paper object fields
|
|
21
|
+
|
|
22
|
+
Dependencies:
|
|
23
|
+
- packages:
|
|
24
|
+
- pydantic
|
|
25
|
+
- scitex
|
|
26
|
+
|
|
27
|
+
IO:
|
|
28
|
+
- input-files:
|
|
29
|
+
- library/MASTER/{paper_id}/metadata.json
|
|
30
|
+
- library/MASTER/{paper_id}/main.pdf
|
|
31
|
+
- library/downloads/{UUID} (for PDF import)
|
|
32
|
+
|
|
33
|
+
- output-files:
|
|
34
|
+
- library/MASTER/{paper_id}/metadata.json
|
|
35
|
+
- library/MASTER/{paper_id}/main.pdf
|
|
36
|
+
- library/MASTER/{paper_id}/content.txt
|
|
37
|
+
- library/MASTER/{paper_id}/tables.json
|
|
38
|
+
- library/MASTER/{paper_id}/images/
|
|
39
|
+
- library/MASTER/{paper_id}/screenshots/
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
"""Imports"""
|
|
43
|
+
import argparse
|
|
44
|
+
import json
|
|
45
|
+
import shutil
|
|
46
|
+
from pathlib import Path
|
|
47
|
+
from typing import Any, Dict, List, Optional
|
|
48
|
+
|
|
49
|
+
from scitex import logging
|
|
50
|
+
from scitex.scholar.core import Paper
|
|
51
|
+
|
|
52
|
+
logger = logging.getLogger(__name__)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class PaperIO:
|
|
56
|
+
"""Simple IO interface for Paper objects.
|
|
57
|
+
|
|
58
|
+
Handles all file operations for a Paper in its MASTER directory.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, paper: Paper, base_dir: Optional[Path] = None):
|
|
62
|
+
"""Initialize PaperIO for a Paper.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
paper: Paper object to manage
|
|
66
|
+
base_dir: Base directory (default: ~/.scitex/scholar/library/MASTER)
|
|
67
|
+
"""
|
|
68
|
+
self.paper = paper
|
|
69
|
+
self.name = self.__class__.__name__
|
|
70
|
+
|
|
71
|
+
if base_dir is None:
|
|
72
|
+
from scitex.scholar import ScholarConfig
|
|
73
|
+
|
|
74
|
+
config = ScholarConfig()
|
|
75
|
+
base_dir = config.get_library_master_dir()
|
|
76
|
+
|
|
77
|
+
# Get paper ID from container
|
|
78
|
+
paper_id = paper.container.library_id
|
|
79
|
+
if not paper_id:
|
|
80
|
+
raise ValueError("Paper must have container.library_id set")
|
|
81
|
+
|
|
82
|
+
self.paper_dir = Path(base_dir) / paper_id
|
|
83
|
+
self.paper_dir.mkdir(parents=True, exist_ok=True)
|
|
84
|
+
|
|
85
|
+
# ========================================
|
|
86
|
+
# Path Getters
|
|
87
|
+
# ========================================
|
|
88
|
+
def get_metadata_path(self) -> Path:
|
|
89
|
+
"""Get path to metadata.json"""
|
|
90
|
+
return self.paper_dir / "metadata.json"
|
|
91
|
+
|
|
92
|
+
def get_pdf_path(self, suffix: str = None) -> Path:
|
|
93
|
+
"""Get formatted PDF path using PathManager template.
|
|
94
|
+
|
|
95
|
+
Returns path like: MASTER/{paper_id}/{FirstAuthor}-{year}-{Journal}.pdf
|
|
96
|
+
If suffix provided: {FirstAuthor}-{year}-{Journal}-{suffix}.pdf
|
|
97
|
+
"""
|
|
98
|
+
from scitex.scholar import ScholarConfig
|
|
99
|
+
|
|
100
|
+
config = ScholarConfig()
|
|
101
|
+
|
|
102
|
+
# Extract metadata for formatting
|
|
103
|
+
first_author = (
|
|
104
|
+
self.paper.metadata.basic.authors[0].split()[-1]
|
|
105
|
+
if self.paper.metadata.basic.authors
|
|
106
|
+
else "Unknown"
|
|
107
|
+
)
|
|
108
|
+
year = self.paper.metadata.basic.year or 0
|
|
109
|
+
journal_name = (
|
|
110
|
+
self.paper.metadata.publication.short_journal
|
|
111
|
+
or self.paper.metadata.publication.journal
|
|
112
|
+
or "Unknown"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Normalize journal name using PathManager (single source of truth)
|
|
116
|
+
# This delegates to PathManager._sanitize_filename() which replaces spaces/dots with hyphens
|
|
117
|
+
pdf_name = config.path_manager.get_library_project_entry_pdf_fname(
|
|
118
|
+
first_author=first_author,
|
|
119
|
+
year=year,
|
|
120
|
+
journal_name=journal_name, # Will be sanitized by PathManager
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Add suffix if provided (for duplicate PDFs)
|
|
124
|
+
if suffix:
|
|
125
|
+
name, ext = pdf_name.rsplit(".", 1)
|
|
126
|
+
pdf_name = f"{name}-{suffix}.{ext}"
|
|
127
|
+
|
|
128
|
+
return self.paper_dir / pdf_name
|
|
129
|
+
|
|
130
|
+
def get_text_path(self) -> Path:
|
|
131
|
+
"""Get path to content.txt"""
|
|
132
|
+
return self.paper_dir / "content.txt"
|
|
133
|
+
|
|
134
|
+
def get_tables_path(self) -> Path:
|
|
135
|
+
"""Get path to tables.json"""
|
|
136
|
+
return self.paper_dir / "tables.json"
|
|
137
|
+
|
|
138
|
+
def get_images_dir(self) -> Path:
|
|
139
|
+
"""Get path to images/ directory"""
|
|
140
|
+
images_dir = self.paper_dir / "images"
|
|
141
|
+
images_dir.mkdir(exist_ok=True)
|
|
142
|
+
return images_dir
|
|
143
|
+
|
|
144
|
+
def get_screenshots_dir(self) -> Path:
|
|
145
|
+
"""Get path to screenshots/ directory"""
|
|
146
|
+
screenshots_dir = self.paper_dir / "screenshots"
|
|
147
|
+
screenshots_dir.mkdir(exist_ok=True)
|
|
148
|
+
return screenshots_dir
|
|
149
|
+
|
|
150
|
+
def get_entry_name_for_project(self) -> str:
|
|
151
|
+
"""Generate entry/symlink name using PathManager format.
|
|
152
|
+
|
|
153
|
+
Returns formatted name like:
|
|
154
|
+
PDF-01_CC-000113_IF-010_2017_Baldassano_Brain
|
|
155
|
+
"""
|
|
156
|
+
from scitex.scholar import ScholarConfig
|
|
157
|
+
|
|
158
|
+
config = ScholarConfig()
|
|
159
|
+
|
|
160
|
+
# Count PDFs in directory
|
|
161
|
+
n_pdfs = len(list(self.paper_dir.glob("*.pdf")))
|
|
162
|
+
|
|
163
|
+
# Extract metadata
|
|
164
|
+
citation_count = self.paper.metadata.citation_count.total or 0
|
|
165
|
+
impact_factor = int(self.paper.metadata.publication.impact_factor or 0)
|
|
166
|
+
year = self.paper.metadata.basic.year or 0
|
|
167
|
+
first_author = (
|
|
168
|
+
self.paper.metadata.basic.authors[0].split()[-1]
|
|
169
|
+
if self.paper.metadata.basic.authors
|
|
170
|
+
else "Unknown"
|
|
171
|
+
)
|
|
172
|
+
journal_name = (
|
|
173
|
+
self.paper.metadata.publication.short_journal
|
|
174
|
+
or self.paper.metadata.publication.journal
|
|
175
|
+
or "Unknown"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Use PathManager to format (single source of truth)
|
|
179
|
+
return config.path_manager.get_library_project_entry_dirname(
|
|
180
|
+
n_pdfs=n_pdfs,
|
|
181
|
+
citation_count=citation_count,
|
|
182
|
+
impact_factor=impact_factor,
|
|
183
|
+
year=year,
|
|
184
|
+
first_author=first_author,
|
|
185
|
+
journal_name=journal_name,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# ========================================
|
|
189
|
+
# Check Methods
|
|
190
|
+
# ========================================
|
|
191
|
+
def has_metadata(self) -> bool:
|
|
192
|
+
"""Check if metadata.json exists"""
|
|
193
|
+
return self.get_metadata_path().exists()
|
|
194
|
+
|
|
195
|
+
def has_pdf(self) -> bool:
|
|
196
|
+
"""Check if any PDF exists in paper directory"""
|
|
197
|
+
return len(list(self.paper_dir.glob("*.pdf"))) > 0
|
|
198
|
+
|
|
199
|
+
def has_content(self) -> bool:
|
|
200
|
+
"""Check if content.txt exists"""
|
|
201
|
+
return self.get_text_path().exists()
|
|
202
|
+
|
|
203
|
+
def has_tables(self) -> bool:
|
|
204
|
+
"""Check if tables.json exists"""
|
|
205
|
+
return self.get_tables_path().exists()
|
|
206
|
+
|
|
207
|
+
# ========================================
|
|
208
|
+
# Save Methods
|
|
209
|
+
# ========================================
|
|
210
|
+
def save_metadata(self) -> Path:
|
|
211
|
+
"""Save Paper metadata to metadata.json
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Path to saved metadata.json
|
|
215
|
+
"""
|
|
216
|
+
path = self.get_metadata_path()
|
|
217
|
+
with open(path, "w") as f:
|
|
218
|
+
json.dump(self.paper.to_dict(), f, indent=2)
|
|
219
|
+
logger.info(f"{self.name}: Saved metadata: {path}")
|
|
220
|
+
return path
|
|
221
|
+
|
|
222
|
+
def save_pdf(self, pdf_path: Path) -> Path:
|
|
223
|
+
"""Copy PDF to paper directory as main.pdf
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
pdf_path: Source PDF file path
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Path to main.pdf in paper directory
|
|
230
|
+
"""
|
|
231
|
+
pdf_path = Path(pdf_path)
|
|
232
|
+
if not pdf_path.exists():
|
|
233
|
+
raise FileNotFoundError(f"PDF not found: {pdf_path}")
|
|
234
|
+
|
|
235
|
+
dest = self.get_pdf_path()
|
|
236
|
+
shutil.copy2(pdf_path, dest)
|
|
237
|
+
|
|
238
|
+
# Update paper object
|
|
239
|
+
self.paper.metadata.path.pdfs = [str(dest)]
|
|
240
|
+
self.paper.container.pdf_size_bytes = dest.stat().st_size
|
|
241
|
+
|
|
242
|
+
logger.info(f"{self.name}: Saved PDF: {dest}")
|
|
243
|
+
return dest
|
|
244
|
+
|
|
245
|
+
def save_text(self, text: str) -> Path:
|
|
246
|
+
"""Save extracted text to content.txt
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
text: Extracted text content
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Path to content.txt
|
|
253
|
+
"""
|
|
254
|
+
path = self.get_text_path()
|
|
255
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
256
|
+
f.write(text)
|
|
257
|
+
logger.info(f"{self.name}: Saved text: {path}")
|
|
258
|
+
return path
|
|
259
|
+
|
|
260
|
+
def save_tables(self, tables: List[Any]) -> Path:
|
|
261
|
+
"""Save extracted tables to tables.json
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
tables: List of table data
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Path to tables.json
|
|
268
|
+
"""
|
|
269
|
+
path = self.get_tables_path()
|
|
270
|
+
with open(path, "w") as f:
|
|
271
|
+
json.dump(tables, f, indent=2)
|
|
272
|
+
logger.info(f"{self.name}: Saved {len(tables)} tables: {path}")
|
|
273
|
+
return path
|
|
274
|
+
|
|
275
|
+
def save_image(self, image_data: bytes, filename: str) -> Path:
|
|
276
|
+
"""Save extracted image to images/ directory
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
image_data: Image bytes
|
|
280
|
+
filename: Image filename (e.g., "fig1.png")
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
Path to saved image
|
|
284
|
+
"""
|
|
285
|
+
images_dir = self.get_images_dir()
|
|
286
|
+
path = images_dir / filename
|
|
287
|
+
with open(path, "wb") as f:
|
|
288
|
+
f.write(image_data)
|
|
289
|
+
logger.info(f"{self.name}: Saved image: {path}")
|
|
290
|
+
return path
|
|
291
|
+
|
|
292
|
+
# ========================================
|
|
293
|
+
# Load Methods
|
|
294
|
+
# ========================================
|
|
295
|
+
def load_metadata(self) -> Paper:
|
|
296
|
+
"""Load Paper from metadata.json and update internal reference.
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
Paper object
|
|
300
|
+
"""
|
|
301
|
+
path = self.get_metadata_path()
|
|
302
|
+
if not path.exists():
|
|
303
|
+
raise FileNotFoundError(f"Metadata not found: {path}")
|
|
304
|
+
|
|
305
|
+
with open(path, "r") as f:
|
|
306
|
+
data = json.load(f)
|
|
307
|
+
|
|
308
|
+
paper = Paper.from_dict(data)
|
|
309
|
+
# Update internal reference so save_metadata() uses the loaded paper
|
|
310
|
+
self.paper = paper
|
|
311
|
+
logger.info(f"{self.name}: Loaded metadata: {path}")
|
|
312
|
+
return paper
|
|
313
|
+
|
|
314
|
+
def load_text(self) -> str:
|
|
315
|
+
"""Load extracted text from content.txt
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
Text content
|
|
319
|
+
"""
|
|
320
|
+
path = self.get_text_path()
|
|
321
|
+
if not path.exists():
|
|
322
|
+
raise FileNotFoundError(f"Text not found: {path}")
|
|
323
|
+
|
|
324
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
325
|
+
text = f.read()
|
|
326
|
+
logger.info(f"{self.name}: Loaded text: {path}")
|
|
327
|
+
return text
|
|
328
|
+
|
|
329
|
+
def load_tables(self) -> List[Any]:
|
|
330
|
+
"""Load extracted tables from tables.json
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
List of tables
|
|
334
|
+
"""
|
|
335
|
+
path = self.get_tables_path()
|
|
336
|
+
if not path.exists():
|
|
337
|
+
raise FileNotFoundError(f"Tables not found: {path}")
|
|
338
|
+
|
|
339
|
+
with open(path, "r") as f:
|
|
340
|
+
tables = json.load(f)
|
|
341
|
+
logger.info(f"{self.name}: Loaded {len(tables)} tables: {path}")
|
|
342
|
+
return tables
|
|
343
|
+
|
|
344
|
+
# ========================================
|
|
345
|
+
# Utility Methods
|
|
346
|
+
# ========================================
|
|
347
|
+
def get_all_files(self) -> Dict[str, bool]:
|
|
348
|
+
"""Get status of all expected files
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Dictionary of actual filename: exists (shows real PDF name if exists)
|
|
352
|
+
"""
|
|
353
|
+
# Get actual PDF filename using PathManager getter
|
|
354
|
+
pdf_files = list(self.paper_dir.glob("*.pdf"))
|
|
355
|
+
if pdf_files:
|
|
356
|
+
# Show actual PDF filename
|
|
357
|
+
pdf_key = pdf_files[0].name
|
|
358
|
+
else:
|
|
359
|
+
# Show expected PDF filename from PathManager
|
|
360
|
+
expected_pdf = self.get_pdf_path()
|
|
361
|
+
pdf_key = expected_pdf.name
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
"metadata.json": self.has_metadata(),
|
|
365
|
+
pdf_key: self.has_pdf(), # Shows actual PDF filename
|
|
366
|
+
"content.txt": self.has_content(),
|
|
367
|
+
"tables.json": self.has_tables(),
|
|
368
|
+
"images/": self.get_images_dir().exists(),
|
|
369
|
+
"screenshots/": self.get_screenshots_dir().exists(),
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
def __repr__(self) -> str:
|
|
373
|
+
"""String representation"""
|
|
374
|
+
return f"PaperIO({self.paper.container.library_id}, {self.paper_dir})"
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def main(args):
|
|
378
|
+
"""Demo: PaperIO usage with worker pattern"""
|
|
379
|
+
from scitex.scholar.core import Paper
|
|
380
|
+
|
|
381
|
+
logger.info("PaperIO Demo - Worker Pattern")
|
|
382
|
+
|
|
383
|
+
# Create paper
|
|
384
|
+
paper = Paper()
|
|
385
|
+
paper.metadata.id.doi = "10.1212/WNL.0000000000200348"
|
|
386
|
+
paper.metadata.id.doi_engines = ["demo"]
|
|
387
|
+
paper.container.library_id = "A1B2C3D4"
|
|
388
|
+
|
|
389
|
+
logger.info(f"Paper DOI: {paper.metadata.id.doi}")
|
|
390
|
+
logger.info(f"Library ID: {paper.container.library_id}")
|
|
391
|
+
|
|
392
|
+
# Initialize IO
|
|
393
|
+
io = PaperIO(paper)
|
|
394
|
+
logger.info(f"Paper directory: {io.paper_dir}")
|
|
395
|
+
|
|
396
|
+
# Worker 1: Metadata
|
|
397
|
+
if not io.has_metadata():
|
|
398
|
+
paper.metadata.basic.title = "Demo Paper"
|
|
399
|
+
paper.metadata.basic.title_engines = ["demo"]
|
|
400
|
+
io.save_metadata()
|
|
401
|
+
logger.success("Metadata saved")
|
|
402
|
+
else:
|
|
403
|
+
logger.info("Metadata exists, loading...")
|
|
404
|
+
paper = io.load_metadata()
|
|
405
|
+
|
|
406
|
+
# Worker 2: PDF
|
|
407
|
+
if not io.has_pdf():
|
|
408
|
+
logger.info("No PDF found (would download here)")
|
|
409
|
+
else:
|
|
410
|
+
logger.success(
|
|
411
|
+
f"PDF exists: {io.get_pdf_path().stat().st_size / 1e6:.2f} MB"
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
# Status
|
|
415
|
+
status = io.get_all_files()
|
|
416
|
+
logger.info("File status:")
|
|
417
|
+
for filename, exists in status.items():
|
|
418
|
+
logger.info(f" {'✓' if exists else '✗'} {filename}")
|
|
419
|
+
|
|
420
|
+
return 0
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def parse_args() -> argparse.Namespace:
|
|
424
|
+
"""Parse command line arguments."""
|
|
425
|
+
parser = argparse.ArgumentParser(
|
|
426
|
+
description="PaperIO - Simple IO interface for Paper objects"
|
|
427
|
+
)
|
|
428
|
+
args = parser.parse_args()
|
|
429
|
+
return args
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def run_main() -> None:
|
|
433
|
+
"""Initialize scitex framework, run main function, and cleanup."""
|
|
434
|
+
global CONFIG, CC, sys, plt, rng
|
|
435
|
+
|
|
436
|
+
import sys
|
|
437
|
+
|
|
438
|
+
import matplotlib.pyplot as plt
|
|
439
|
+
|
|
440
|
+
import scitex as stx
|
|
441
|
+
|
|
442
|
+
args = parse_args()
|
|
443
|
+
|
|
444
|
+
CONFIG, sys.stdout, sys.stderr, plt, CC, rng = stx.session.start(
|
|
445
|
+
sys,
|
|
446
|
+
plt,
|
|
447
|
+
args=args,
|
|
448
|
+
file=__FILE__,
|
|
449
|
+
sdir_suffix=None,
|
|
450
|
+
verbose=False,
|
|
451
|
+
agg=True,
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
exit_status = main(args)
|
|
455
|
+
|
|
456
|
+
stx.session.close(
|
|
457
|
+
CONFIG,
|
|
458
|
+
verbose=False,
|
|
459
|
+
notify=False,
|
|
460
|
+
message="",
|
|
461
|
+
exit_status=exit_status,
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
if __name__ == "__main__":
|
|
466
|
+
run_main()
|
|
467
|
+
|
|
468
|
+
# EOF
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Timestamp: "2025-09-30 04:18:54 (ywatanabe)"
|
|
4
|
+
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/scholar/storage/ScholarLibrary.py
|
|
5
|
+
# ----------------------------------------
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
__FILE__ = __file__
|
|
9
|
+
__DIR__ = os.path.dirname(__FILE__)
|
|
10
|
+
# ----------------------------------------
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Dict, List, Optional, Union
|
|
14
|
+
|
|
15
|
+
from scitex.scholar.config import ScholarConfig
|
|
16
|
+
|
|
17
|
+
from ._LibraryCacheManager import LibraryCacheManager
|
|
18
|
+
from ._LibraryManager import LibraryManager
|
|
19
|
+
from .BibTeXHandler import BibTeXHandler
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ScholarLibrary:
|
|
23
|
+
"""Unified Scholar library management combining cache and storage operations."""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
project: Union[str, Path] = None,
|
|
28
|
+
config: Optional[ScholarConfig] = None
|
|
29
|
+
):
|
|
30
|
+
"""Initialize ScholarLibrary.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
project: Project name (str) or library directory path (Path)
|
|
34
|
+
config: Optional ScholarConfig instance
|
|
35
|
+
"""
|
|
36
|
+
self.config = config or ScholarConfig()
|
|
37
|
+
|
|
38
|
+
# Handle both project name and library directory path
|
|
39
|
+
if isinstance(project, Path):
|
|
40
|
+
# If Path is the library root dir (e.g., ~/.scitex/scholar/library)
|
|
41
|
+
# use the default project from config
|
|
42
|
+
if project.name == 'library':
|
|
43
|
+
self.project = self.config.resolve("project", None)
|
|
44
|
+
self.config.library_dir = str(project)
|
|
45
|
+
else:
|
|
46
|
+
# Path points to specific project dir
|
|
47
|
+
# e.g., /home/user/.scitex/scholar/library/myproject -> "myproject"
|
|
48
|
+
self.project = project.name if project.is_dir() else project.stem
|
|
49
|
+
# Set library_dir to parent if it's named 'library'
|
|
50
|
+
if project.parent.name == 'library':
|
|
51
|
+
self.config.library_dir = str(project.parent)
|
|
52
|
+
else:
|
|
53
|
+
# Standard project name
|
|
54
|
+
self.project = self.config.resolve("project", project)
|
|
55
|
+
|
|
56
|
+
self._cache_manager = LibraryCacheManager(
|
|
57
|
+
project=self.project, config=self.config
|
|
58
|
+
)
|
|
59
|
+
self._library_manager = LibraryManager(
|
|
60
|
+
project=self.project, config=self.config
|
|
61
|
+
)
|
|
62
|
+
self.bibtex_handler = BibTeXHandler(
|
|
63
|
+
project=self.project, config=self.config
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def load_paper(self, library_id: str) -> Dict[str, Any]:
|
|
67
|
+
"""Load paper metadata from library."""
|
|
68
|
+
return self._cache_manager.load_paper_metadata(library_id)
|
|
69
|
+
|
|
70
|
+
def _extract_primitive(self, value):
|
|
71
|
+
"""Extract primitive value from DotDict or nested structure."""
|
|
72
|
+
from scitex.dict import DotDict
|
|
73
|
+
|
|
74
|
+
if value is None:
|
|
75
|
+
return None
|
|
76
|
+
if isinstance(value, DotDict):
|
|
77
|
+
# Convert DotDict to plain dict first
|
|
78
|
+
value = dict(value)
|
|
79
|
+
if isinstance(value, dict):
|
|
80
|
+
# For nested dict structures, return as-is (will be handled by save_resolved_paper)
|
|
81
|
+
return value
|
|
82
|
+
# Return primitive types as-is
|
|
83
|
+
return value
|
|
84
|
+
|
|
85
|
+
def save_paper(self, paper: "Paper", force: bool = False) -> str:
|
|
86
|
+
"""Save paper to library with explicit parameters.
|
|
87
|
+
|
|
88
|
+
Supports both old flat Paper and new Pydantic Paper structures.
|
|
89
|
+
"""
|
|
90
|
+
# Check if this is a Pydantic Paper (has metadata attribute)
|
|
91
|
+
if hasattr(paper, 'metadata'):
|
|
92
|
+
# New Pydantic Paper structure
|
|
93
|
+
return self._library_manager.save_resolved_paper(
|
|
94
|
+
# Required fields
|
|
95
|
+
title=paper.metadata.basic.title or '',
|
|
96
|
+
doi=paper.metadata.id.doi or '',
|
|
97
|
+
|
|
98
|
+
# Optional bibliographic fields
|
|
99
|
+
year=paper.metadata.basic.year,
|
|
100
|
+
authors=paper.metadata.basic.authors,
|
|
101
|
+
journal=paper.metadata.publication.journal,
|
|
102
|
+
abstract=paper.metadata.basic.abstract,
|
|
103
|
+
|
|
104
|
+
# Additional bibliographic fields
|
|
105
|
+
volume=paper.metadata.publication.volume,
|
|
106
|
+
issue=paper.metadata.publication.issue,
|
|
107
|
+
pages=f"{paper.metadata.publication.first_page or ''}-{paper.metadata.publication.last_page or ''}" if paper.metadata.publication.first_page else None,
|
|
108
|
+
publisher=paper.metadata.publication.publisher,
|
|
109
|
+
issn=paper.metadata.publication.issn,
|
|
110
|
+
|
|
111
|
+
# Enrichment fields
|
|
112
|
+
citation_count=paper.metadata.citation_count.total,
|
|
113
|
+
impact_factor=paper.metadata.publication.impact_factor,
|
|
114
|
+
|
|
115
|
+
# Library management
|
|
116
|
+
library_id=paper.container.library_id,
|
|
117
|
+
project=self.project,
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
# Old flat Paper structure (legacy support)
|
|
121
|
+
paper_dict = paper.to_dict() if hasattr(paper, 'to_dict') else {}
|
|
122
|
+
|
|
123
|
+
return self._library_manager.save_resolved_paper(
|
|
124
|
+
# Required fields
|
|
125
|
+
title=self._extract_primitive(getattr(paper, 'title', paper_dict.get('title', ''))),
|
|
126
|
+
doi=self._extract_primitive(getattr(paper, 'doi', paper_dict.get('doi', ''))),
|
|
127
|
+
|
|
128
|
+
# Optional bibliographic fields
|
|
129
|
+
year=self._extract_primitive(getattr(paper, 'year', paper_dict.get('year'))),
|
|
130
|
+
authors=self._extract_primitive(getattr(paper, 'authors', paper_dict.get('authors'))),
|
|
131
|
+
journal=self._extract_primitive(getattr(paper, 'journal', paper_dict.get('journal'))),
|
|
132
|
+
abstract=self._extract_primitive(getattr(paper, 'abstract', paper_dict.get('abstract'))),
|
|
133
|
+
|
|
134
|
+
# Additional bibliographic fields
|
|
135
|
+
volume=self._extract_primitive(getattr(paper, 'volume', paper_dict.get('volume'))),
|
|
136
|
+
issue=self._extract_primitive(getattr(paper, 'issue', paper_dict.get('issue'))),
|
|
137
|
+
pages=self._extract_primitive(getattr(paper, 'pages', paper_dict.get('pages'))),
|
|
138
|
+
publisher=self._extract_primitive(getattr(paper, 'publisher', paper_dict.get('publisher'))),
|
|
139
|
+
issn=self._extract_primitive(getattr(paper, 'issn', paper_dict.get('issn'))),
|
|
140
|
+
|
|
141
|
+
# Enrichment fields
|
|
142
|
+
citation_count=self._extract_primitive(getattr(paper, 'citation_count', paper_dict.get('citation_count'))),
|
|
143
|
+
impact_factor=self._extract_primitive(getattr(paper, 'journal_impact_factor', paper_dict.get('journal_impact_factor'))),
|
|
144
|
+
|
|
145
|
+
# Source tracking
|
|
146
|
+
doi_source=self._extract_primitive(getattr(paper, 'doi_source', paper_dict.get('doi_source'))),
|
|
147
|
+
title_source=self._extract_primitive(getattr(paper, 'title_source', paper_dict.get('title_source'))),
|
|
148
|
+
abstract_source=self._extract_primitive(getattr(paper, 'abstract_source', paper_dict.get('abstract_source'))),
|
|
149
|
+
|
|
150
|
+
# Library management
|
|
151
|
+
library_id=self._extract_primitive(getattr(paper, 'library_id', paper_dict.get('library_id'))),
|
|
152
|
+
project=self.project,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def papers_from_bibtex(
|
|
156
|
+
self, bibtex_input: Union[str, Path]
|
|
157
|
+
) -> List["Paper"]:
|
|
158
|
+
"""Create Papers from BibTeX file or content."""
|
|
159
|
+
return self.bibtex_handler.papers_from_bibtex(bibtex_input)
|
|
160
|
+
|
|
161
|
+
def paper_from_bibtex_entry(
|
|
162
|
+
self, entry: Dict[str, Any]
|
|
163
|
+
) -> Optional["Paper"]:
|
|
164
|
+
"""Convert BibTeX entry to Paper."""
|
|
165
|
+
return self.bibtex_handler.paper_from_bibtex_entry(entry)
|
|
166
|
+
|
|
167
|
+
def check_existing_doi(
|
|
168
|
+
self, title: str, year: Optional[int] = None
|
|
169
|
+
) -> Optional[str]:
|
|
170
|
+
"""Check if DOI exists in library."""
|
|
171
|
+
return self._cache_manager.is_doi_stored(title, year)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
|
|
176
|
+
# Implement main guard to demonstrate typical usage of this script
|
|
177
|
+
def main():
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
main()
|
|
181
|
+
|
|
182
|
+
# EOF
|