scitex 2.5.0__py3-none-any.whl → 2.7.0__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 +19 -8
- scitex/__main__.py +2 -1
- scitex/__version__.py +1 -1
- scitex/_optional_deps.py +13 -20
- scitex/ai/__init__.py +5 -0
- scitex/ai/_gen_ai/_Anthropic.py +3 -1
- scitex/ai/_gen_ai/_BaseGenAI.py +3 -2
- scitex/ai/_gen_ai/_DeepSeek.py +1 -1
- scitex/ai/_gen_ai/_Google.py +3 -2
- scitex/ai/_gen_ai/_Llama.py +4 -2
- scitex/ai/_gen_ai/_OpenAI.py +3 -1
- scitex/ai/_gen_ai/_PARAMS.py +1 -0
- scitex/ai/_gen_ai/_Perplexity.py +3 -1
- scitex/ai/_gen_ai/__init__.py +1 -0
- scitex/ai/_gen_ai/_format_output_func.py +3 -1
- scitex/ai/classification/CrossValidationExperiment.py +8 -14
- scitex/ai/classification/examples/timeseries_cv_demo.py +128 -112
- scitex/ai/classification/reporters/_BaseClassificationReporter.py +2 -0
- scitex/ai/classification/reporters/_ClassificationReporter.py +30 -45
- scitex/ai/classification/reporters/_MultiClassificationReporter.py +8 -11
- scitex/ai/classification/reporters/_SingleClassificationReporter.py +126 -182
- scitex/ai/classification/reporters/__init__.py +1 -1
- scitex/ai/classification/reporters/reporter_utils/_Plotter.py +213 -119
- scitex/ai/classification/reporters/reporter_utils/__init__.py +28 -36
- scitex/ai/classification/reporters/reporter_utils/aggregation.py +125 -143
- scitex/ai/classification/reporters/reporter_utils/data_models.py +128 -120
- scitex/ai/classification/reporters/reporter_utils/reporting.py +507 -340
- scitex/ai/classification/reporters/reporter_utils/storage.py +4 -1
- scitex/ai/classification/reporters/reporter_utils/validation.py +141 -154
- scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +204 -129
- scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +215 -171
- scitex/ai/classification/timeseries/_TimeSeriesMetadata.py +17 -17
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +67 -143
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +67 -143
- scitex/ai/classification/timeseries/_TimeSeriesStrategy.py +12 -13
- scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +231 -144
- scitex/ai/classification/timeseries/__init__.py +2 -4
- scitex/ai/classification/timeseries/_normalize_timestamp.py +3 -0
- scitex/ai/clustering/_pca.py +0 -1
- scitex/ai/clustering/_umap.py +1 -2
- scitex/ai/feature_extraction/__init__.py +10 -8
- scitex/ai/feature_extraction/vit.py +0 -1
- scitex/ai/feature_selection/feature_selection.py +3 -8
- scitex/ai/metrics/_calc_conf_mat.py +2 -0
- scitex/ai/metrics/_calc_feature_importance.py +3 -7
- scitex/ai/metrics/_calc_pre_rec_auc.py +5 -5
- scitex/ai/metrics/_calc_roc_auc.py +4 -2
- scitex/ai/metrics/_calc_seizure_prediction_metrics.py +35 -20
- scitex/ai/metrics/_calc_silhouette_score.py +1 -3
- scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger.py +0 -3
- scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger2020.py +0 -3
- scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger913A.py +0 -3
- scitex/ai/optim/_optimizers.py +1 -1
- scitex/ai/plt/__init__.py +6 -1
- scitex/ai/plt/_plot_feature_importance.py +1 -3
- scitex/ai/plt/_plot_learning_curve.py +9 -24
- scitex/ai/plt/_plot_optuna_study.py +4 -3
- scitex/ai/plt/_plot_pre_rec_curve.py +9 -15
- scitex/ai/plt/_plot_roc_curve.py +6 -8
- scitex/ai/plt/_stx_conf_mat.py +121 -122
- scitex/ai/sampling/undersample.py +3 -2
- scitex/ai/sklearn/__init__.py +2 -2
- scitex/ai/training/_LearningCurveLogger.py +23 -10
- scitex/ai/utils/_check_params.py +0 -1
- scitex/benchmark/__init__.py +15 -25
- scitex/benchmark/benchmark.py +124 -117
- scitex/benchmark/monitor.py +117 -107
- scitex/benchmark/profiler.py +61 -58
- scitex/bridge/__init__.py +110 -0
- scitex/bridge/_helpers.py +149 -0
- scitex/bridge/_plt_vis.py +529 -0
- scitex/bridge/_protocol.py +283 -0
- scitex/bridge/_stats_plt.py +261 -0
- scitex/bridge/_stats_vis.py +265 -0
- scitex/browser/__init__.py +0 -2
- scitex/browser/auth/__init__.py +0 -0
- scitex/browser/auth/google.py +16 -11
- scitex/browser/automation/CookieHandler.py +2 -3
- scitex/browser/collaboration/__init__.py +3 -0
- scitex/browser/collaboration/auth_helpers.py +3 -1
- scitex/browser/collaboration/collaborative_agent.py +2 -0
- scitex/browser/collaboration/interactive_panel.py +2 -2
- scitex/browser/collaboration/shared_session.py +20 -11
- scitex/browser/collaboration/standard_interactions.py +1 -0
- scitex/browser/core/BrowserMixin.py +12 -30
- scitex/browser/core/ChromeProfileManager.py +9 -24
- scitex/browser/debugging/_browser_logger.py +15 -25
- scitex/browser/debugging/_failure_capture.py +9 -2
- scitex/browser/debugging/_highlight_element.py +15 -6
- scitex/browser/debugging/_show_grid.py +5 -6
- scitex/browser/debugging/_sync_session.py +4 -3
- scitex/browser/debugging/_test_monitor.py +14 -5
- scitex/browser/debugging/_visual_cursor.py +46 -35
- scitex/browser/interaction/click_center.py +4 -3
- scitex/browser/interaction/click_with_fallbacks.py +7 -10
- scitex/browser/interaction/close_popups.py +79 -66
- scitex/browser/interaction/fill_with_fallbacks.py +8 -8
- scitex/browser/pdf/__init__.py +3 -1
- scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +11 -10
- scitex/browser/pdf/detect_chrome_pdf_viewer.py +3 -6
- scitex/browser/remote/CaptchaHandler.py +109 -96
- scitex/browser/remote/ZenRowsAPIClient.py +91 -97
- scitex/browser/remote/ZenRowsBrowserManager.py +138 -112
- scitex/browser/stealth/HumanBehavior.py +4 -9
- scitex/browser/stealth/StealthManager.py +11 -26
- scitex/capture/__init__.py +17 -17
- scitex/capture/__main__.py +2 -3
- scitex/capture/capture.py +23 -51
- scitex/capture/cli.py +14 -39
- scitex/capture/gif.py +5 -9
- scitex/capture/mcp_server.py +7 -20
- scitex/capture/session.py +4 -3
- scitex/capture/utils.py +18 -53
- scitex/cli/__init__.py +1 -1
- scitex/cli/cloud.py +158 -116
- scitex/cli/config.py +224 -0
- scitex/cli/main.py +41 -40
- scitex/cli/scholar.py +60 -27
- scitex/cli/security.py +14 -20
- scitex/cli/web.py +87 -90
- scitex/cli/writer.py +51 -45
- scitex/cloud/__init__.py +14 -11
- scitex/cloud/_matplotlib_hook.py +6 -6
- scitex/config/README.md +313 -0
- scitex/config/{PriorityConfig.py → _PriorityConfig.py} +114 -17
- scitex/config/_ScitexConfig.py +319 -0
- scitex/config/__init__.py +41 -9
- scitex/config/_paths.py +325 -0
- scitex/config/default.yaml +81 -0
- scitex/context/_suppress_output.py +2 -3
- scitex/db/_BaseMixins/_BaseBackupMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseBatchMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseBlobMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseImportExportMixin.py +1 -3
- scitex/db/_BaseMixins/_BaseIndexMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseMaintenanceMixin.py +1 -3
- scitex/db/_BaseMixins/_BaseQueryMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseRowMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseTableMixin.py +3 -1
- scitex/db/_BaseMixins/_BaseTransactionMixin.py +1 -3
- scitex/db/_BaseMixins/__init__.py +1 -1
- scitex/db/__init__.py +9 -1
- scitex/db/__main__.py +8 -21
- scitex/db/_check_health.py +15 -31
- scitex/db/_delete_duplicates.py +7 -4
- scitex/db/_inspect.py +22 -38
- scitex/db/_inspect_optimized.py +89 -85
- scitex/db/_postgresql/_PostgreSQL.py +0 -1
- scitex/db/_postgresql/_PostgreSQLMixins/_BlobMixin.py +3 -1
- scitex/db/_postgresql/_PostgreSQLMixins/_ConnectionMixin.py +1 -3
- scitex/db/_postgresql/_PostgreSQLMixins/_ImportExportMixin.py +1 -3
- scitex/db/_postgresql/_PostgreSQLMixins/_MaintenanceMixin.py +1 -4
- scitex/db/_postgresql/_PostgreSQLMixins/_QueryMixin.py +3 -3
- scitex/db/_postgresql/_PostgreSQLMixins/_RowMixin.py +3 -1
- scitex/db/_postgresql/_PostgreSQLMixins/_TransactionMixin.py +1 -3
- scitex/db/_postgresql/__init__.py +1 -1
- scitex/db/_sqlite3/_SQLite3.py +2 -4
- scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin.py +11 -12
- scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin_v01-need-_hash-col.py +19 -14
- scitex/db/_sqlite3/_SQLite3Mixins/_BatchMixin.py +3 -1
- scitex/db/_sqlite3/_SQLite3Mixins/_BlobMixin.py +7 -7
- scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin.py +118 -111
- scitex/db/_sqlite3/_SQLite3Mixins/_ConnectionMixin.py +8 -10
- scitex/db/_sqlite3/_SQLite3Mixins/_GitMixin.py +17 -45
- scitex/db/_sqlite3/_SQLite3Mixins/_ImportExportMixin.py +1 -3
- scitex/db/_sqlite3/_SQLite3Mixins/_IndexMixin.py +3 -1
- scitex/db/_sqlite3/_SQLite3Mixins/_QueryMixin.py +3 -4
- scitex/db/_sqlite3/_SQLite3Mixins/_RowMixin.py +9 -9
- scitex/db/_sqlite3/_SQLite3Mixins/_TableMixin.py +18 -11
- scitex/db/_sqlite3/_SQLite3Mixins/__init__.py +1 -0
- scitex/db/_sqlite3/__init__.py +1 -1
- scitex/db/_sqlite3/_delete_duplicates.py +13 -11
- scitex/decorators/__init__.py +29 -4
- scitex/decorators/_auto_order.py +43 -43
- scitex/decorators/_batch_fn.py +12 -6
- scitex/decorators/_cache_disk.py +8 -9
- scitex/decorators/_cache_disk_async.py +8 -7
- scitex/decorators/_combined.py +19 -13
- scitex/decorators/_converters.py +16 -3
- scitex/decorators/_deprecated.py +32 -22
- scitex/decorators/_numpy_fn.py +18 -4
- scitex/decorators/_pandas_fn.py +17 -5
- scitex/decorators/_signal_fn.py +17 -3
- scitex/decorators/_torch_fn.py +32 -15
- scitex/decorators/_xarray_fn.py +23 -9
- scitex/dev/_analyze_code_flow.py +0 -2
- scitex/dict/_DotDict.py +15 -19
- scitex/dict/_flatten.py +1 -0
- scitex/dict/_listed_dict.py +1 -0
- scitex/dict/_pop_keys.py +1 -0
- scitex/dict/_replace.py +1 -0
- scitex/dict/_safe_merge.py +1 -0
- scitex/dict/_to_str.py +2 -3
- scitex/dsp/__init__.py +13 -4
- scitex/dsp/_crop.py +3 -1
- scitex/dsp/_detect_ripples.py +3 -1
- scitex/dsp/_modulation_index.py +3 -1
- scitex/dsp/_time.py +3 -1
- scitex/dsp/_wavelet.py +0 -1
- scitex/dsp/example.py +0 -5
- scitex/dsp/filt.py +4 -0
- scitex/dsp/utils/__init__.py +4 -1
- scitex/dsp/utils/pac.py +3 -3
- scitex/dt/_normalize_timestamp.py +4 -1
- scitex/errors.py +3 -6
- scitex/etc/__init__.py +1 -1
- scitex/gen/_DimHandler.py +6 -6
- scitex/gen/__init__.py +5 -1
- scitex/gen/_deprecated_close.py +1 -0
- scitex/gen/_deprecated_start.py +5 -3
- scitex/gen/_detect_environment.py +44 -41
- scitex/gen/_detect_notebook_path.py +51 -47
- scitex/gen/_embed.py +1 -1
- scitex/gen/_get_notebook_path.py +81 -62
- scitex/gen/_inspect_module.py +0 -1
- scitex/gen/_norm.py +16 -7
- scitex/gen/_norm_cache.py +78 -65
- scitex/gen/_print_config.py +0 -3
- scitex/gen/_src.py +2 -3
- scitex/gen/_title_case.py +3 -2
- scitex/gen/_to_even.py +8 -8
- scitex/gen/_transpose.py +3 -3
- scitex/gen/misc.py +0 -3
- scitex/gists/_SigMacro_processFigure_S.py +2 -2
- scitex/gists/_SigMacro_toBlue.py +2 -2
- scitex/gists/__init__.py +4 -1
- scitex/git/_branch.py +19 -11
- scitex/git/_clone.py +23 -15
- scitex/git/_commit.py +10 -12
- scitex/git/_init.py +15 -38
- scitex/git/_remote.py +9 -3
- scitex/git/_result.py +3 -0
- scitex/git/_retry.py +2 -5
- scitex/git/_types.py +4 -0
- scitex/git/_validation.py +8 -8
- scitex/git/_workflow.py +4 -4
- scitex/io/__init__.py +2 -1
- scitex/io/_glob.py +2 -2
- scitex/io/_json2md.py +3 -3
- scitex/io/_load.py +6 -8
- scitex/io/_load_cache.py +71 -71
- scitex/io/_load_configs.py +2 -3
- scitex/io/_load_modules/_H5Explorer.py +6 -12
- scitex/io/_load_modules/_ZarrExplorer.py +3 -3
- scitex/io/_load_modules/_bibtex.py +62 -63
- scitex/io/_load_modules/_canvas.py +4 -9
- scitex/io/_load_modules/_catboost.py +7 -2
- scitex/io/_load_modules/_hdf5.py +2 -0
- scitex/io/_load_modules/_image.py +5 -1
- scitex/io/_load_modules/_matlab.py +3 -1
- scitex/io/_load_modules/_optuna.py +0 -1
- scitex/io/_load_modules/_pdf.py +38 -29
- scitex/io/_load_modules/_sqlite3.py +1 -0
- scitex/io/_load_modules/_txt.py +2 -0
- scitex/io/_load_modules/_xml.py +9 -9
- scitex/io/_load_modules/_zarr.py +12 -10
- scitex/io/_metadata.py +76 -37
- scitex/io/_qr_utils.py +18 -13
- scitex/io/_save.py +220 -63
- scitex/io/_save_modules/__init__.py +7 -2
- scitex/io/_save_modules/_bibtex.py +66 -61
- scitex/io/_save_modules/_canvas.py +5 -6
- scitex/io/_save_modules/_catboost.py +2 -2
- scitex/io/_save_modules/_csv.py +4 -4
- scitex/io/_save_modules/_excel.py +5 -9
- scitex/io/_save_modules/_hdf5.py +9 -21
- scitex/io/_save_modules/_html.py +5 -5
- scitex/io/_save_modules/_image.py +105 -8
- scitex/io/_save_modules/_joblib.py +2 -2
- scitex/io/_save_modules/_json.py +51 -6
- scitex/io/_save_modules/_listed_dfs_as_csv.py +2 -1
- scitex/io/_save_modules/_listed_scalars_as_csv.py +2 -1
- scitex/io/_save_modules/_matlab.py +2 -2
- scitex/io/_save_modules/_numpy.py +6 -8
- scitex/io/_save_modules/_pickle.py +4 -4
- scitex/io/_save_modules/_plotly.py +3 -3
- scitex/io/_save_modules/_tex.py +23 -25
- scitex/io/_save_modules/_text.py +2 -2
- scitex/io/_save_modules/_yaml.py +9 -9
- scitex/io/_save_modules/_zarr.py +15 -15
- scitex/io/utils/__init__.py +2 -1
- scitex/io/utils/h5_to_zarr.py +173 -155
- scitex/linalg/__init__.py +1 -1
- scitex/linalg/_geometric_median.py +4 -3
- scitex/logging/_Tee.py +5 -7
- scitex/logging/__init__.py +18 -19
- scitex/logging/_config.py +4 -1
- scitex/logging/_context.py +6 -5
- scitex/logging/_formatters.py +2 -3
- scitex/logging/_handlers.py +19 -20
- scitex/logging/_levels.py +9 -17
- scitex/logging/_logger.py +74 -15
- scitex/logging/_print_capture.py +17 -17
- scitex/nn/_BNet.py +1 -3
- scitex/nn/_Filters.py +6 -2
- scitex/nn/_ModulationIndex.py +3 -1
- scitex/nn/_PAC.py +3 -2
- scitex/nn/_PSD.py +0 -1
- scitex/nn/__init__.py +16 -3
- scitex/path/_clean.py +10 -8
- scitex/path/_find.py +1 -1
- scitex/path/_get_spath.py +1 -2
- scitex/path/_mk_spath.py +1 -1
- scitex/path/_symlink.py +5 -10
- scitex/pd/__init__.py +4 -1
- scitex/pd/_force_df.py +24 -24
- scitex/pd/_get_unique.py +1 -0
- scitex/pd/_merge_columns.py +1 -1
- scitex/pd/_round.py +11 -7
- scitex/pd/_to_xy.py +0 -1
- scitex/plt/REQUESTS.md +191 -0
- scitex/plt/__init__.py +185 -87
- scitex/plt/_subplots/_AxesWrapper.py +22 -6
- scitex/plt/_subplots/_AxisWrapper.py +100 -39
- scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +74 -52
- scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +183 -73
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +61 -45
- scitex/plt/_subplots/_AxisWrapperMixins/_TrackingMixin.py +26 -14
- scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +80 -73
- scitex/plt/_subplots/_FigWrapper.py +93 -60
- scitex/plt/_subplots/_SubplotsWrapper.py +135 -68
- scitex/plt/_subplots/__init__.py +10 -0
- scitex/plt/_subplots/_export_as_csv.py +89 -47
- scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +1 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +6 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_bar.py +88 -38
- scitex/plt/_subplots/_export_as_csv_formatters/_format_barh.py +25 -31
- scitex/plt/_subplots/_export_as_csv_formatters/_format_boxplot.py +53 -23
- scitex/plt/_subplots/_export_as_csv_formatters/_format_contour.py +38 -25
- scitex/plt/_subplots/_export_as_csv_formatters/_format_contourf.py +17 -9
- scitex/plt/_subplots/_export_as_csv_formatters/_format_errorbar.py +70 -124
- scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +12 -10
- scitex/plt/_subplots/_export_as_csv_formatters/_format_fill.py +31 -17
- scitex/plt/_subplots/_export_as_csv_formatters/_format_fill_between.py +33 -21
- scitex/plt/_subplots/_export_as_csv_formatters/_format_hexbin.py +14 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_hist.py +43 -29
- scitex/plt/_subplots/_export_as_csv_formatters/_format_hist2d.py +14 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +27 -11
- scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +7 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +9 -7
- scitex/plt/_subplots/_export_as_csv_formatters/_format_pie.py +15 -6
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +85 -46
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +52 -27
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +1 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +16 -17
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +7 -5
- scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -8
- scitex/plt/_subplots/_export_as_csv_formatters/_format_scatter.py +17 -6
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_barplot.py +43 -26
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_boxplot.py +68 -47
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_heatmap.py +52 -64
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_histplot.py +55 -50
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +9 -11
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_kdeplot.py +63 -29
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +4 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +6 -4
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_scatterplot.py +44 -40
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_stripplot.py +46 -39
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_swarmplot.py +46 -39
- scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_violinplot.py +75 -94
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stem.py +12 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_step.py +12 -3
- scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +10 -8
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +17 -15
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +10 -9
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_fillv.py +35 -31
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +18 -18
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +24 -18
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +9 -7
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_line.py +34 -23
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +15 -13
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +12 -10
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +15 -13
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +11 -9
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_rectangle.py +84 -56
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +35 -32
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_shaded_line.py +46 -30
- scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_violin.py +51 -51
- scitex/plt/_subplots/_export_as_csv_formatters/_format_text.py +32 -31
- scitex/plt/_subplots/_export_as_csv_formatters/_format_violin.py +34 -31
- scitex/plt/_subplots/_export_as_csv_formatters/_format_violinplot.py +44 -37
- scitex/plt/_subplots/_export_as_csv_formatters/verify_formatters.py +91 -74
- scitex/plt/_tpl.py +6 -5
- scitex/plt/ax/_plot/__init__.py +24 -0
- scitex/plt/ax/_plot/_add_fitted_line.py +12 -11
- scitex/plt/ax/_plot/_plot_circular_hist.py +3 -1
- scitex/plt/ax/_plot/_plot_statistical_shaded_line.py +25 -19
- scitex/plt/ax/_plot/_stx_conf_mat.py +6 -3
- scitex/plt/ax/_plot/_stx_ecdf.py +5 -3
- scitex/plt/ax/_plot/_stx_fillv.py +4 -2
- scitex/plt/ax/_plot/_stx_heatmap.py +7 -4
- scitex/plt/ax/_plot/_stx_image.py +7 -5
- scitex/plt/ax/_plot/_stx_joyplot.py +32 -10
- scitex/plt/ax/_plot/_stx_raster.py +26 -11
- scitex/plt/ax/_plot/_stx_rectangle.py +2 -2
- scitex/plt/ax/_plot/_stx_shaded_line.py +15 -11
- scitex/plt/ax/_plot/_stx_violin.py +3 -1
- scitex/plt/ax/_style/_add_marginal_ax.py +6 -4
- scitex/plt/ax/_style/_auto_scale_axis.py +14 -10
- scitex/plt/ax/_style/_extend.py +3 -1
- scitex/plt/ax/_style/_force_aspect.py +5 -3
- scitex/plt/ax/_style/_format_units.py +2 -2
- scitex/plt/ax/_style/_hide_spines.py +5 -1
- scitex/plt/ax/_style/_map_ticks.py +5 -3
- scitex/plt/ax/_style/_rotate_labels.py +5 -4
- scitex/plt/ax/_style/_rotate_labels_v01.py +73 -63
- scitex/plt/ax/_style/_set_log_scale.py +120 -85
- scitex/plt/ax/_style/_set_meta.py +99 -76
- scitex/plt/ax/_style/_set_supxyt.py +33 -16
- scitex/plt/ax/_style/_set_xyt.py +27 -18
- scitex/plt/ax/_style/_share_axes.py +15 -5
- scitex/plt/ax/_style/_show_spines.py +58 -57
- scitex/plt/ax/_style/_style_barplot.py +1 -1
- scitex/plt/ax/_style/_style_boxplot.py +25 -14
- scitex/plt/ax/_style/_style_errorbar.py +0 -0
- scitex/plt/ax/_style/_style_scatter.py +1 -1
- scitex/plt/ax/_style/_style_suptitles.py +3 -3
- scitex/plt/ax/_style/_style_violinplot.py +8 -2
- scitex/plt/color/__init__.py +34 -2
- scitex/plt/color/_add_hue_col.py +1 -0
- scitex/plt/color/_colors.py +0 -1
- scitex/plt/color/_get_colors_from_conf_matap.py +3 -1
- scitex/plt/color/_vizualize_colors.py +0 -1
- scitex/plt/docs/FIGURE_ARCHITECTURE.md +155 -97
- scitex/plt/gallery/README.md +75 -0
- scitex/plt/gallery/__init__.py +29 -0
- scitex/plt/gallery/_generate.py +153 -0
- scitex/plt/gallery/_plots.py +594 -0
- scitex/plt/gallery/_registry.py +153 -0
- scitex/plt/styles/__init__.py +9 -9
- scitex/plt/styles/_plot_defaults.py +62 -61
- scitex/plt/styles/_plot_postprocess.py +126 -77
- scitex/plt/styles/_style_loader.py +0 -0
- scitex/plt/styles/presets.py +43 -18
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +110 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +88 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +103 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +106 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +83 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +93 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +91 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +91 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +91 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +97 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +98 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +101 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +96 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +93 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +83 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +121 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +110 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +96 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +95 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +92 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +94 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +109 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +108 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +93 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +84 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +96 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +96 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +96 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +99 -0
- scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +100 -0
- scitex/plt/utils/__init__.py +29 -2
- scitex/plt/utils/_close.py +8 -3
- scitex/plt/utils/_collect_figure_metadata.py +3031 -265
- scitex/plt/utils/_colorbar.py +15 -17
- scitex/plt/utils/_configure_mpl.py +22 -14
- scitex/plt/utils/_crop.py +60 -27
- scitex/plt/utils/_csv_column_naming.py +123 -72
- scitex/plt/utils/_dimension_viewer.py +7 -19
- scitex/plt/utils/_figure_from_axes_mm.py +70 -16
- scitex/plt/utils/_figure_mm.py +3 -2
- scitex/plt/utils/_get_actual_font.py +5 -4
- scitex/plt/utils/_histogram_utils.py +52 -48
- scitex/plt/utils/_is_valid_axis.py +19 -13
- scitex/plt/utils/_mk_colorbar.py +3 -3
- scitex/plt/utils/_scientific_captions.py +202 -139
- scitex/plt/utils/_scitex_config.py +98 -98
- scitex/plt/utils/_units.py +0 -0
- scitex/plt/utils/metadata/__init__.py +36 -0
- scitex/plt/utils/metadata/_artist_extraction.py +119 -0
- scitex/plt/utils/metadata/_axes_metadata.py +93 -0
- scitex/plt/utils/metadata/_collection_artists.py +292 -0
- scitex/plt/utils/metadata/_core.py +208 -0
- scitex/plt/utils/metadata/_csv_column_extraction.py +186 -0
- scitex/plt/utils/metadata/_csv_hash.py +115 -0
- scitex/plt/utils/metadata/_csv_verification.py +95 -0
- scitex/plt/utils/metadata/_data_linkage.py +263 -0
- scitex/plt/utils/metadata/_dimensions.py +239 -0
- scitex/plt/utils/metadata/_figure_metadata.py +58 -0
- scitex/plt/utils/metadata/_image_text_artists.py +168 -0
- scitex/plt/utils/metadata/_label_parsing.py +82 -0
- scitex/plt/utils/metadata/_legend_extraction.py +120 -0
- scitex/plt/utils/metadata/_line_artists.py +367 -0
- scitex/plt/utils/metadata/_line_semantic_handling.py +173 -0
- scitex/plt/utils/metadata/_patch_artists.py +211 -0
- scitex/plt/utils/metadata/_plot_content.py +26 -0
- scitex/plt/utils/metadata/_plot_type_detection.py +184 -0
- scitex/plt/utils/metadata/_precision.py +134 -0
- scitex/plt/utils/metadata/_precision_config.py +68 -0
- scitex/plt/utils/metadata/_precision_sections.py +211 -0
- scitex/plt/utils/metadata/_recipe_extraction.py +267 -0
- scitex/plt/utils/metadata/_style_parsing.py +174 -0
- scitex/repro/_RandomStateManager.py +33 -38
- scitex/repro/__init__.py +16 -7
- scitex/repro/_gen_ID.py +7 -9
- scitex/repro/_gen_timestamp.py +7 -6
- scitex/repro/_hash_array.py +8 -12
- scitex/reproduce/__init__.py +1 -1
- scitex/resource/_get_processor_usages.py +3 -1
- scitex/resource/_log_processor_usages.py +3 -1
- scitex/rng/__init__.py +1 -1
- scitex/schema/README.md +178 -0
- scitex/schema/__init__.py +144 -0
- scitex/schema/_canvas.py +444 -0
- scitex/schema/_stats.py +762 -0
- scitex/schema/_validation.py +590 -0
- scitex/scholar/.legacy/Scholar.py +5 -12
- scitex/scholar/.legacy/_Scholar.py +66 -99
- scitex/scholar/.legacy/_ScholarAPI.py +75 -66
- scitex/scholar/.legacy/_tmp/search_engine/_BaseSearchEngine.py +3 -3
- scitex/scholar/.legacy/_tmp/search_engine/_UnifiedSearcher.py +4 -9
- scitex/scholar/.legacy/_tmp/search_engine/__init__.py +14 -21
- scitex/scholar/.legacy/_tmp/search_engine/local/_LocalSearchEngine.py +40 -37
- scitex/scholar/.legacy/_tmp/search_engine/local/_VectorSearchEngine.py +31 -28
- scitex/scholar/.legacy/_tmp/search_engine/web/_ArxivSearchEngine.py +74 -65
- scitex/scholar/.legacy/_tmp/search_engine/web/_CrossRefSearchEngine.py +122 -116
- scitex/scholar/.legacy/_tmp/search_engine/web/_GoogleScholarSearchEngine.py +65 -59
- scitex/scholar/.legacy/_tmp/search_engine/web/_PubMedSearchEngine.py +121 -107
- scitex/scholar/.legacy/_tmp/search_engine/web/_SemanticScholarSearchEngine.py +5 -12
- scitex/scholar/.legacy/database/_DatabaseEntry.py +49 -45
- scitex/scholar/.legacy/database/_DatabaseIndex.py +131 -94
- scitex/scholar/.legacy/database/_LibraryManager.py +65 -63
- scitex/scholar/.legacy/database/_PaperDatabase.py +138 -124
- scitex/scholar/.legacy/database/_ScholarDatabaseIntegration.py +14 -36
- scitex/scholar/.legacy/database/_StorageIntegratedDB.py +192 -156
- scitex/scholar/.legacy/database/_ZoteroCompatibleDB.py +300 -237
- scitex/scholar/.legacy/database/__init__.py +2 -1
- scitex/scholar/.legacy/database/manage.py +92 -84
- scitex/scholar/.legacy/lookup/_LookupIndex.py +157 -101
- scitex/scholar/.legacy/lookup/__init__.py +2 -1
- scitex/scholar/.legacy/metadata/doi/batch/_MetadataHandlerForBatchDOIResolution.py +4 -9
- scitex/scholar/.legacy/metadata/doi/batch/_ProgressManagerForBatchDOIResolution.py +10 -23
- scitex/scholar/.legacy/metadata/doi/batch/_SourceStatsManagerForBatchDOIResolution.py +4 -9
- scitex/scholar/.legacy/metadata/doi/batch/__init__.py +3 -1
- scitex/scholar/.legacy/metadata/doi/resolvers/_BatchDOIResolver.py +10 -25
- scitex/scholar/.legacy/metadata/doi/resolvers/_BibTeXDOIResolver.py +19 -49
- scitex/scholar/.legacy/metadata/doi/resolvers/_DOIResolver.py +1 -0
- scitex/scholar/.legacy/metadata/doi/resolvers/_SingleDOIResolver.py +8 -20
- scitex/scholar/.legacy/metadata/doi/sources/.combined-SemanticScholarSource/_SemanticScholarSource.py +37 -35
- scitex/scholar/.legacy/metadata/doi/sources/.combined-SemanticScholarSource/_SemanticScholarSourceEnhanced.py +49 -37
- scitex/scholar/.legacy/metadata/doi/sources/_ArXivSource.py +11 -30
- scitex/scholar/.legacy/metadata/doi/sources/_BaseDOISource.py +19 -47
- scitex/scholar/.legacy/metadata/doi/sources/_CrossRefLocalSource.py +1 -0
- scitex/scholar/.legacy/metadata/doi/sources/_CrossRefSource.py +12 -33
- scitex/scholar/.legacy/metadata/doi/sources/_OpenAlexSource.py +8 -20
- scitex/scholar/.legacy/metadata/doi/sources/_PubMedSource.py +10 -27
- scitex/scholar/.legacy/metadata/doi/sources/_SemanticScholarSource.py +11 -29
- scitex/scholar/.legacy/metadata/doi/sources/_SourceManager.py +8 -21
- scitex/scholar/.legacy/metadata/doi/sources/_SourceResolutionStrategy.py +24 -55
- scitex/scholar/.legacy/metadata/doi/sources/_SourceRotationManager.py +8 -21
- scitex/scholar/.legacy/metadata/doi/sources/_URLDOISource.py +9 -16
- scitex/scholar/.legacy/metadata/doi/sources/_UnifiedSource.py +8 -22
- scitex/scholar/.legacy/metadata/doi/sources/__init__.py +1 -0
- scitex/scholar/.legacy/metadata/doi/utils/_PubMedConverter.py +4 -8
- scitex/scholar/.legacy/metadata/doi/utils/_RateLimitHandler.py +17 -43
- scitex/scholar/.legacy/metadata/doi/utils/_TextNormalizer.py +8 -18
- scitex/scholar/.legacy/metadata/doi/utils/_URLDOIExtractor.py +4 -8
- scitex/scholar/.legacy/metadata/doi/utils/__init__.py +1 -0
- scitex/scholar/.legacy/metadata/doi/utils/_to_complete_metadata_structure.py +1 -0
- scitex/scholar/.legacy/metadata/enrichment/_LibraryEnricher.py +2 -3
- scitex/scholar/.legacy/metadata/enrichment/enrichers/_ImpactFactorEnricher.py +6 -12
- scitex/scholar/.legacy/metadata/enrichment/enrichers/_SmartEnricher.py +5 -10
- scitex/scholar/.legacy/metadata/enrichment/sources/_UnifiedMetadataSource.py +4 -5
- scitex/scholar/.legacy/metadata/query_to_full_meta_json.py +8 -12
- scitex/scholar/.legacy/metadata/urls/_URLMetadataHandler.py +3 -3
- scitex/scholar/.legacy/metadata/urls/_ZoteroTranslatorRunner.py +15 -21
- scitex/scholar/.legacy/metadata/urls/__init__.py +3 -3
- scitex/scholar/.legacy/metadata/urls/_finder.py +4 -6
- scitex/scholar/.legacy/metadata/urls/_handler.py +7 -15
- scitex/scholar/.legacy/metadata/urls/_resolver.py +6 -12
- scitex/scholar/.legacy/search/_Embedder.py +74 -69
- scitex/scholar/.legacy/search/_SemanticSearch.py +91 -90
- scitex/scholar/.legacy/search/_SemanticSearchEngine.py +104 -109
- scitex/scholar/.legacy/search/_UnifiedSearcher.py +530 -471
- scitex/scholar/.legacy/search/_VectorDatabase.py +111 -92
- scitex/scholar/.legacy/search/__init__.py +1 -0
- scitex/scholar/.legacy/storage/_EnhancedStorageManager.py +182 -154
- scitex/scholar/.legacy/storage/__init__.py +2 -1
- scitex/scholar/__init__.py +0 -2
- scitex/scholar/__main__.py +1 -3
- scitex/scholar/auth/ScholarAuthManager.py +13 -36
- scitex/scholar/auth/core/AuthenticationGateway.py +15 -29
- scitex/scholar/auth/core/BrowserAuthenticator.py +22 -57
- scitex/scholar/auth/core/StrategyResolver.py +10 -27
- scitex/scholar/auth/core/__init__.py +5 -1
- scitex/scholar/auth/gateway/_OpenURLLinkFinder.py +11 -21
- scitex/scholar/auth/gateway/_OpenURLResolver.py +10 -18
- scitex/scholar/auth/gateway/_resolve_functions.py +3 -3
- scitex/scholar/auth/providers/BaseAuthenticator.py +1 -0
- scitex/scholar/auth/providers/EZProxyAuthenticator.py +7 -14
- scitex/scholar/auth/providers/OpenAthensAuthenticator.py +29 -57
- scitex/scholar/auth/providers/ShibbolethAuthenticator.py +87 -73
- scitex/scholar/auth/session/AuthCacheManager.py +12 -22
- scitex/scholar/auth/session/SessionManager.py +4 -6
- scitex/scholar/auth/sso/BaseSSOAutomator.py +13 -19
- scitex/scholar/auth/sso/OpenAthensSSOAutomator.py +16 -45
- scitex/scholar/auth/sso/SSOAutomator.py +8 -15
- scitex/scholar/auth/sso/UniversityOfMelbourneSSOAutomator.py +13 -23
- scitex/scholar/browser/ScholarBrowserManager.py +31 -56
- scitex/scholar/browser/__init__.py +1 -0
- scitex/scholar/browser/utils/click_and_wait.py +3 -4
- scitex/scholar/browser/utils/close_unwanted_pages.py +4 -7
- scitex/scholar/browser/utils/wait_redirects.py +15 -40
- scitex/scholar/citation_graph/__init__.py +0 -0
- scitex/scholar/citation_graph/builder.py +3 -7
- scitex/scholar/citation_graph/database.py +4 -11
- scitex/scholar/citation_graph/example.py +5 -10
- scitex/scholar/citation_graph/models.py +0 -0
- scitex/scholar/cli/_url_utils.py +1 -1
- scitex/scholar/cli/chrome.py +5 -3
- scitex/scholar/cli/download_pdf.py +13 -14
- scitex/scholar/cli/handlers/bibtex_handler.py +4 -12
- scitex/scholar/cli/handlers/doi_handler.py +1 -3
- scitex/scholar/cli/handlers/project_handler.py +6 -20
- scitex/scholar/cli/open_browser.py +41 -39
- scitex/scholar/cli/open_browser_auto.py +31 -39
- scitex/scholar/cli/open_browser_monitored.py +27 -24
- scitex/scholar/config/ScholarConfig.py +5 -8
- scitex/scholar/config/__init__.py +1 -0
- scitex/scholar/config/core/_CascadeConfig.py +3 -3
- scitex/scholar/config/core/_PathManager.py +16 -28
- scitex/scholar/core/Paper.py +79 -78
- scitex/scholar/core/Papers.py +16 -27
- scitex/scholar/core/Scholar.py +98 -229
- scitex/scholar/core/journal_normalizer.py +52 -49
- scitex/scholar/core/oa_cache.py +27 -23
- scitex/scholar/core/open_access.py +17 -8
- scitex/scholar/docs/template.py +4 -3
- scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/clf_svm.py +0 -0
- scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/download.py +0 -0
- scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_conf_mat.py +0 -0
- scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_digits.py +0 -0
- scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_umap_space.py +0 -0
- scitex/scholar/examples/00_config.py +10 -9
- scitex/scholar/examples/01_auth.py +3 -0
- scitex/scholar/examples/02_browser.py +14 -10
- scitex/scholar/examples/03_01-engine.py +3 -0
- scitex/scholar/examples/03_02-engine-for-bibtex.py +4 -3
- scitex/scholar/examples/04_01-url.py +9 -9
- scitex/scholar/examples/04_02-url-for-bibtex.py +7 -3
- scitex/scholar/examples/04_02-url-for-dois.py +87 -97
- scitex/scholar/examples/05_download_pdf.py +10 -4
- scitex/scholar/examples/06_find_and_download.py +6 -6
- scitex/scholar/examples/06_parse_bibtex.py +17 -17
- scitex/scholar/examples/07_storage_integration.py +6 -9
- scitex/scholar/examples/99_fullpipeline-for-bibtex.py +14 -15
- scitex/scholar/examples/99_fullpipeline-for-one-entry.py +31 -23
- scitex/scholar/examples/99_maintenance.py +3 -0
- scitex/scholar/examples/dev.py +2 -3
- scitex/scholar/examples/zotero_integration.py +11 -18
- scitex/scholar/impact_factor/ImpactFactorEngine.py +7 -9
- scitex/scholar/impact_factor/estimation/__init__.py +4 -4
- scitex/scholar/impact_factor/estimation/core/__init__.py +3 -7
- scitex/scholar/impact_factor/estimation/core/cache_manager.py +223 -211
- scitex/scholar/impact_factor/estimation/core/calculator.py +165 -131
- scitex/scholar/impact_factor/estimation/core/journal_matcher.py +217 -172
- scitex/scholar/impact_factor/jcr/ImpactFactorJCREngine.py +6 -14
- scitex/scholar/impact_factor/jcr/build_database.py +4 -3
- scitex/scholar/integration/base.py +9 -17
- scitex/scholar/integration/mendeley/exporter.py +2 -4
- scitex/scholar/integration/mendeley/importer.py +3 -3
- scitex/scholar/integration/mendeley/linker.py +3 -3
- scitex/scholar/integration/mendeley/mapper.py +9 -6
- scitex/scholar/integration/zotero/__main__.py +26 -43
- scitex/scholar/integration/zotero/exporter.py +15 -11
- scitex/scholar/integration/zotero/importer.py +12 -10
- scitex/scholar/integration/zotero/linker.py +8 -12
- scitex/scholar/integration/zotero/mapper.py +17 -12
- scitex/scholar/metadata_engines/.combined-SemanticScholarSource/_SemanticScholarSource.py +37 -35
- scitex/scholar/metadata_engines/.combined-SemanticScholarSource/_SemanticScholarSourceEnhanced.py +47 -35
- scitex/scholar/metadata_engines/ScholarEngine.py +21 -43
- scitex/scholar/metadata_engines/__init__.py +1 -0
- scitex/scholar/metadata_engines/individual/ArXivEngine.py +15 -37
- scitex/scholar/metadata_engines/individual/CrossRefEngine.py +15 -42
- scitex/scholar/metadata_engines/individual/CrossRefLocalEngine.py +24 -45
- scitex/scholar/metadata_engines/individual/OpenAlexEngine.py +11 -21
- scitex/scholar/metadata_engines/individual/PubMedEngine.py +10 -27
- scitex/scholar/metadata_engines/individual/SemanticScholarEngine.py +28 -35
- scitex/scholar/metadata_engines/individual/URLDOIEngine.py +11 -22
- scitex/scholar/metadata_engines/individual/_BaseDOIEngine.py +20 -49
- scitex/scholar/metadata_engines/utils/_PubMedConverter.py +4 -8
- scitex/scholar/metadata_engines/utils/_URLDOIExtractor.py +5 -10
- scitex/scholar/metadata_engines/utils/__init__.py +2 -0
- scitex/scholar/metadata_engines/utils/_metadata2bibtex.py +3 -0
- scitex/scholar/metadata_engines/utils/_standardize_metadata.py +2 -3
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +25 -37
- scitex/scholar/pdf_download/strategies/chrome_pdf_viewer.py +11 -19
- scitex/scholar/pdf_download/strategies/direct_download.py +5 -9
- scitex/scholar/pdf_download/strategies/manual_download_fallback.py +3 -3
- scitex/scholar/pdf_download/strategies/manual_download_utils.py +6 -13
- scitex/scholar/pdf_download/strategies/open_access_download.py +49 -31
- scitex/scholar/pdf_download/strategies/response_body.py +8 -19
- scitex/scholar/pipelines/ScholarPipelineBibTeX.py +9 -18
- scitex/scholar/pipelines/ScholarPipelineMetadataParallel.py +25 -26
- scitex/scholar/pipelines/ScholarPipelineMetadataSingle.py +62 -23
- scitex/scholar/pipelines/ScholarPipelineParallel.py +13 -30
- scitex/scholar/pipelines/ScholarPipelineSearchParallel.py +299 -220
- scitex/scholar/pipelines/ScholarPipelineSearchSingle.py +202 -165
- scitex/scholar/pipelines/ScholarPipelineSingle.py +25 -51
- scitex/scholar/pipelines/SearchQueryParser.py +55 -55
- scitex/scholar/search_engines/ScholarSearchEngine.py +31 -27
- scitex/scholar/search_engines/_BaseSearchEngine.py +20 -23
- scitex/scholar/search_engines/individual/ArXivSearchEngine.py +53 -35
- scitex/scholar/search_engines/individual/CrossRefSearchEngine.py +47 -40
- scitex/scholar/search_engines/individual/OpenAlexSearchEngine.py +55 -50
- scitex/scholar/search_engines/individual/PubMedSearchEngine.py +8 -10
- scitex/scholar/search_engines/individual/SemanticScholarSearchEngine.py +55 -49
- scitex/scholar/storage/BibTeXHandler.py +150 -95
- scitex/scholar/storage/PaperIO.py +3 -6
- scitex/scholar/storage/ScholarLibrary.py +70 -49
- scitex/scholar/storage/_DeduplicationManager.py +52 -25
- scitex/scholar/storage/_LibraryCacheManager.py +19 -46
- scitex/scholar/storage/_LibraryManager.py +65 -175
- scitex/scholar/url_finder/ScholarURLFinder.py +9 -25
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_direct_links.py +1 -1
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_href.py +6 -10
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_navigation.py +4 -6
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_publisher_patterns.py +8 -15
- scitex/scholar/url_finder/strategies/find_pdf_urls_by_zotero_translators.py +3 -3
- scitex/scholar/url_finder/strategies/find_supplementary_urls_by_href.py +3 -3
- scitex/scholar/url_finder/translators/core/patterns.py +6 -4
- scitex/scholar/url_finder/translators/core/registry.py +6 -9
- scitex/scholar/url_finder/translators/individual/BOFiP_Impots.py +60 -52
- scitex/scholar/url_finder/translators/individual/Baidu_Scholar.py +54 -62
- scitex/scholar/url_finder/translators/individual/Bangkok_Post.py +38 -44
- scitex/scholar/url_finder/translators/individual/Baruch_Foundation.py +43 -47
- scitex/scholar/url_finder/translators/individual/Beobachter.py +46 -50
- scitex/scholar/url_finder/translators/individual/Bezneng_Gajit.py +37 -41
- scitex/scholar/url_finder/translators/individual/BibLaTeX.py +59 -52
- scitex/scholar/url_finder/translators/individual/BibTeX.py +83 -79
- scitex/scholar/url_finder/translators/individual/Biblio_com.py +48 -51
- scitex/scholar/url_finder/translators/individual/Bibliontology_RDF.py +58 -56
- scitex/scholar/url_finder/translators/individual/Camara_Brasileira_do_Livro_ISBN.py +102 -99
- scitex/scholar/url_finder/translators/individual/CanLII.py +49 -43
- scitex/scholar/url_finder/translators/individual/Canada_com.py +36 -40
- scitex/scholar/url_finder/translators/individual/Canadian_Letters_and_Images.py +43 -43
- scitex/scholar/url_finder/translators/individual/Canadiana_ca.py +77 -66
- scitex/scholar/url_finder/translators/individual/Cascadilla_Proceedings_Project.py +68 -62
- scitex/scholar/url_finder/translators/individual/Central_and_Eastern_European_Online_Library_Journals.py +60 -60
- scitex/scholar/url_finder/translators/individual/Champlain_Society_Collection.py +63 -61
- scitex/scholar/url_finder/translators/individual/Chicago_Journal_of_Theoretical_Computer_Science.py +74 -58
- scitex/scholar/url_finder/translators/individual/Christian_Science_Monitor.py +32 -38
- scitex/scholar/url_finder/translators/individual/Columbia_University_Press.py +51 -47
- scitex/scholar/url_finder/translators/individual/Common_Place.py +66 -57
- scitex/scholar/url_finder/translators/individual/Cornell_LII.py +66 -62
- scitex/scholar/url_finder/translators/individual/Cornell_University_Press.py +38 -45
- scitex/scholar/url_finder/translators/individual/CourtListener.py +52 -56
- scitex/scholar/url_finder/translators/individual/DAI_Zenon.py +53 -54
- scitex/scholar/url_finder/translators/individual/access_medicine.py +27 -33
- scitex/scholar/url_finder/translators/individual/acm.py +1 -1
- scitex/scholar/url_finder/translators/individual/acm_digital_library.py +93 -63
- scitex/scholar/url_finder/translators/individual/airiti.py +3 -1
- scitex/scholar/url_finder/translators/individual/aosic.py +3 -1
- scitex/scholar/url_finder/translators/individual/archive_ouverte_aosic.py +3 -1
- scitex/scholar/url_finder/translators/individual/archive_ouverte_en_sciences_de_l_information_et_de_la_communication___aosic_.py +6 -2
- scitex/scholar/url_finder/translators/individual/artforum.py +35 -27
- scitex/scholar/url_finder/translators/individual/arxiv.py +1 -1
- scitex/scholar/url_finder/translators/individual/arxiv_org.py +8 -4
- scitex/scholar/url_finder/translators/individual/atlanta_journal_constitution.py +22 -18
- scitex/scholar/url_finder/translators/individual/atypon_journals.py +19 -11
- scitex/scholar/url_finder/translators/individual/austlii_and_nzlii.py +48 -44
- scitex/scholar/url_finder/translators/individual/australian_dictionary_of_biography.py +21 -17
- scitex/scholar/url_finder/translators/individual/bailii.py +22 -19
- scitex/scholar/url_finder/translators/individual/bbc.py +46 -42
- scitex/scholar/url_finder/translators/individual/bbc_genome.py +37 -25
- scitex/scholar/url_finder/translators/individual/biblioteca_nacional_de_maestros.py +24 -20
- scitex/scholar/url_finder/translators/individual/bibliotheque_archives_nationale_quebec_pistard.py +42 -43
- scitex/scholar/url_finder/translators/individual/bibliotheque_archives_nationales_quebec.py +87 -81
- scitex/scholar/url_finder/translators/individual/bibliotheque_nationale_france.py +39 -37
- scitex/scholar/url_finder/translators/individual/bibsys.py +32 -28
- scitex/scholar/url_finder/translators/individual/bioconductor.py +58 -52
- scitex/scholar/url_finder/translators/individual/biomed_central.py +23 -15
- scitex/scholar/url_finder/translators/individual/biorxiv.py +26 -13
- scitex/scholar/url_finder/translators/individual/blogger.py +39 -43
- scitex/scholar/url_finder/translators/individual/bloomberg.py +48 -52
- scitex/scholar/url_finder/translators/individual/bloomsbury_food_library.py +37 -37
- scitex/scholar/url_finder/translators/individual/bluesky.py +30 -28
- scitex/scholar/url_finder/translators/individual/bnf_isbn.py +1 -1
- scitex/scholar/url_finder/translators/individual/bocc.py +66 -60
- scitex/scholar/url_finder/translators/individual/boe.py +52 -52
- scitex/scholar/url_finder/translators/individual/brill.py +3 -1
- scitex/scholar/url_finder/translators/individual/business_standard.py +36 -38
- scitex/scholar/url_finder/translators/individual/cabi_cab_abstracts.py +39 -41
- scitex/scholar/url_finder/translators/individual/cambridge.py +3 -1
- scitex/scholar/url_finder/translators/individual/cambridge_core.py +30 -24
- scitex/scholar/url_finder/translators/individual/caod.py +50 -46
- scitex/scholar/url_finder/translators/individual/cbc.py +91 -67
- scitex/scholar/url_finder/translators/individual/ccfr_bnf.py +49 -53
- scitex/scholar/url_finder/translators/individual/cia_world_factbook.py +43 -33
- scitex/scholar/url_finder/translators/individual/crossref_rest.py +208 -174
- scitex/scholar/url_finder/translators/individual/current_affairs.py +29 -35
- scitex/scholar/url_finder/translators/individual/dabi.py +70 -66
- scitex/scholar/url_finder/translators/individual/dagens_nyheter.py +3 -1
- scitex/scholar/url_finder/translators/individual/dagstuhl.py +10 -15
- scitex/scholar/url_finder/translators/individual/dar_almandumah.py +13 -9
- scitex/scholar/url_finder/translators/individual/dart_europe.py +19 -22
- scitex/scholar/url_finder/translators/individual/data_gov.py +2 -2
- scitex/scholar/url_finder/translators/individual/databrary.py +27 -28
- scitex/scholar/url_finder/translators/individual/datacite_json.py +152 -137
- scitex/scholar/url_finder/translators/individual/dataverse.py +68 -64
- scitex/scholar/url_finder/translators/individual/daum_news.py +38 -38
- scitex/scholar/url_finder/translators/individual/dblp.py +4 -8
- scitex/scholar/url_finder/translators/individual/dblp_computer_science_bibliography.py +8 -3
- scitex/scholar/url_finder/translators/individual/dbpia.py +5 -3
- scitex/scholar/url_finder/translators/individual/defense_technical_information_center.py +30 -28
- scitex/scholar/url_finder/translators/individual/delpher.py +102 -79
- scitex/scholar/url_finder/translators/individual/demographic_research.py +35 -31
- scitex/scholar/url_finder/translators/individual/denik_cz.py +58 -54
- scitex/scholar/url_finder/translators/individual/depatisnet.py +7 -10
- scitex/scholar/url_finder/translators/individual/der_freitag.py +81 -66
- scitex/scholar/url_finder/translators/individual/der_spiegel.py +56 -54
- scitex/scholar/url_finder/translators/individual/digibib_net.py +3 -1
- scitex/scholar/url_finder/translators/individual/digizeitschriften.py +3 -1
- scitex/scholar/url_finder/translators/individual/dpla.py +13 -14
- scitex/scholar/url_finder/translators/individual/dspace.py +2 -2
- scitex/scholar/url_finder/translators/individual/ebrary.py +3 -1
- scitex/scholar/url_finder/translators/individual/ebscohost.py +3 -1
- scitex/scholar/url_finder/translators/individual/electronic_colloquium_on_computational_complexity.py +3 -1
- scitex/scholar/url_finder/translators/individual/elife.py +3 -1
- scitex/scholar/url_finder/translators/individual/elsevier_health_journals.py +3 -1
- scitex/scholar/url_finder/translators/individual/emerald.py +3 -1
- scitex/scholar/url_finder/translators/individual/emerald_insight.py +3 -1
- scitex/scholar/url_finder/translators/individual/epicurious.py +3 -1
- scitex/scholar/url_finder/translators/individual/eurogamerusgamer.py +3 -1
- scitex/scholar/url_finder/translators/individual/fachportal_padagogik.py +3 -1
- scitex/scholar/url_finder/translators/individual/frontiers.py +1 -1
- scitex/scholar/url_finder/translators/individual/gale_databases.py +3 -1
- scitex/scholar/url_finder/translators/individual/gms_german_medical_science.py +6 -2
- scitex/scholar/url_finder/translators/individual/ieee_computer_society.py +6 -2
- scitex/scholar/url_finder/translators/individual/ieee_xplore.py +41 -35
- scitex/scholar/url_finder/translators/individual/inter_research_science_center.py +6 -2
- scitex/scholar/url_finder/translators/individual/jisc_historical_texts.py +3 -1
- scitex/scholar/url_finder/translators/individual/jstor.py +14 -12
- scitex/scholar/url_finder/translators/individual/korean_national_library.py +3 -1
- scitex/scholar/url_finder/translators/individual/la_times.py +3 -1
- scitex/scholar/url_finder/translators/individual/landesbibliographie_baden_wurttemberg.py +3 -1
- scitex/scholar/url_finder/translators/individual/legislative_insight.py +3 -1
- scitex/scholar/url_finder/translators/individual/libraries_tasmania.py +3 -1
- scitex/scholar/url_finder/translators/individual/library_catalog__koha_.py +3 -1
- scitex/scholar/url_finder/translators/individual/lingbuzz.py +2 -2
- scitex/scholar/url_finder/translators/individual/max_planck_institute_for_the_history_of_science_virtual_laboratory_library.py +3 -1
- scitex/scholar/url_finder/translators/individual/mdpi.py +12 -6
- scitex/scholar/url_finder/translators/individual/microbiology_society_journals.py +3 -1
- scitex/scholar/url_finder/translators/individual/midas_journals.py +3 -1
- scitex/scholar/url_finder/translators/individual/nagoya_university_opac.py +3 -1
- scitex/scholar/url_finder/translators/individual/nature_publishing_group.py +32 -19
- scitex/scholar/url_finder/translators/individual/ntsb_accident_reports.py +3 -1
- scitex/scholar/url_finder/translators/individual/openedition_journals.py +8 -4
- scitex/scholar/url_finder/translators/individual/orcid.py +16 -15
- scitex/scholar/url_finder/translators/individual/oxford.py +25 -19
- scitex/scholar/url_finder/translators/individual/oxford_dictionaries_premium.py +3 -1
- scitex/scholar/url_finder/translators/individual/ozon_ru.py +3 -1
- scitex/scholar/url_finder/translators/individual/plos.py +9 -12
- scitex/scholar/url_finder/translators/individual/polygon.py +3 -1
- scitex/scholar/url_finder/translators/individual/primo.py +3 -1
- scitex/scholar/url_finder/translators/individual/project_muse.py +3 -1
- scitex/scholar/url_finder/translators/individual/pubfactory_journals.py +3 -1
- scitex/scholar/url_finder/translators/individual/pubmed.py +71 -65
- scitex/scholar/url_finder/translators/individual/pubmed_central.py +8 -6
- scitex/scholar/url_finder/translators/individual/rechtspraak_nl.py +3 -1
- scitex/scholar/url_finder/translators/individual/sage_journals.py +25 -17
- scitex/scholar/url_finder/translators/individual/sciencedirect.py +36 -17
- scitex/scholar/url_finder/translators/individual/semantics_visual_library.py +3 -1
- scitex/scholar/url_finder/translators/individual/silverchair.py +70 -52
- scitex/scholar/url_finder/translators/individual/sora.py +3 -1
- scitex/scholar/url_finder/translators/individual/springer.py +15 -11
- scitex/scholar/url_finder/translators/individual/ssrn.py +3 -3
- scitex/scholar/url_finder/translators/individual/stanford_encyclopedia_of_philosophy.py +3 -1
- scitex/scholar/url_finder/translators/individual/superlib.py +3 -1
- scitex/scholar/url_finder/translators/individual/treesearch.py +3 -1
- scitex/scholar/url_finder/translators/individual/university_of_chicago_press_books.py +3 -1
- scitex/scholar/url_finder/translators/individual/vlex.py +3 -1
- scitex/scholar/url_finder/translators/individual/web_of_science.py +3 -1
- scitex/scholar/url_finder/translators/individual/web_of_science_nextgen.py +3 -1
- scitex/scholar/url_finder/translators/individual/wiley.py +31 -25
- scitex/scholar/url_finder/translators/individual/wilson_center_digital_archive.py +3 -1
- scitex/scholar/utils/bibtex/_parse_bibtex.py +3 -3
- scitex/scholar/utils/cleanup/_cleanup_scholar_processes.py +5 -9
- scitex/scholar/utils/text/_TextNormalizer.py +249 -176
- scitex/scholar/utils/validation/DOIValidator.py +31 -28
- scitex/scholar/utils/validation/__init__.py +0 -0
- scitex/scholar/utils/validation/validate_library_dois.py +61 -57
- scitex/scholar/zotero/__init__.py +1 -1
- scitex/security/cli.py +7 -20
- scitex/security/github.py +45 -32
- scitex/session/__init__.py +8 -9
- scitex/session/_decorator.py +49 -42
- scitex/session/_lifecycle.py +39 -39
- scitex/session/_manager.py +24 -20
- scitex/sh/__init__.py +4 -3
- scitex/sh/_execute.py +10 -7
- scitex/sh/_security.py +3 -3
- scitex/sh/_types.py +2 -3
- scitex/stats/__init__.py +57 -6
- scitex/stats/_schema.py +42 -569
- scitex/stats/auto/__init__.py +188 -0
- scitex/stats/auto/_context.py +331 -0
- scitex/stats/auto/_formatting.py +679 -0
- scitex/stats/auto/_rules.py +901 -0
- scitex/stats/auto/_selector.py +554 -0
- scitex/stats/auto/_styles.py +721 -0
- scitex/stats/correct/__init__.py +4 -4
- scitex/stats/correct/_correct_bonferroni.py +43 -34
- scitex/stats/correct/_correct_fdr.py +14 -40
- scitex/stats/correct/_correct_fdr_.py +39 -46
- scitex/stats/correct/_correct_holm.py +14 -32
- scitex/stats/correct/_correct_sidak.py +36 -21
- scitex/stats/descriptive/_circular.py +20 -21
- scitex/stats/descriptive/_describe.py +19 -5
- scitex/stats/descriptive/_nan.py +5 -7
- scitex/stats/descriptive/_real.py +4 -3
- scitex/stats/effect_sizes/__init__.py +10 -11
- scitex/stats/effect_sizes/_cliffs_delta.py +35 -32
- scitex/stats/effect_sizes/_cohens_d.py +30 -31
- scitex/stats/effect_sizes/_epsilon_squared.py +19 -22
- scitex/stats/effect_sizes/_eta_squared.py +23 -27
- scitex/stats/effect_sizes/_prob_superiority.py +18 -21
- scitex/stats/posthoc/__init__.py +3 -3
- scitex/stats/posthoc/_dunnett.py +75 -55
- scitex/stats/posthoc/_games_howell.py +61 -43
- scitex/stats/posthoc/_tukey_hsd.py +42 -34
- scitex/stats/power/__init__.py +2 -2
- scitex/stats/power/_power.py +56 -56
- scitex/stats/tests/__init__.py +1 -1
- scitex/stats/tests/correlation/__init__.py +1 -1
- scitex/stats/tests/correlation/_test_pearson.py +28 -38
- scitex/stats/utils/__init__.py +14 -17
- scitex/stats/utils/_effect_size.py +85 -78
- scitex/stats/utils/_formatters.py +49 -43
- scitex/stats/utils/_normalizers.py +7 -14
- scitex/stats/utils/_power.py +56 -56
- scitex/str/__init__.py +1 -0
- scitex/str/_clean_path.py +3 -3
- scitex/str/_factor_out_digits.py +86 -58
- scitex/str/_format_plot_text.py +180 -111
- scitex/str/_latex.py +19 -19
- scitex/str/_latex_fallback.py +9 -10
- scitex/str/_parse.py +3 -6
- scitex/str/_print_debug.py +13 -13
- scitex/str/_printc.py +2 -0
- scitex/str/_search.py +3 -3
- scitex/template/.legacy/_clone_project.py +9 -13
- scitex/template/__init__.py +10 -2
- scitex/template/_clone_project.py +7 -2
- scitex/template/_copy.py +1 -0
- scitex/template/_customize.py +3 -6
- scitex/template/_git_strategy.py +2 -3
- scitex/template/_rename.py +1 -0
- scitex/template/clone_pip_project.py +6 -7
- scitex/template/clone_research.py +7 -10
- scitex/template/clone_singularity.py +6 -7
- scitex/template/clone_writer_directory.py +6 -7
- scitex/tex/_preview.py +26 -11
- scitex/tex/_to_vec.py +10 -7
- scitex/torch/__init__.py +11 -1
- scitex/types/_ArrayLike.py +2 -0
- scitex/types/_is_listed_X.py +3 -3
- scitex/units.py +110 -77
- scitex/utils/_compress_hdf5.py +3 -3
- scitex/utils/_email.py +8 -4
- scitex/utils/_notify.py +14 -8
- scitex/utils/_search.py +6 -6
- scitex/utils/_verify_scitex_format.py +17 -42
- scitex/utils/_verify_scitex_format_v01.py +12 -34
- scitex/utils/template.py +4 -3
- scitex/vis/__init__.py +0 -0
- scitex/vis/backend/__init__.py +3 -3
- scitex/vis/backend/{export.py → _export.py} +1 -1
- scitex/vis/backend/{parser.py → _parser.py} +1 -3
- scitex/vis/backend/{render.py → _render.py} +1 -1
- scitex/vis/canvas.py +15 -3
- scitex/vis/editor/__init__.py +0 -0
- scitex/vis/editor/_dearpygui_editor.py +450 -304
- scitex/vis/editor/_defaults.py +114 -123
- scitex/vis/editor/_edit.py +38 -26
- scitex/vis/editor/_flask_editor.py +8 -8
- scitex/vis/editor/_mpl_editor.py +63 -48
- scitex/vis/editor/_qt_editor.py +210 -159
- scitex/vis/editor/_tkinter_editor.py +146 -89
- scitex/vis/editor/flask_editor/__init__.py +10 -10
- scitex/vis/editor/flask_editor/_bbox.py +529 -0
- scitex/vis/editor/flask_editor/{core.py → _core.py} +45 -29
- scitex/vis/editor/flask_editor/_plotter.py +567 -0
- scitex/vis/editor/flask_editor/_renderer.py +393 -0
- scitex/vis/editor/flask_editor/{utils.py → _utils.py} +13 -14
- scitex/vis/editor/flask_editor/templates/__init__.py +5 -5
- scitex/vis/editor/flask_editor/templates/{html.py → _html.py} +234 -16
- scitex/vis/editor/flask_editor/templates/_scripts.py +1261 -0
- scitex/vis/editor/flask_editor/templates/{styles.py → _styles.py} +192 -2
- scitex/vis/io/__init__.py +5 -5
- scitex/vis/io/{canvas.py → _canvas.py} +8 -4
- scitex/vis/io/{data.py → _data.py} +13 -9
- scitex/vis/io/{directory.py → _directory.py} +7 -4
- scitex/vis/io/{export.py → _export.py} +15 -12
- scitex/vis/io/{load.py → _load.py} +1 -1
- scitex/vis/io/{panel.py → _panel.py} +21 -13
- scitex/vis/io/{save.py → _save.py} +0 -0
- scitex/vis/model/__init__.py +7 -7
- scitex/vis/model/{annotations.py → _annotations.py} +2 -4
- scitex/vis/model/{axes.py → _axes.py} +1 -1
- scitex/vis/model/{figure.py → _figure.py} +0 -0
- scitex/vis/model/{guides.py → _guides.py} +1 -1
- scitex/vis/model/{plot.py → _plot.py} +2 -4
- scitex/vis/model/{plot_types.py → _plot_types.py} +0 -0
- scitex/vis/model/{styles.py → _styles.py} +0 -0
- scitex/vis/utils/__init__.py +2 -2
- scitex/vis/utils/{defaults.py → _defaults.py} +1 -2
- scitex/vis/utils/{validate.py → _validate.py} +3 -9
- scitex/web/__init__.py +7 -1
- scitex/web/_scraping.py +54 -38
- scitex/web/_search_pubmed.py +30 -14
- scitex/writer/.legacy/Writer_v01-refactored.py +4 -4
- scitex/writer/.legacy/_compile.py +18 -28
- scitex/writer/Writer.py +8 -21
- scitex/writer/__init__.py +11 -11
- scitex/writer/_clone_writer_project.py +2 -6
- scitex/writer/_compile/__init__.py +1 -0
- scitex/writer/_compile/_parser.py +1 -0
- scitex/writer/_compile/_runner.py +35 -38
- scitex/writer/_compile/_validator.py +1 -0
- scitex/writer/_compile/manuscript.py +1 -0
- scitex/writer/_compile/revision.py +1 -0
- scitex/writer/_compile/supplementary.py +1 -0
- scitex/writer/_compile_async.py +5 -12
- scitex/writer/_project/__init__.py +1 -0
- scitex/writer/_project/_create.py +10 -25
- scitex/writer/_project/_trees.py +4 -9
- scitex/writer/_project/_validate.py +2 -3
- scitex/writer/_validate_tree_structures.py +7 -18
- scitex/writer/dataclasses/__init__.py +8 -10
- scitex/writer/dataclasses/config/_CONSTANTS.py +2 -3
- scitex/writer/dataclasses/config/_WriterConfig.py +4 -9
- scitex/writer/dataclasses/contents/_ManuscriptContents.py +14 -25
- scitex/writer/dataclasses/contents/_RevisionContents.py +21 -16
- scitex/writer/dataclasses/contents/_SupplementaryContents.py +21 -24
- scitex/writer/dataclasses/core/_Document.py +2 -3
- scitex/writer/dataclasses/core/_DocumentSection.py +8 -23
- scitex/writer/dataclasses/results/_CompilationResult.py +2 -3
- scitex/writer/dataclasses/results/_LaTeXIssue.py +3 -6
- scitex/writer/dataclasses/results/_SaveSectionsResponse.py +20 -9
- scitex/writer/dataclasses/results/_SectionReadResponse.py +24 -10
- scitex/writer/dataclasses/tree/_ConfigTree.py +7 -4
- scitex/writer/dataclasses/tree/_ManuscriptTree.py +10 -13
- scitex/writer/dataclasses/tree/_RevisionTree.py +16 -17
- scitex/writer/dataclasses/tree/_ScriptsTree.py +10 -5
- scitex/writer/dataclasses/tree/_SharedTree.py +10 -13
- scitex/writer/dataclasses/tree/_SupplementaryTree.py +15 -14
- scitex/writer/utils/.legacy_git_retry.py +3 -8
- scitex/writer/utils/_parse_latex_logs.py +2 -3
- scitex/writer/utils/_parse_script_args.py +20 -23
- scitex/writer/utils/_watch.py +5 -5
- {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/METADATA +4 -10
- {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/RECORD +1071 -975
- scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin_v01-indentation-issues.py +0 -583
- scitex/plt/_subplots/_export_as_csv_formatters.py +0 -112
- scitex/vis/editor/flask_editor/bbox.py +0 -216
- scitex/vis/editor/flask_editor/plotter.py +0 -130
- scitex/vis/editor/flask_editor/renderer.py +0 -184
- scitex/vis/editor/flask_editor/templates/scripts.py +0 -614
- {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/WHEEL +0 -0
- {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.5.0.dist-info → scitex-2.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# ----------------------------------------
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
import os
|
|
8
|
+
|
|
8
9
|
__FILE__ = __file__
|
|
9
10
|
__DIR__ = os.path.dirname(__FILE__)
|
|
10
11
|
# ----------------------------------------
|
|
@@ -44,11 +45,11 @@ logger = logging.getLogger(__name__)
|
|
|
44
45
|
|
|
45
46
|
class ZoteroCompatibleDB:
|
|
46
47
|
"""Zotero-compatible database with enrichment workflow.
|
|
47
|
-
|
|
48
|
+
|
|
48
49
|
Follows Zotero's schema and storage patterns for compatibility while
|
|
49
50
|
adding source tracking and enrichment capabilities.
|
|
50
51
|
"""
|
|
51
|
-
|
|
52
|
+
|
|
52
53
|
# Zotero-compatible item types
|
|
53
54
|
ITEM_TYPES = {
|
|
54
55
|
"journalArticle": 1,
|
|
@@ -58,13 +59,13 @@ class ZoteroCompatibleDB:
|
|
|
58
59
|
"conferencePaper": 5,
|
|
59
60
|
"preprint": 6,
|
|
60
61
|
"report": 7,
|
|
61
|
-
"webpage": 8
|
|
62
|
+
"webpage": 8,
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
+
|
|
64
65
|
# Field mappings (Zotero fieldID to our field names)
|
|
65
66
|
FIELD_MAP = {
|
|
66
67
|
1: "title",
|
|
67
|
-
2: "abstract",
|
|
68
|
+
2: "abstract",
|
|
68
69
|
3: "journal",
|
|
69
70
|
4: "volume",
|
|
70
71
|
5: "issue",
|
|
@@ -88,12 +89,12 @@ class ZoteroCompatibleDB:
|
|
|
88
89
|
23: "rights",
|
|
89
90
|
24: "language",
|
|
90
91
|
25: "dateAdded",
|
|
91
|
-
26: "dateModified"
|
|
92
|
+
26: "dateModified",
|
|
92
93
|
}
|
|
93
|
-
|
|
94
|
+
|
|
94
95
|
def __init__(self, base_dir: Optional[Path] = None, library_name: str = "default"):
|
|
95
96
|
"""Initialize Zotero-compatible database.
|
|
96
|
-
|
|
97
|
+
|
|
97
98
|
Args:
|
|
98
99
|
base_dir: Base directory (default: $SCITEX_DIR/scholar/library/<library_name>)
|
|
99
100
|
library_name: Name of the library (default: "default")
|
|
@@ -106,49 +107,52 @@ class ZoteroCompatibleDB:
|
|
|
106
107
|
base_dir = Path(scitex_dir) / "scholar" / "library" / library_name
|
|
107
108
|
else:
|
|
108
109
|
# Fallback to ~/.scitex
|
|
109
|
-
base_dir =
|
|
110
|
-
|
|
110
|
+
base_dir = (
|
|
111
|
+
Path.home() / ".scitex" / "scholar" / "library" / library_name
|
|
112
|
+
)
|
|
113
|
+
|
|
111
114
|
logger.info(f"Using library directory: {base_dir}")
|
|
112
|
-
|
|
115
|
+
|
|
113
116
|
self.base_dir = Path(base_dir)
|
|
114
117
|
self.library_name = library_name
|
|
115
118
|
self.db_path = self.base_dir / "zotero.sqlite"
|
|
116
119
|
self.storage_dir = self.base_dir / "storage"
|
|
117
|
-
|
|
120
|
+
|
|
118
121
|
# Detect platform
|
|
119
122
|
import platform
|
|
123
|
+
|
|
120
124
|
self.is_windows = platform.system() == "Windows"
|
|
121
125
|
self.is_wsl = "microsoft" in platform.uname().release.lower()
|
|
122
|
-
|
|
126
|
+
|
|
123
127
|
# Create structure
|
|
124
128
|
self._init_directories()
|
|
125
129
|
self._init_database()
|
|
126
|
-
|
|
130
|
+
|
|
127
131
|
# Create library config
|
|
128
132
|
self._save_library_config()
|
|
129
|
-
|
|
133
|
+
|
|
130
134
|
def _init_directories(self):
|
|
131
135
|
"""Create Zotero-style directory structure with human-readable organization."""
|
|
132
136
|
self.base_dir.mkdir(parents=True, exist_ok=True)
|
|
133
|
-
|
|
137
|
+
|
|
134
138
|
# Primary storage (Zotero-style)
|
|
135
139
|
(self.storage_dir / "by_key").mkdir(parents=True, exist_ok=True)
|
|
136
|
-
|
|
140
|
+
|
|
137
141
|
# Human-readable organization
|
|
138
142
|
(self.storage_dir / "by_citation").mkdir(exist_ok=True)
|
|
139
143
|
(self.storage_dir / "by_year").mkdir(exist_ok=True)
|
|
140
144
|
(self.storage_dir / "by_journal").mkdir(exist_ok=True)
|
|
141
145
|
(self.storage_dir / "by_topic").mkdir(exist_ok=True)
|
|
142
|
-
|
|
146
|
+
|
|
143
147
|
# Windows shortcuts directory (separate from symlinks)
|
|
144
148
|
if self.is_windows or self.is_wsl:
|
|
145
149
|
(self.storage_dir / "shortcuts_windows").mkdir(exist_ok=True)
|
|
146
|
-
|
|
150
|
+
|
|
147
151
|
# Zotero compatibility directories
|
|
148
152
|
(self.base_dir / "translators").mkdir(exist_ok=True)
|
|
149
153
|
(self.base_dir / "styles").mkdir(exist_ok=True)
|
|
150
154
|
(self.base_dir / "locate").mkdir(exist_ok=True)
|
|
151
|
-
|
|
155
|
+
|
|
152
156
|
def _init_database(self):
|
|
153
157
|
"""Initialize Zotero-compatible database schema."""
|
|
154
158
|
with self._get_connection() as conn:
|
|
@@ -156,7 +160,7 @@ class ZoteroCompatibleDB:
|
|
|
156
160
|
conn.execute("PRAGMA page_size=4096")
|
|
157
161
|
conn.execute("PRAGMA journal_mode=WAL")
|
|
158
162
|
conn.execute("PRAGMA synchronous=NORMAL")
|
|
159
|
-
|
|
163
|
+
|
|
160
164
|
# Version info (Zotero compatibility)
|
|
161
165
|
conn.execute("""
|
|
162
166
|
CREATE TABLE IF NOT EXISTS version (
|
|
@@ -165,7 +169,7 @@ class ZoteroCompatibleDB:
|
|
|
165
169
|
)
|
|
166
170
|
""")
|
|
167
171
|
conn.execute("INSERT OR REPLACE INTO version VALUES ('userdata', 120)")
|
|
168
|
-
|
|
172
|
+
|
|
169
173
|
# Libraries (simplified - we use single library)
|
|
170
174
|
conn.execute("""
|
|
171
175
|
CREATE TABLE IF NOT EXISTS libraries (
|
|
@@ -178,7 +182,7 @@ class ZoteroCompatibleDB:
|
|
|
178
182
|
conn.execute("""
|
|
179
183
|
INSERT OR IGNORE INTO libraries VALUES (1, 'user', 1, 1)
|
|
180
184
|
""")
|
|
181
|
-
|
|
185
|
+
|
|
182
186
|
# Items (main table)
|
|
183
187
|
conn.execute("""
|
|
184
188
|
CREATE TABLE IF NOT EXISTS items (
|
|
@@ -195,7 +199,7 @@ class ZoteroCompatibleDB:
|
|
|
195
199
|
FOREIGN KEY (libraryID) REFERENCES libraries(libraryID)
|
|
196
200
|
)
|
|
197
201
|
""")
|
|
198
|
-
|
|
202
|
+
|
|
199
203
|
# Item data values
|
|
200
204
|
conn.execute("""
|
|
201
205
|
CREATE TABLE IF NOT EXISTS itemDataValues (
|
|
@@ -203,7 +207,7 @@ class ZoteroCompatibleDB:
|
|
|
203
207
|
value TEXT UNIQUE
|
|
204
208
|
)
|
|
205
209
|
""")
|
|
206
|
-
|
|
210
|
+
|
|
207
211
|
# Item data (field values)
|
|
208
212
|
conn.execute("""
|
|
209
213
|
CREATE TABLE IF NOT EXISTS itemData (
|
|
@@ -215,7 +219,7 @@ class ZoteroCompatibleDB:
|
|
|
215
219
|
FOREIGN KEY (valueID) REFERENCES itemDataValues(valueID)
|
|
216
220
|
)
|
|
217
221
|
""")
|
|
218
|
-
|
|
222
|
+
|
|
219
223
|
# Creators
|
|
220
224
|
conn.execute("""
|
|
221
225
|
CREATE TABLE IF NOT EXISTS creators (
|
|
@@ -226,7 +230,7 @@ class ZoteroCompatibleDB:
|
|
|
226
230
|
UNIQUE(firstName, lastName)
|
|
227
231
|
)
|
|
228
232
|
""")
|
|
229
|
-
|
|
233
|
+
|
|
230
234
|
# Item creators
|
|
231
235
|
conn.execute("""
|
|
232
236
|
CREATE TABLE IF NOT EXISTS itemCreators (
|
|
@@ -239,7 +243,7 @@ class ZoteroCompatibleDB:
|
|
|
239
243
|
FOREIGN KEY (creatorID) REFERENCES creators(creatorID)
|
|
240
244
|
)
|
|
241
245
|
""")
|
|
242
|
-
|
|
246
|
+
|
|
243
247
|
# Collections
|
|
244
248
|
conn.execute("""
|
|
245
249
|
CREATE TABLE IF NOT EXISTS collections (
|
|
@@ -258,7 +262,7 @@ class ZoteroCompatibleDB:
|
|
|
258
262
|
FOREIGN KEY (parentCollectionID) REFERENCES collections(collectionID)
|
|
259
263
|
)
|
|
260
264
|
""")
|
|
261
|
-
|
|
265
|
+
|
|
262
266
|
# Collection items
|
|
263
267
|
conn.execute("""
|
|
264
268
|
CREATE TABLE IF NOT EXISTS collectionItems (
|
|
@@ -270,7 +274,7 @@ class ZoteroCompatibleDB:
|
|
|
270
274
|
FOREIGN KEY (itemID) REFERENCES items(itemID)
|
|
271
275
|
)
|
|
272
276
|
""")
|
|
273
|
-
|
|
277
|
+
|
|
274
278
|
# Tags
|
|
275
279
|
conn.execute("""
|
|
276
280
|
CREATE TABLE IF NOT EXISTS tags (
|
|
@@ -278,7 +282,7 @@ class ZoteroCompatibleDB:
|
|
|
278
282
|
name TEXT NOT NULL UNIQUE
|
|
279
283
|
)
|
|
280
284
|
""")
|
|
281
|
-
|
|
285
|
+
|
|
282
286
|
# Item tags
|
|
283
287
|
conn.execute("""
|
|
284
288
|
CREATE TABLE IF NOT EXISTS itemTags (
|
|
@@ -290,7 +294,7 @@ class ZoteroCompatibleDB:
|
|
|
290
294
|
FOREIGN KEY (tagID) REFERENCES tags(tagID)
|
|
291
295
|
)
|
|
292
296
|
""")
|
|
293
|
-
|
|
297
|
+
|
|
294
298
|
# Item attachments
|
|
295
299
|
conn.execute("""
|
|
296
300
|
CREATE TABLE IF NOT EXISTS itemAttachments (
|
|
@@ -307,7 +311,7 @@ class ZoteroCompatibleDB:
|
|
|
307
311
|
FOREIGN KEY (parentItemID) REFERENCES items(itemID)
|
|
308
312
|
)
|
|
309
313
|
""")
|
|
310
|
-
|
|
314
|
+
|
|
311
315
|
# Item notes
|
|
312
316
|
conn.execute("""
|
|
313
317
|
CREATE TABLE IF NOT EXISTS itemNotes (
|
|
@@ -319,7 +323,7 @@ class ZoteroCompatibleDB:
|
|
|
319
323
|
FOREIGN KEY (parentItemID) REFERENCES items(itemID)
|
|
320
324
|
)
|
|
321
325
|
""")
|
|
322
|
-
|
|
326
|
+
|
|
323
327
|
# Deleted items
|
|
324
328
|
conn.execute("""
|
|
325
329
|
CREATE TABLE IF NOT EXISTS deletedItems (
|
|
@@ -327,7 +331,7 @@ class ZoteroCompatibleDB:
|
|
|
327
331
|
dateDeleted DEFAULT CURRENT_TIMESTAMP
|
|
328
332
|
)
|
|
329
333
|
""")
|
|
330
|
-
|
|
334
|
+
|
|
331
335
|
# Full text content
|
|
332
336
|
conn.execute("""
|
|
333
337
|
CREATE TABLE IF NOT EXISTS fulltextItems (
|
|
@@ -341,7 +345,7 @@ class ZoteroCompatibleDB:
|
|
|
341
345
|
FOREIGN KEY (itemID) REFERENCES items(itemID)
|
|
342
346
|
)
|
|
343
347
|
""")
|
|
344
|
-
|
|
348
|
+
|
|
345
349
|
# Full text words
|
|
346
350
|
conn.execute("""
|
|
347
351
|
CREATE TABLE IF NOT EXISTS fulltextWords (
|
|
@@ -349,7 +353,7 @@ class ZoteroCompatibleDB:
|
|
|
349
353
|
word TEXT UNIQUE
|
|
350
354
|
)
|
|
351
355
|
""")
|
|
352
|
-
|
|
356
|
+
|
|
353
357
|
# Full text item words
|
|
354
358
|
conn.execute("""
|
|
355
359
|
CREATE TABLE IF NOT EXISTS fulltextItemWords (
|
|
@@ -360,9 +364,9 @@ class ZoteroCompatibleDB:
|
|
|
360
364
|
FOREIGN KEY (itemID) REFERENCES items(itemID)
|
|
361
365
|
)
|
|
362
366
|
""")
|
|
363
|
-
|
|
367
|
+
|
|
364
368
|
# SciTeX extensions
|
|
365
|
-
|
|
369
|
+
|
|
366
370
|
# Source tracking table
|
|
367
371
|
conn.execute("""
|
|
368
372
|
CREATE TABLE IF NOT EXISTS scitex_field_sources (
|
|
@@ -375,7 +379,7 @@ class ZoteroCompatibleDB:
|
|
|
375
379
|
FOREIGN KEY (itemID) REFERENCES items(itemID)
|
|
376
380
|
)
|
|
377
381
|
""")
|
|
378
|
-
|
|
382
|
+
|
|
379
383
|
# Enrichment status
|
|
380
384
|
conn.execute("""
|
|
381
385
|
CREATE TABLE IF NOT EXISTS scitex_enrichment_status (
|
|
@@ -389,15 +393,21 @@ class ZoteroCompatibleDB:
|
|
|
389
393
|
FOREIGN KEY (itemID) REFERENCES items(itemID)
|
|
390
394
|
)
|
|
391
395
|
""")
|
|
392
|
-
|
|
396
|
+
|
|
393
397
|
# Create indexes
|
|
394
398
|
conn.execute("CREATE INDEX IF NOT EXISTS idx_items_key ON items(key)")
|
|
395
|
-
conn.execute(
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
+
conn.execute(
|
|
400
|
+
"CREATE INDEX IF NOT EXISTS idx_items_dateAdded ON items(dateAdded)"
|
|
401
|
+
)
|
|
402
|
+
conn.execute(
|
|
403
|
+
"CREATE INDEX IF NOT EXISTS idx_items_dateModified ON items(dateModified)"
|
|
404
|
+
)
|
|
405
|
+
conn.execute(
|
|
406
|
+
"CREATE INDEX IF NOT EXISTS idx_itemData_value ON itemData(itemID, valueID)"
|
|
407
|
+
)
|
|
408
|
+
|
|
399
409
|
conn.commit()
|
|
400
|
-
|
|
410
|
+
|
|
401
411
|
@contextmanager
|
|
402
412
|
def _get_connection(self):
|
|
403
413
|
"""Get database connection."""
|
|
@@ -407,37 +417,37 @@ class ZoteroCompatibleDB:
|
|
|
407
417
|
yield conn
|
|
408
418
|
finally:
|
|
409
419
|
conn.close()
|
|
410
|
-
|
|
420
|
+
|
|
411
421
|
def _generate_key(self, length: int = 8) -> str:
|
|
412
422
|
"""Generate Zotero-style key."""
|
|
413
423
|
# Zotero uses base32-like encoding
|
|
414
424
|
chars = string.ascii_uppercase + string.digits
|
|
415
425
|
# Avoid ambiguous characters
|
|
416
|
-
chars =
|
|
417
|
-
|
|
418
|
-
|
|
426
|
+
chars = (
|
|
427
|
+
chars.replace("0", "").replace("O", "").replace("1", "").replace("I", "")
|
|
428
|
+
)
|
|
429
|
+
return "".join(random.choice(chars) for _ in range(length))
|
|
430
|
+
|
|
419
431
|
def _get_or_create_value(self, conn: sqlite3.Connection, value: Any) -> int:
|
|
420
432
|
"""Get or create value in itemDataValues table."""
|
|
421
433
|
if value is None:
|
|
422
434
|
return None
|
|
423
|
-
|
|
435
|
+
|
|
424
436
|
value_str = str(value)
|
|
425
|
-
|
|
437
|
+
|
|
426
438
|
cursor = conn.execute(
|
|
427
|
-
"SELECT valueID FROM itemDataValues WHERE value = ?",
|
|
428
|
-
(value_str,)
|
|
439
|
+
"SELECT valueID FROM itemDataValues WHERE value = ?", (value_str,)
|
|
429
440
|
)
|
|
430
441
|
row = cursor.fetchone()
|
|
431
|
-
|
|
442
|
+
|
|
432
443
|
if row:
|
|
433
444
|
return row["valueID"]
|
|
434
445
|
else:
|
|
435
446
|
cursor = conn.execute(
|
|
436
|
-
"INSERT INTO itemDataValues (value) VALUES (?)",
|
|
437
|
-
(value_str,)
|
|
447
|
+
"INSERT INTO itemDataValues (value) VALUES (?)", (value_str,)
|
|
438
448
|
)
|
|
439
449
|
return cursor.lastrowid
|
|
440
|
-
|
|
450
|
+
|
|
441
451
|
def _get_field_id(self, field_name: str) -> Optional[int]:
|
|
442
452
|
"""Get Zotero fieldID from field name."""
|
|
443
453
|
# Reverse lookup in field map
|
|
@@ -448,15 +458,16 @@ class ZoteroCompatibleDB:
|
|
|
448
458
|
if field_name == "doi":
|
|
449
459
|
return 8 # DOI fieldID
|
|
450
460
|
return None
|
|
451
|
-
|
|
452
|
-
def add_item_from_partial(
|
|
453
|
-
|
|
461
|
+
|
|
462
|
+
def add_item_from_partial(
|
|
463
|
+
self, partial_info: Dict[str, Any], item_type: str = "journalArticle"
|
|
464
|
+
) -> int:
|
|
454
465
|
"""Add item from partial information (step 1 of workflow).
|
|
455
|
-
|
|
466
|
+
|
|
456
467
|
Args:
|
|
457
468
|
partial_info: Dict with title, authors, year, etc.
|
|
458
469
|
item_type: Zotero item type
|
|
459
|
-
|
|
470
|
+
|
|
460
471
|
Returns:
|
|
461
472
|
Item ID
|
|
462
473
|
"""
|
|
@@ -464,115 +475,122 @@ class ZoteroCompatibleDB:
|
|
|
464
475
|
# Create item
|
|
465
476
|
key = self._generate_key()
|
|
466
477
|
item_type_id = self.ITEM_TYPES.get(item_type, 1)
|
|
467
|
-
|
|
468
|
-
cursor = conn.execute(
|
|
478
|
+
|
|
479
|
+
cursor = conn.execute(
|
|
480
|
+
"""
|
|
469
481
|
INSERT INTO items (itemTypeID, libraryID, key)
|
|
470
482
|
VALUES (?, 1, ?)
|
|
471
|
-
""",
|
|
483
|
+
""",
|
|
484
|
+
(item_type_id, key),
|
|
485
|
+
)
|
|
472
486
|
item_id = cursor.lastrowid
|
|
473
|
-
|
|
487
|
+
|
|
474
488
|
# Add basic fields
|
|
475
489
|
fields_to_add = {
|
|
476
490
|
"title": partial_info.get("title"),
|
|
477
|
-
"date": str(partial_info.get("year"))
|
|
491
|
+
"date": str(partial_info.get("year"))
|
|
492
|
+
if partial_info.get("year")
|
|
493
|
+
else None,
|
|
478
494
|
"DOI": partial_info.get("doi"),
|
|
479
495
|
"url": partial_info.get("url"),
|
|
480
496
|
"journal": partial_info.get("journal"),
|
|
481
|
-
"abstract": partial_info.get("abstract")
|
|
497
|
+
"abstract": partial_info.get("abstract"),
|
|
482
498
|
}
|
|
483
|
-
|
|
499
|
+
|
|
484
500
|
for field_name, value in fields_to_add.items():
|
|
485
501
|
if value:
|
|
486
502
|
self._set_item_field(conn, item_id, field_name, value)
|
|
487
|
-
|
|
503
|
+
|
|
488
504
|
# Track source
|
|
489
505
|
source = partial_info.get(f"{field_name}_source", "initial_import")
|
|
490
506
|
self._track_field_source(conn, item_id, field_name, source)
|
|
491
|
-
|
|
507
|
+
|
|
492
508
|
# Add creators
|
|
493
509
|
authors = partial_info.get("authors", [])
|
|
494
510
|
for idx, author in enumerate(authors):
|
|
495
511
|
self._add_creator(conn, item_id, author, idx)
|
|
496
|
-
|
|
512
|
+
|
|
497
513
|
# Add tags/keywords
|
|
498
514
|
keywords = partial_info.get("keywords", [])
|
|
499
515
|
for keyword in keywords:
|
|
500
516
|
self._add_tag(conn, item_id, keyword)
|
|
501
|
-
|
|
517
|
+
|
|
502
518
|
# Initialize enrichment status
|
|
503
|
-
conn.execute(
|
|
519
|
+
conn.execute(
|
|
520
|
+
"""
|
|
504
521
|
INSERT INTO scitex_enrichment_status (itemID)
|
|
505
522
|
VALUES (?)
|
|
506
|
-
""",
|
|
507
|
-
|
|
523
|
+
""",
|
|
524
|
+
(item_id,),
|
|
525
|
+
)
|
|
526
|
+
|
|
508
527
|
conn.commit()
|
|
509
|
-
|
|
528
|
+
|
|
510
529
|
# Create storage directory
|
|
511
530
|
storage_path = self.storage_dir / key
|
|
512
531
|
storage_path.mkdir(exist_ok=True)
|
|
513
|
-
|
|
514
|
-
logger.info(
|
|
515
|
-
|
|
532
|
+
|
|
533
|
+
logger.info(
|
|
534
|
+
f"Added item {item_id} with key {key}: {partial_info.get('title', '')[:50]}..."
|
|
535
|
+
)
|
|
536
|
+
|
|
516
537
|
# Create human-readable links
|
|
517
538
|
self._create_human_readable_links(item_id, key, partial_info)
|
|
518
|
-
|
|
539
|
+
|
|
519
540
|
return item_id
|
|
520
|
-
|
|
521
|
-
def _create_human_readable_links(
|
|
522
|
-
|
|
541
|
+
|
|
542
|
+
def _create_human_readable_links(
|
|
543
|
+
self, item_id: int, key: str, metadata: Dict[str, Any]
|
|
544
|
+
):
|
|
523
545
|
"""Create human-readable symlinks and Windows shortcuts."""
|
|
524
546
|
# Generate citation-style name
|
|
525
547
|
citation_name = self._generate_citation_name(metadata)
|
|
526
|
-
|
|
548
|
+
|
|
527
549
|
# Primary storage path
|
|
528
550
|
primary_path = self.storage_dir / "by_key" / key
|
|
529
|
-
|
|
551
|
+
|
|
530
552
|
# Create by_citation links
|
|
531
553
|
citation_dir = self.storage_dir / "by_citation" / citation_name
|
|
532
554
|
citation_dir.mkdir(parents=True, exist_ok=True)
|
|
533
|
-
|
|
555
|
+
|
|
534
556
|
# Create symlinks (works on Linux/Mac/WSL)
|
|
535
557
|
if not self.is_windows:
|
|
536
|
-
self._create_symlink(
|
|
537
|
-
|
|
538
|
-
citation_dir / f"{citation_name}-{key}"
|
|
539
|
-
)
|
|
540
|
-
|
|
558
|
+
self._create_symlink(primary_path, citation_dir / f"{citation_name}-{key}")
|
|
559
|
+
|
|
541
560
|
# Create Windows shortcuts
|
|
542
561
|
if self.is_windows or self.is_wsl:
|
|
543
562
|
self._create_windows_shortcut(
|
|
544
563
|
primary_path,
|
|
545
|
-
self.storage_dir
|
|
564
|
+
self.storage_dir
|
|
565
|
+
/ "shortcuts_windows"
|
|
566
|
+
/ citation_name
|
|
567
|
+
/ f"{citation_name}-{key}.lnk",
|
|
546
568
|
)
|
|
547
|
-
|
|
569
|
+
|
|
548
570
|
# Create by_year organization
|
|
549
571
|
year = metadata.get("year")
|
|
550
572
|
if year:
|
|
551
573
|
year_dir = self.storage_dir / "by_year" / str(year)
|
|
552
574
|
year_dir.mkdir(parents=True, exist_ok=True)
|
|
553
|
-
|
|
575
|
+
|
|
554
576
|
if not self.is_windows:
|
|
555
|
-
self._create_symlink(
|
|
556
|
-
|
|
557
|
-
year_dir / f"{citation_name}-{key}"
|
|
558
|
-
)
|
|
559
|
-
|
|
577
|
+
self._create_symlink(primary_path, year_dir / f"{citation_name}-{key}")
|
|
578
|
+
|
|
560
579
|
# Create by_journal organization
|
|
561
580
|
journal = metadata.get("journal")
|
|
562
581
|
if journal:
|
|
563
582
|
safe_journal = self._sanitize_filename(journal)[:50]
|
|
564
583
|
journal_dir = self.storage_dir / "by_journal" / safe_journal
|
|
565
584
|
journal_dir.mkdir(parents=True, exist_ok=True)
|
|
566
|
-
|
|
585
|
+
|
|
567
586
|
if not self.is_windows:
|
|
568
587
|
self._create_symlink(
|
|
569
|
-
primary_path,
|
|
570
|
-
journal_dir / f"{citation_name}-{key}"
|
|
588
|
+
primary_path, journal_dir / f"{citation_name}-{key}"
|
|
571
589
|
)
|
|
572
|
-
|
|
590
|
+
|
|
573
591
|
def _generate_citation_name(self, metadata: Dict[str, Any]) -> str:
|
|
574
592
|
"""Generate human-readable citation name.
|
|
575
|
-
|
|
593
|
+
|
|
576
594
|
Format: FIRSTAUTHOR-YEAR-SOURCE
|
|
577
595
|
Where SOURCE can be journal abbreviation, conference, book publisher, etc.
|
|
578
596
|
"""
|
|
@@ -586,16 +604,16 @@ class ZoteroCompatibleDB:
|
|
|
586
604
|
author_name = first_author.split()[-1] if first_author else "Unknown"
|
|
587
605
|
else:
|
|
588
606
|
author_name = "Unknown"
|
|
589
|
-
|
|
607
|
+
|
|
590
608
|
# Sanitize author name
|
|
591
609
|
author_name = self._sanitize_filename(author_name)
|
|
592
|
-
|
|
610
|
+
|
|
593
611
|
# Get year
|
|
594
612
|
year = metadata.get("year", "XXXX")
|
|
595
|
-
|
|
613
|
+
|
|
596
614
|
# Get source (journal, conference, publisher, etc.)
|
|
597
615
|
source = None
|
|
598
|
-
|
|
616
|
+
|
|
599
617
|
# Priority order for source
|
|
600
618
|
if metadata.get("journal"):
|
|
601
619
|
source = self._abbreviate_journal(metadata["journal"])
|
|
@@ -611,11 +629,11 @@ class ZoteroCompatibleDB:
|
|
|
611
629
|
source = "arXiv"
|
|
612
630
|
else:
|
|
613
631
|
source = "Misc"
|
|
614
|
-
|
|
632
|
+
|
|
615
633
|
source = self._sanitize_filename(source)[:30] # Limit length
|
|
616
|
-
|
|
634
|
+
|
|
617
635
|
return f"{author_name}-{year}-{source}"
|
|
618
|
-
|
|
636
|
+
|
|
619
637
|
def _abbreviate_journal(self, journal_name: str) -> str:
|
|
620
638
|
"""Create journal abbreviation."""
|
|
621
639
|
# Common journal abbreviations
|
|
@@ -633,35 +651,36 @@ class ZoteroCompatibleDB:
|
|
|
633
651
|
"acm transactions": "ACM-T",
|
|
634
652
|
"neural information processing systems": "NeurIPS",
|
|
635
653
|
"international conference on machine learning": "ICML",
|
|
636
|
-
"conference on computer vision and pattern recognition": "CVPR"
|
|
654
|
+
"conference on computer vision and pattern recognition": "CVPR",
|
|
637
655
|
}
|
|
638
|
-
|
|
656
|
+
|
|
639
657
|
# Check for exact matches
|
|
640
658
|
journal_lower = journal_name.lower()
|
|
641
659
|
for key, abbrev in abbreviations.items():
|
|
642
660
|
if key in journal_lower:
|
|
643
661
|
return abbrev
|
|
644
|
-
|
|
662
|
+
|
|
645
663
|
# Create abbreviation from first letters of significant words
|
|
646
664
|
words = journal_name.split()
|
|
647
665
|
stop_words = {"of", "the", "and", "in", "on", "for", "a", "an"}
|
|
648
666
|
significant_words = [w for w in words if w.lower() not in stop_words]
|
|
649
|
-
|
|
667
|
+
|
|
650
668
|
if len(significant_words) <= 3:
|
|
651
669
|
return "-".join(significant_words)
|
|
652
670
|
else:
|
|
653
671
|
# Use first letter of each significant word
|
|
654
672
|
return "".join(w[0].upper() for w in significant_words[:6])
|
|
655
|
-
|
|
673
|
+
|
|
656
674
|
def _abbreviate_conference(self, conference_name: str) -> str:
|
|
657
675
|
"""Create conference abbreviation."""
|
|
658
676
|
# Extract year if present
|
|
659
677
|
import re
|
|
660
|
-
|
|
661
|
-
|
|
678
|
+
|
|
679
|
+
year_match = re.search(r"\b(19|20)\d{2}\b", conference_name)
|
|
680
|
+
|
|
662
681
|
# Remove year for abbreviation
|
|
663
|
-
name_without_year = re.sub(r
|
|
664
|
-
|
|
682
|
+
name_without_year = re.sub(r"\b(19|20)\d{2}\b", "", conference_name).strip()
|
|
683
|
+
|
|
665
684
|
# Common conference patterns
|
|
666
685
|
if "workshop" in name_without_year.lower():
|
|
667
686
|
return "Workshop"
|
|
@@ -672,9 +691,9 @@ class ZoteroCompatibleDB:
|
|
|
672
691
|
words = name_without_year.lower().split("conference")[0].split()
|
|
673
692
|
if words:
|
|
674
693
|
return "".join(w[0].upper() for w in words if len(w) > 2)[:6] + "-Conf"
|
|
675
|
-
|
|
694
|
+
|
|
676
695
|
return self._abbreviate_journal(name_without_year)
|
|
677
|
-
|
|
696
|
+
|
|
678
697
|
def _sanitize_filename(self, name: str) -> str:
|
|
679
698
|
"""Sanitize string for use in filename."""
|
|
680
699
|
# Replace problematic characters
|
|
@@ -684,7 +703,7 @@ class ZoteroCompatibleDB:
|
|
|
684
703
|
":": "-",
|
|
685
704
|
"*": "-",
|
|
686
705
|
"?": "",
|
|
687
|
-
"
|
|
706
|
+
'"': "",
|
|
688
707
|
"<": "",
|
|
689
708
|
">": "",
|
|
690
709
|
"|": "-",
|
|
@@ -709,23 +728,23 @@ class ZoteroCompatibleDB:
|
|
|
709
728
|
"=": "-",
|
|
710
729
|
"+": "plus",
|
|
711
730
|
"~": "-",
|
|
712
|
-
"`": ""
|
|
731
|
+
"`": "",
|
|
713
732
|
}
|
|
714
|
-
|
|
733
|
+
|
|
715
734
|
result = name
|
|
716
735
|
for old, new in replacements.items():
|
|
717
736
|
result = result.replace(old, new)
|
|
718
|
-
|
|
737
|
+
|
|
719
738
|
# Remove multiple underscores/hyphens
|
|
720
|
-
result = re.sub(r
|
|
721
|
-
result = result.strip(
|
|
722
|
-
|
|
739
|
+
result = re.sub(r"[-_]+", "-", result)
|
|
740
|
+
result = result.strip("-_")
|
|
741
|
+
|
|
723
742
|
# Ensure it's not empty
|
|
724
743
|
if not result:
|
|
725
744
|
result = "Unknown"
|
|
726
|
-
|
|
745
|
+
|
|
727
746
|
return result
|
|
728
|
-
|
|
747
|
+
|
|
729
748
|
def _create_symlink(self, target: Path, link: Path):
|
|
730
749
|
"""Create symlink (Linux/Mac/WSL)."""
|
|
731
750
|
try:
|
|
@@ -736,15 +755,16 @@ class ZoteroCompatibleDB:
|
|
|
736
755
|
logger.debug(f"Created symlink: {link} -> {target}")
|
|
737
756
|
except Exception as e:
|
|
738
757
|
logger.warning(f"Failed to create symlink: {e}")
|
|
739
|
-
|
|
758
|
+
|
|
740
759
|
def _create_windows_shortcut(self, target: Path, shortcut_path: Path):
|
|
741
760
|
"""Create Windows .lnk shortcut file."""
|
|
742
761
|
try:
|
|
743
762
|
shortcut_path.parent.mkdir(parents=True, exist_ok=True)
|
|
744
|
-
|
|
763
|
+
|
|
745
764
|
# Use Windows COM to create shortcut
|
|
746
765
|
if self.is_windows:
|
|
747
766
|
import win32com.client
|
|
767
|
+
|
|
748
768
|
shell = win32com.client.Dispatch("WScript.Shell")
|
|
749
769
|
shortcut = shell.CreateShortCut(str(shortcut_path))
|
|
750
770
|
shortcut.Targetpath = str(target)
|
|
@@ -754,15 +774,17 @@ class ZoteroCompatibleDB:
|
|
|
754
774
|
elif self.is_wsl:
|
|
755
775
|
# For WSL, create a simple text file with the path
|
|
756
776
|
# (Real .lnk files need to be created from Windows side)
|
|
757
|
-
info_file = shortcut_path.with_suffix(
|
|
758
|
-
with open(info_file,
|
|
777
|
+
info_file = shortcut_path.with_suffix(".txt")
|
|
778
|
+
with open(info_file, "w") as f:
|
|
759
779
|
f.write(f"Target: {target}\n")
|
|
760
|
-
f.write(
|
|
780
|
+
f.write(
|
|
781
|
+
f"This is a placeholder. Create real .lnk file from Windows.\n"
|
|
782
|
+
)
|
|
761
783
|
logger.debug(f"Created shortcut info file: {info_file}")
|
|
762
|
-
|
|
784
|
+
|
|
763
785
|
except Exception as e:
|
|
764
786
|
logger.warning(f"Failed to create Windows shortcut: {e}")
|
|
765
|
-
|
|
787
|
+
|
|
766
788
|
def _save_library_config(self):
|
|
767
789
|
"""Save library configuration for easy access."""
|
|
768
790
|
config = {
|
|
@@ -775,65 +797,79 @@ class ZoteroCompatibleDB:
|
|
|
775
797
|
"human_readable_links": True,
|
|
776
798
|
"metadata_source_tracking": True,
|
|
777
799
|
"enrichment_workflow": True,
|
|
778
|
-
"windows_shortcuts": self.is_windows or self.is_wsl
|
|
779
|
-
}
|
|
800
|
+
"windows_shortcuts": self.is_windows or self.is_wsl,
|
|
801
|
+
},
|
|
780
802
|
}
|
|
781
|
-
|
|
803
|
+
|
|
782
804
|
config_path = self.base_dir / "library.json"
|
|
783
|
-
with open(config_path,
|
|
805
|
+
with open(config_path, "w") as f:
|
|
784
806
|
json.dump(config, f, indent=2)
|
|
785
|
-
|
|
807
|
+
|
|
786
808
|
# Also save to global library registry
|
|
787
809
|
registry_dir = self.base_dir.parent.parent / "config"
|
|
788
810
|
registry_dir.mkdir(parents=True, exist_ok=True)
|
|
789
|
-
|
|
811
|
+
|
|
790
812
|
registry_path = registry_dir / "libraries.json"
|
|
791
|
-
|
|
813
|
+
|
|
792
814
|
# Load existing registry
|
|
793
815
|
if registry_path.exists():
|
|
794
|
-
with open(registry_path,
|
|
816
|
+
with open(registry_path, "r") as f:
|
|
795
817
|
registry = json.load(f)
|
|
796
818
|
else:
|
|
797
819
|
registry = {"libraries": {}}
|
|
798
|
-
|
|
820
|
+
|
|
799
821
|
# Update registry
|
|
800
822
|
registry["libraries"][self.library_name] = {
|
|
801
823
|
"path": str(self.base_dir),
|
|
802
824
|
"created_at": config["created_at"],
|
|
803
|
-
"last_accessed": datetime.now().isoformat()
|
|
825
|
+
"last_accessed": datetime.now().isoformat(),
|
|
804
826
|
}
|
|
805
|
-
|
|
806
|
-
with open(registry_path,
|
|
827
|
+
|
|
828
|
+
with open(registry_path, "w") as f:
|
|
807
829
|
json.dump(registry, f, indent=2)
|
|
808
|
-
|
|
830
|
+
|
|
809
831
|
logger.info(f"Library config saved: {config_path}")
|
|
810
|
-
|
|
811
|
-
def _set_item_field(
|
|
812
|
-
|
|
832
|
+
|
|
833
|
+
def _set_item_field(
|
|
834
|
+
self, conn: sqlite3.Connection, item_id: int, field_name: str, value: Any
|
|
835
|
+
):
|
|
813
836
|
"""Set field value for item."""
|
|
814
837
|
field_id = self._get_field_id(field_name)
|
|
815
838
|
if not field_id:
|
|
816
839
|
logger.warning(f"Unknown field: {field_name}")
|
|
817
840
|
return
|
|
818
|
-
|
|
841
|
+
|
|
819
842
|
value_id = self._get_or_create_value(conn, value)
|
|
820
843
|
if value_id:
|
|
821
|
-
conn.execute(
|
|
844
|
+
conn.execute(
|
|
845
|
+
"""
|
|
822
846
|
INSERT OR REPLACE INTO itemData (itemID, fieldID, valueID)
|
|
823
847
|
VALUES (?, ?, ?)
|
|
824
|
-
""",
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
848
|
+
""",
|
|
849
|
+
(item_id, field_id, value_id),
|
|
850
|
+
)
|
|
851
|
+
|
|
852
|
+
def _track_field_source(
|
|
853
|
+
self,
|
|
854
|
+
conn: sqlite3.Connection,
|
|
855
|
+
item_id: int,
|
|
856
|
+
field_name: str,
|
|
857
|
+
source: str,
|
|
858
|
+
confidence: float = 1.0,
|
|
859
|
+
):
|
|
828
860
|
"""Track metadata source for field."""
|
|
829
|
-
conn.execute(
|
|
861
|
+
conn.execute(
|
|
862
|
+
"""
|
|
830
863
|
INSERT OR REPLACE INTO scitex_field_sources
|
|
831
864
|
(itemID, fieldName, source, confidence)
|
|
832
865
|
VALUES (?, ?, ?, ?)
|
|
833
|
-
""",
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
866
|
+
""",
|
|
867
|
+
(item_id, field_name, source, confidence),
|
|
868
|
+
)
|
|
869
|
+
|
|
870
|
+
def _add_creator(
|
|
871
|
+
self, conn: sqlite3.Connection, item_id: int, author_name: str, order_index: int
|
|
872
|
+
):
|
|
837
873
|
"""Add creator to item."""
|
|
838
874
|
# Parse name
|
|
839
875
|
if "," in author_name:
|
|
@@ -846,71 +882,78 @@ class ZoteroCompatibleDB:
|
|
|
846
882
|
first_name = author_name
|
|
847
883
|
last_name = ""
|
|
848
884
|
field_mode = 1
|
|
849
|
-
|
|
885
|
+
|
|
850
886
|
# Get or create creator
|
|
851
887
|
cursor = conn.execute(
|
|
852
888
|
"SELECT creatorID FROM creators WHERE firstName = ? AND lastName = ?",
|
|
853
|
-
(first_name, last_name)
|
|
889
|
+
(first_name, last_name),
|
|
854
890
|
)
|
|
855
891
|
row = cursor.fetchone()
|
|
856
|
-
|
|
892
|
+
|
|
857
893
|
if row:
|
|
858
894
|
creator_id = row["creatorID"]
|
|
859
895
|
else:
|
|
860
896
|
cursor = conn.execute(
|
|
861
897
|
"INSERT INTO creators (firstName, lastName, fieldMode) VALUES (?, ?, ?)",
|
|
862
|
-
(first_name, last_name, field_mode)
|
|
898
|
+
(first_name, last_name, field_mode),
|
|
863
899
|
)
|
|
864
900
|
creator_id = cursor.lastrowid
|
|
865
|
-
|
|
901
|
+
|
|
866
902
|
# Link to item
|
|
867
|
-
conn.execute(
|
|
903
|
+
conn.execute(
|
|
904
|
+
"""
|
|
868
905
|
INSERT OR REPLACE INTO itemCreators
|
|
869
906
|
(itemID, creatorID, creatorTypeID, orderIndex)
|
|
870
907
|
VALUES (?, ?, 1, ?)
|
|
871
|
-
""",
|
|
872
|
-
|
|
908
|
+
""",
|
|
909
|
+
(item_id, creator_id, order_index),
|
|
910
|
+
)
|
|
911
|
+
|
|
873
912
|
def _add_tag(self, conn: sqlite3.Connection, item_id: int, tag_name: str):
|
|
874
913
|
"""Add tag to item."""
|
|
875
914
|
# Get or create tag
|
|
876
915
|
cursor = conn.execute(
|
|
877
|
-
"SELECT tagID FROM tags WHERE name = ?",
|
|
878
|
-
(tag_name.lower(),)
|
|
916
|
+
"SELECT tagID FROM tags WHERE name = ?", (tag_name.lower(),)
|
|
879
917
|
)
|
|
880
918
|
row = cursor.fetchone()
|
|
881
|
-
|
|
919
|
+
|
|
882
920
|
if row:
|
|
883
921
|
tag_id = row["tagID"]
|
|
884
922
|
else:
|
|
885
923
|
cursor = conn.execute(
|
|
886
|
-
"INSERT INTO tags (name) VALUES (?)",
|
|
887
|
-
(tag_name.lower(),)
|
|
924
|
+
"INSERT INTO tags (name) VALUES (?)", (tag_name.lower(),)
|
|
888
925
|
)
|
|
889
926
|
tag_id = cursor.lastrowid
|
|
890
|
-
|
|
927
|
+
|
|
891
928
|
# Link to item
|
|
892
|
-
conn.execute(
|
|
929
|
+
conn.execute(
|
|
930
|
+
"""
|
|
893
931
|
INSERT OR IGNORE INTO itemTags (itemID, tagID)
|
|
894
932
|
VALUES (?, ?)
|
|
895
|
-
""",
|
|
896
|
-
|
|
933
|
+
""",
|
|
934
|
+
(item_id, tag_id),
|
|
935
|
+
)
|
|
936
|
+
|
|
897
937
|
def update_item_doi(self, item_id: int, doi: str, source: str) -> bool:
|
|
898
938
|
"""Update item with resolved DOI (step 2 of workflow)."""
|
|
899
939
|
with self._get_connection() as conn:
|
|
900
940
|
self._set_item_field(conn, item_id, "DOI", doi)
|
|
901
941
|
self._track_field_source(conn, item_id, "DOI", source)
|
|
902
|
-
|
|
942
|
+
|
|
903
943
|
# Update enrichment status
|
|
904
|
-
conn.execute(
|
|
944
|
+
conn.execute(
|
|
945
|
+
"""
|
|
905
946
|
UPDATE scitex_enrichment_status
|
|
906
947
|
SET doi_resolved = 1
|
|
907
948
|
WHERE itemID = ?
|
|
908
|
-
""",
|
|
909
|
-
|
|
949
|
+
""",
|
|
950
|
+
(item_id,),
|
|
951
|
+
)
|
|
952
|
+
|
|
910
953
|
conn.commit()
|
|
911
954
|
logger.info(f"Updated item {item_id} with DOI: {doi}")
|
|
912
955
|
return True
|
|
913
|
-
|
|
956
|
+
|
|
914
957
|
def enrich_item_metadata(self, item_id: int, metadata: Dict[str, Any]) -> bool:
|
|
915
958
|
"""Enrich item with metadata from various sources (step 3 of workflow)."""
|
|
916
959
|
with self._get_connection() as conn:
|
|
@@ -918,105 +961,125 @@ class ZoteroCompatibleDB:
|
|
|
918
961
|
for field_name, value in metadata.items():
|
|
919
962
|
if field_name.endswith("_source"):
|
|
920
963
|
continue
|
|
921
|
-
|
|
964
|
+
|
|
922
965
|
if value:
|
|
923
966
|
self._set_item_field(conn, item_id, field_name, value)
|
|
924
|
-
|
|
967
|
+
|
|
925
968
|
# Track source
|
|
926
969
|
source = metadata.get(f"{field_name}_source", "enrichment")
|
|
927
970
|
self._track_field_source(conn, item_id, field_name, source)
|
|
928
|
-
|
|
971
|
+
|
|
929
972
|
# Update modified timestamp
|
|
930
|
-
conn.execute(
|
|
973
|
+
conn.execute(
|
|
974
|
+
"""
|
|
931
975
|
UPDATE items
|
|
932
976
|
SET dateModified = CURRENT_TIMESTAMP
|
|
933
977
|
WHERE itemID = ?
|
|
934
|
-
""",
|
|
935
|
-
|
|
978
|
+
""",
|
|
979
|
+
(item_id,),
|
|
980
|
+
)
|
|
981
|
+
|
|
936
982
|
# Update enrichment status
|
|
937
|
-
conn.execute(
|
|
983
|
+
conn.execute(
|
|
984
|
+
"""
|
|
938
985
|
UPDATE scitex_enrichment_status
|
|
939
986
|
SET metadata_enriched = 1, last_enrichment = CURRENT_TIMESTAMP
|
|
940
987
|
WHERE itemID = ?
|
|
941
|
-
""",
|
|
942
|
-
|
|
988
|
+
""",
|
|
989
|
+
(item_id,),
|
|
990
|
+
)
|
|
991
|
+
|
|
943
992
|
conn.commit()
|
|
944
993
|
logger.info(f"Enriched item {item_id} with {len(metadata)} fields")
|
|
945
994
|
return True
|
|
946
|
-
|
|
947
|
-
def attach_pdf(
|
|
948
|
-
|
|
995
|
+
|
|
996
|
+
def attach_pdf(
|
|
997
|
+
self, parent_item_id: int, pdf_path: Path, title: str = "Full Text PDF"
|
|
998
|
+
) -> int:
|
|
949
999
|
"""Attach PDF to item (Zotero-style)."""
|
|
950
1000
|
if not pdf_path.exists():
|
|
951
1001
|
logger.error(f"PDF not found: {pdf_path}")
|
|
952
1002
|
return None
|
|
953
|
-
|
|
1003
|
+
|
|
954
1004
|
with self._get_connection() as conn:
|
|
955
1005
|
# Get parent key for storage
|
|
956
1006
|
cursor = conn.execute(
|
|
957
|
-
"SELECT key FROM items WHERE itemID = ?",
|
|
958
|
-
(parent_item_id,)
|
|
1007
|
+
"SELECT key FROM items WHERE itemID = ?", (parent_item_id,)
|
|
959
1008
|
)
|
|
960
1009
|
parent = cursor.fetchone()
|
|
961
1010
|
if not parent:
|
|
962
1011
|
logger.error(f"Parent item {parent_item_id} not found")
|
|
963
1012
|
return None
|
|
964
|
-
|
|
1013
|
+
|
|
965
1014
|
parent_key = parent["key"]
|
|
966
|
-
|
|
1015
|
+
|
|
967
1016
|
# Create attachment item
|
|
968
1017
|
attachment_key = self._generate_key()
|
|
969
|
-
cursor = conn.execute(
|
|
1018
|
+
cursor = conn.execute(
|
|
1019
|
+
"""
|
|
970
1020
|
INSERT INTO items (itemTypeID, libraryID, key)
|
|
971
1021
|
VALUES (14, 1, ?)
|
|
972
|
-
""",
|
|
1022
|
+
""",
|
|
1023
|
+
(attachment_key,),
|
|
1024
|
+
) # 14 = attachment
|
|
973
1025
|
attachment_id = cursor.lastrowid
|
|
974
|
-
|
|
1026
|
+
|
|
975
1027
|
# Copy PDF to storage
|
|
976
1028
|
storage_path = self.storage_dir / parent_key
|
|
977
1029
|
storage_path.mkdir(exist_ok=True)
|
|
978
|
-
|
|
1030
|
+
|
|
979
1031
|
dest_path = storage_path / f"{attachment_key}.pdf"
|
|
980
1032
|
shutil.copy2(pdf_path, dest_path)
|
|
981
|
-
|
|
1033
|
+
|
|
982
1034
|
# Calculate hash
|
|
983
|
-
with open(dest_path,
|
|
1035
|
+
with open(dest_path, "rb") as f:
|
|
984
1036
|
content = f.read()
|
|
985
1037
|
storage_hash = hashlib.md5(content).hexdigest()
|
|
986
|
-
|
|
1038
|
+
|
|
987
1039
|
# Create attachment record
|
|
988
|
-
conn.execute(
|
|
1040
|
+
conn.execute(
|
|
1041
|
+
"""
|
|
989
1042
|
INSERT INTO itemAttachments
|
|
990
1043
|
(itemID, parentItemID, linkMode, contentType, path, storageHash)
|
|
991
1044
|
VALUES (?, ?, 0, 'application/pdf', ?, ?)
|
|
992
|
-
""",
|
|
993
|
-
|
|
1045
|
+
""",
|
|
1046
|
+
(
|
|
1047
|
+
attachment_id,
|
|
1048
|
+
parent_item_id,
|
|
1049
|
+
f"storage:{attachment_key}.pdf",
|
|
1050
|
+
storage_hash,
|
|
1051
|
+
),
|
|
1052
|
+
)
|
|
1053
|
+
|
|
994
1054
|
# Set title
|
|
995
1055
|
self._set_item_field(conn, attachment_id, "title", title)
|
|
996
|
-
|
|
1056
|
+
|
|
997
1057
|
# Update enrichment status
|
|
998
|
-
conn.execute(
|
|
1058
|
+
conn.execute(
|
|
1059
|
+
"""
|
|
999
1060
|
UPDATE scitex_enrichment_status
|
|
1000
1061
|
SET pdf_download = 1
|
|
1001
1062
|
WHERE itemID = ?
|
|
1002
|
-
""",
|
|
1003
|
-
|
|
1063
|
+
""",
|
|
1064
|
+
(parent_item_id,),
|
|
1065
|
+
)
|
|
1066
|
+
|
|
1004
1067
|
conn.commit()
|
|
1005
|
-
|
|
1068
|
+
|
|
1006
1069
|
logger.info(f"Attached PDF to item {parent_item_id}")
|
|
1007
1070
|
return attachment_id
|
|
1008
|
-
|
|
1071
|
+
|
|
1009
1072
|
def get_items_needing_enrichment(self, stage: str = "doi") -> List[Dict[str, Any]]:
|
|
1010
1073
|
"""Get items that need enrichment at specific stage."""
|
|
1011
1074
|
stage_conditions = {
|
|
1012
1075
|
"doi": "doi_resolved = 0",
|
|
1013
1076
|
"metadata": "doi_resolved = 1 AND metadata_enriched = 0",
|
|
1014
1077
|
"pdf": "metadata_enriched = 1 AND pdf_download = 0",
|
|
1015
|
-
"fulltext": "pdf_download = 1 AND fulltext_extracted = 0"
|
|
1078
|
+
"fulltext": "pdf_download = 1 AND fulltext_extracted = 0",
|
|
1016
1079
|
}
|
|
1017
|
-
|
|
1080
|
+
|
|
1018
1081
|
condition = stage_conditions.get(stage, "1=1")
|
|
1019
|
-
|
|
1082
|
+
|
|
1020
1083
|
with self._get_connection() as conn:
|
|
1021
1084
|
cursor = conn.execute(f"""
|
|
1022
1085
|
SELECT i.itemID, i.key,
|
|
@@ -1033,27 +1096,27 @@ class ZoteroCompatibleDB:
|
|
|
1033
1096
|
AND {condition}
|
|
1034
1097
|
ORDER BY i.dateAdded
|
|
1035
1098
|
""")
|
|
1036
|
-
|
|
1099
|
+
|
|
1037
1100
|
return [dict(row) for row in cursor]
|
|
1038
|
-
|
|
1101
|
+
|
|
1039
1102
|
def export_to_zotero_rdf(self, output_path: Path):
|
|
1040
1103
|
"""Export database in Zotero RDF format."""
|
|
1041
1104
|
# Implementation would generate Zotero RDF/XML format
|
|
1042
1105
|
# This is a placeholder for the full implementation
|
|
1043
1106
|
logger.info(f"Exporting to Zotero RDF: {output_path}")
|
|
1044
|
-
|
|
1107
|
+
|
|
1045
1108
|
def import_from_zotero(self, zotero_db_path: Path):
|
|
1046
1109
|
"""Import from existing Zotero database."""
|
|
1047
1110
|
logger.info(f"Importing from Zotero: {zotero_db_path}")
|
|
1048
|
-
|
|
1111
|
+
|
|
1049
1112
|
# This would copy relevant tables and data
|
|
1050
1113
|
# Placeholder for full implementation
|
|
1051
|
-
|
|
1114
|
+
|
|
1052
1115
|
def get_statistics(self) -> Dict[str, Any]:
|
|
1053
1116
|
"""Get database statistics."""
|
|
1054
1117
|
with self._get_connection() as conn:
|
|
1055
1118
|
stats = {}
|
|
1056
|
-
|
|
1119
|
+
|
|
1057
1120
|
# Item counts by type
|
|
1058
1121
|
cursor = conn.execute("""
|
|
1059
1122
|
SELECT
|
|
@@ -1064,7 +1127,7 @@ class ZoteroCompatibleDB:
|
|
|
1064
1127
|
WHERE itemID NOT IN (SELECT itemID FROM deletedItems)
|
|
1065
1128
|
""")
|
|
1066
1129
|
stats["items"] = dict(cursor.fetchone())
|
|
1067
|
-
|
|
1130
|
+
|
|
1068
1131
|
# Enrichment status
|
|
1069
1132
|
cursor = conn.execute("""
|
|
1070
1133
|
SELECT
|
|
@@ -1075,14 +1138,14 @@ class ZoteroCompatibleDB:
|
|
|
1075
1138
|
FROM scitex_enrichment_status
|
|
1076
1139
|
""")
|
|
1077
1140
|
stats["enrichment"] = dict(cursor.fetchone())
|
|
1078
|
-
|
|
1141
|
+
|
|
1079
1142
|
# Storage info
|
|
1080
1143
|
pdf_count = len(list(self.storage_dir.glob("*/*.pdf")))
|
|
1081
1144
|
stats["storage"] = {
|
|
1082
1145
|
"pdf_count": pdf_count,
|
|
1083
|
-
"storage_path": str(self.storage_dir)
|
|
1146
|
+
"storage_path": str(self.storage_dir),
|
|
1084
1147
|
}
|
|
1085
|
-
|
|
1148
|
+
|
|
1086
1149
|
return stats
|
|
1087
1150
|
|
|
1088
1151
|
|
|
@@ -1095,7 +1158,7 @@ if __name__ == "__main__":
|
|
|
1095
1158
|
print("3. Enrich metadata using DOI")
|
|
1096
1159
|
print("4. Download and attach PDF")
|
|
1097
1160
|
print("5. Extract full text for search")
|
|
1098
|
-
|
|
1161
|
+
|
|
1099
1162
|
print("\nExample usage:")
|
|
1100
1163
|
print("""
|
|
1101
1164
|
# Initialize
|
|
@@ -1128,4 +1191,4 @@ if __name__ == "__main__":
|
|
|
1128
1191
|
needs_metadata = db.get_items_needing_enrichment("metadata")
|
|
1129
1192
|
""")
|
|
1130
1193
|
|
|
1131
|
-
# EOF
|
|
1194
|
+
# EOF
|