scitex 2.0.0__py2.py3-none-any.whl → 2.1.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (704) hide show
  1. scitex/__init__.py +53 -15
  2. scitex/__main__.py +72 -26
  3. scitex/__version__.py +1 -1
  4. scitex/_sh.py +145 -23
  5. scitex/ai/__init__.py +30 -16
  6. scitex/ai/_gen_ai/_Anthropic.py +5 -7
  7. scitex/ai/_gen_ai/_BaseGenAI.py +2 -2
  8. scitex/ai/_gen_ai/_DeepSeek.py +10 -2
  9. scitex/ai/_gen_ai/_Google.py +2 -2
  10. scitex/ai/_gen_ai/_Llama.py +2 -2
  11. scitex/ai/_gen_ai/_OpenAI.py +2 -2
  12. scitex/ai/_gen_ai/_PARAMS.py +51 -65
  13. scitex/ai/_gen_ai/_Perplexity.py +2 -2
  14. scitex/ai/_gen_ai/__init__.py +25 -14
  15. scitex/ai/_gen_ai/_format_output_func.py +4 -4
  16. scitex/ai/classification/{classifier_server.py → Classifier.py} +5 -5
  17. scitex/ai/classification/CrossValidationExperiment.py +374 -0
  18. scitex/ai/classification/__init__.py +43 -4
  19. scitex/ai/classification/reporters/_BaseClassificationReporter.py +281 -0
  20. scitex/ai/classification/reporters/_ClassificationReporter.py +773 -0
  21. scitex/ai/classification/reporters/_MultiClassificationReporter.py +406 -0
  22. scitex/ai/classification/reporters/_SingleClassificationReporter.py +1834 -0
  23. scitex/ai/classification/reporters/__init__.py +11 -0
  24. scitex/ai/classification/reporters/reporter_utils/_Plotter.py +1028 -0
  25. scitex/ai/classification/reporters/reporter_utils/__init__.py +80 -0
  26. scitex/ai/classification/reporters/reporter_utils/aggregation.py +457 -0
  27. scitex/ai/classification/reporters/reporter_utils/data_models.py +313 -0
  28. scitex/ai/classification/reporters/reporter_utils/reporting.py +1056 -0
  29. scitex/ai/classification/reporters/reporter_utils/storage.py +221 -0
  30. scitex/ai/classification/reporters/reporter_utils/validation.py +395 -0
  31. scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +568 -0
  32. scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +688 -0
  33. scitex/ai/classification/timeseries/_TimeSeriesMetadata.py +139 -0
  34. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +1716 -0
  35. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +1685 -0
  36. scitex/ai/classification/timeseries/_TimeSeriesStrategy.py +84 -0
  37. scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +610 -0
  38. scitex/ai/classification/timeseries/__init__.py +39 -0
  39. scitex/ai/classification/timeseries/_normalize_timestamp.py +436 -0
  40. scitex/ai/clustering/_umap.py +2 -2
  41. scitex/ai/feature_extraction/vit.py +1 -0
  42. scitex/ai/feature_selection/__init__.py +30 -0
  43. scitex/ai/feature_selection/feature_selection.py +364 -0
  44. scitex/ai/loss/multi_task_loss.py +1 -1
  45. scitex/ai/metrics/__init__.py +51 -4
  46. scitex/ai/metrics/_calc_bacc.py +61 -0
  47. scitex/ai/metrics/_calc_bacc_from_conf_mat.py +38 -0
  48. scitex/ai/metrics/_calc_clf_report.py +78 -0
  49. scitex/ai/metrics/_calc_conf_mat.py +93 -0
  50. scitex/ai/metrics/_calc_feature_importance.py +183 -0
  51. scitex/ai/metrics/_calc_mcc.py +61 -0
  52. scitex/ai/metrics/_calc_pre_rec_auc.py +116 -0
  53. scitex/ai/metrics/_calc_roc_auc.py +110 -0
  54. scitex/ai/metrics/_calc_seizure_prediction_metrics.py +490 -0
  55. scitex/ai/metrics/{silhoute_score_block.py → _calc_silhouette_score.py} +15 -8
  56. scitex/ai/metrics/_normalize_labels.py +83 -0
  57. scitex/ai/plt/__init__.py +47 -8
  58. scitex/ai/plt/{_conf_mat.py → _plot_conf_mat.py} +158 -87
  59. scitex/ai/plt/_plot_feature_importance.py +323 -0
  60. scitex/ai/plt/_plot_learning_curve.py +345 -0
  61. scitex/ai/plt/_plot_optuna_study.py +225 -0
  62. scitex/ai/plt/_plot_pre_rec_curve.py +290 -0
  63. scitex/ai/plt/_plot_roc_curve.py +255 -0
  64. scitex/ai/training/{learning_curve_logger.py → _LearningCurveLogger.py} +197 -213
  65. scitex/ai/training/__init__.py +2 -2
  66. scitex/ai/utils/grid_search.py +3 -3
  67. scitex/benchmark/__init__.py +52 -0
  68. scitex/benchmark/benchmark.py +400 -0
  69. scitex/benchmark/monitor.py +370 -0
  70. scitex/benchmark/profiler.py +297 -0
  71. scitex/browser/__init__.py +48 -0
  72. scitex/browser/automation/CookieHandler.py +216 -0
  73. scitex/browser/automation/__init__.py +7 -0
  74. scitex/browser/collaboration/__init__.py +55 -0
  75. scitex/browser/collaboration/auth_helpers.py +94 -0
  76. scitex/browser/collaboration/collaborative_agent.py +136 -0
  77. scitex/browser/collaboration/credential_manager.py +188 -0
  78. scitex/browser/collaboration/interactive_panel.py +400 -0
  79. scitex/browser/collaboration/persistent_browser.py +170 -0
  80. scitex/browser/collaboration/shared_session.py +383 -0
  81. scitex/browser/collaboration/standard_interactions.py +246 -0
  82. scitex/browser/collaboration/visual_feedback.py +181 -0
  83. scitex/browser/core/BrowserMixin.py +326 -0
  84. scitex/browser/core/ChromeProfileManager.py +446 -0
  85. scitex/browser/core/__init__.py +9 -0
  86. scitex/browser/debugging/__init__.py +18 -0
  87. scitex/browser/debugging/_browser_logger.py +657 -0
  88. scitex/browser/debugging/_highlight_element.py +143 -0
  89. scitex/browser/debugging/_show_grid.py +154 -0
  90. scitex/browser/interaction/__init__.py +24 -0
  91. scitex/browser/interaction/click_center.py +149 -0
  92. scitex/browser/interaction/click_with_fallbacks.py +206 -0
  93. scitex/browser/interaction/close_popups.py +498 -0
  94. scitex/browser/interaction/fill_with_fallbacks.py +209 -0
  95. scitex/browser/pdf/__init__.py +14 -0
  96. scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +200 -0
  97. scitex/browser/pdf/detect_chrome_pdf_viewer.py +198 -0
  98. scitex/browser/remote/CaptchaHandler.py +434 -0
  99. scitex/browser/remote/ZenRowsAPIClient.py +347 -0
  100. scitex/browser/remote/ZenRowsBrowserManager.py +570 -0
  101. scitex/browser/remote/__init__.py +11 -0
  102. scitex/browser/stealth/HumanBehavior.py +344 -0
  103. scitex/browser/stealth/StealthManager.py +1008 -0
  104. scitex/browser/stealth/__init__.py +9 -0
  105. scitex/browser/template.py +122 -0
  106. scitex/capture/__init__.py +110 -0
  107. scitex/capture/__main__.py +25 -0
  108. scitex/capture/capture.py +848 -0
  109. scitex/capture/cli.py +233 -0
  110. scitex/capture/gif.py +344 -0
  111. scitex/capture/mcp_server.py +961 -0
  112. scitex/capture/session.py +70 -0
  113. scitex/capture/utils.py +705 -0
  114. scitex/cli/__init__.py +17 -0
  115. scitex/cli/cloud.py +447 -0
  116. scitex/cli/main.py +42 -0
  117. scitex/cli/scholar.py +280 -0
  118. scitex/context/_suppress_output.py +5 -3
  119. scitex/db/__init__.py +30 -3
  120. scitex/db/__main__.py +75 -0
  121. scitex/db/_check_health.py +381 -0
  122. scitex/db/_delete_duplicates.py +25 -386
  123. scitex/db/_inspect.py +335 -114
  124. scitex/db/_inspect_optimized.py +301 -0
  125. scitex/db/{_PostgreSQL.py → _postgresql/_PostgreSQL.py} +3 -3
  126. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BackupMixin.py +1 -1
  127. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BatchMixin.py +1 -1
  128. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BlobMixin.py +1 -1
  129. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_ConnectionMixin.py +1 -1
  130. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_MaintenanceMixin.py +1 -1
  131. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_QueryMixin.py +1 -1
  132. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_SchemaMixin.py +1 -1
  133. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_TransactionMixin.py +1 -1
  134. scitex/db/_postgresql/__init__.py +6 -0
  135. scitex/db/_sqlite3/_SQLite3.py +210 -0
  136. scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin.py +581 -0
  137. scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin_v01-need-_hash-col.py +517 -0
  138. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_BatchMixin.py +1 -1
  139. scitex/db/_sqlite3/_SQLite3Mixins/_BlobMixin.py +281 -0
  140. scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin.py +548 -0
  141. scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin_v01-indentation-issues.py +583 -0
  142. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_ConnectionMixin.py +29 -13
  143. scitex/db/_sqlite3/_SQLite3Mixins/_GitMixin.py +583 -0
  144. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_ImportExportMixin.py +1 -1
  145. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_IndexMixin.py +1 -1
  146. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_MaintenanceMixin.py +2 -1
  147. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_QueryMixin.py +37 -10
  148. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_RowMixin.py +46 -6
  149. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_TableMixin.py +56 -10
  150. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_TransactionMixin.py +1 -1
  151. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/__init__.py +14 -2
  152. scitex/db/_sqlite3/__init__.py +7 -0
  153. scitex/db/_sqlite3/_delete_duplicates.py +274 -0
  154. scitex/decorators/__init__.py +2 -0
  155. scitex/decorators/_cache_disk.py +13 -5
  156. scitex/decorators/_cache_disk_async.py +49 -0
  157. scitex/decorators/_deprecated.py +175 -10
  158. scitex/decorators/_timeout.py +1 -1
  159. scitex/dev/_analyze_code_flow.py +2 -2
  160. scitex/dict/_DotDict.py +73 -15
  161. scitex/dict/_DotDict_v01-not-handling-recursive-instantiations.py +442 -0
  162. scitex/dict/_DotDict_v02-not-serializing-Path-object.py +446 -0
  163. scitex/dict/__init__.py +2 -0
  164. scitex/dict/_flatten.py +27 -0
  165. scitex/dsp/_crop.py +2 -2
  166. scitex/dsp/_demo_sig.py +2 -2
  167. scitex/dsp/_detect_ripples.py +2 -2
  168. scitex/dsp/_hilbert.py +2 -2
  169. scitex/dsp/_listen.py +6 -6
  170. scitex/dsp/_modulation_index.py +2 -2
  171. scitex/dsp/_pac.py +1 -1
  172. scitex/dsp/_psd.py +2 -2
  173. scitex/dsp/_resample.py +2 -1
  174. scitex/dsp/_time.py +3 -2
  175. scitex/dsp/_wavelet.py +3 -2
  176. scitex/dsp/add_noise.py +2 -2
  177. scitex/dsp/example.py +1 -0
  178. scitex/dsp/filt.py +10 -9
  179. scitex/dsp/template.py +3 -2
  180. scitex/dsp/utils/_differential_bandpass_filters.py +1 -1
  181. scitex/dsp/utils/pac.py +2 -2
  182. scitex/dt/_normalize_timestamp.py +432 -0
  183. scitex/errors.py +572 -0
  184. scitex/gen/_DimHandler.py +2 -2
  185. scitex/gen/__init__.py +37 -7
  186. scitex/gen/_deprecated_close.py +80 -0
  187. scitex/gen/_deprecated_start.py +26 -0
  188. scitex/gen/_detect_environment.py +152 -0
  189. scitex/gen/_detect_notebook_path.py +169 -0
  190. scitex/gen/_embed.py +6 -2
  191. scitex/gen/_get_notebook_path.py +257 -0
  192. scitex/gen/_less.py +1 -1
  193. scitex/gen/_list_packages.py +2 -2
  194. scitex/gen/_norm.py +44 -9
  195. scitex/gen/_norm_cache.py +269 -0
  196. scitex/gen/_src.py +3 -5
  197. scitex/gen/_title_case.py +3 -3
  198. scitex/io/__init__.py +28 -6
  199. scitex/io/_glob.py +13 -7
  200. scitex/io/_load.py +108 -21
  201. scitex/io/_load_cache.py +303 -0
  202. scitex/io/_load_configs.py +40 -15
  203. scitex/io/{_H5Explorer.py → _load_modules/_H5Explorer.py} +80 -17
  204. scitex/io/_load_modules/_ZarrExplorer.py +114 -0
  205. scitex/io/_load_modules/_bibtex.py +207 -0
  206. scitex/io/_load_modules/_hdf5.py +53 -178
  207. scitex/io/_load_modules/_json.py +5 -3
  208. scitex/io/_load_modules/_pdf.py +871 -16
  209. scitex/io/_load_modules/_sqlite3.py +15 -0
  210. scitex/io/_load_modules/_txt.py +41 -12
  211. scitex/io/_load_modules/_yaml.py +4 -3
  212. scitex/io/_load_modules/_zarr.py +126 -0
  213. scitex/io/_save.py +429 -171
  214. scitex/io/_save_modules/__init__.py +6 -0
  215. scitex/io/_save_modules/_bibtex.py +194 -0
  216. scitex/io/_save_modules/_csv.py +8 -4
  217. scitex/io/_save_modules/_excel.py +174 -15
  218. scitex/io/_save_modules/_hdf5.py +251 -226
  219. scitex/io/_save_modules/_image.py +1 -3
  220. scitex/io/_save_modules/_json.py +49 -4
  221. scitex/io/_save_modules/_listed_dfs_as_csv.py +1 -3
  222. scitex/io/_save_modules/_listed_scalars_as_csv.py +1 -3
  223. scitex/io/_save_modules/_tex.py +277 -0
  224. scitex/io/_save_modules/_yaml.py +42 -3
  225. scitex/io/_save_modules/_zarr.py +160 -0
  226. scitex/io/utils/__init__.py +20 -0
  227. scitex/io/utils/h5_to_zarr.py +616 -0
  228. scitex/linalg/_geometric_median.py +6 -2
  229. scitex/{gen/_tee.py → logging/_Tee.py} +43 -84
  230. scitex/logging/__init__.py +122 -0
  231. scitex/logging/_config.py +158 -0
  232. scitex/logging/_context.py +103 -0
  233. scitex/logging/_formatters.py +128 -0
  234. scitex/logging/_handlers.py +64 -0
  235. scitex/logging/_levels.py +35 -0
  236. scitex/logging/_logger.py +163 -0
  237. scitex/logging/_print_capture.py +95 -0
  238. scitex/ml/__init__.py +69 -0
  239. scitex/{ai/genai/anthropic.py → ml/_gen_ai/_Anthropic.py} +13 -19
  240. scitex/{ai/genai/base_genai.py → ml/_gen_ai/_BaseGenAI.py} +5 -5
  241. scitex/{ai/genai/deepseek.py → ml/_gen_ai/_DeepSeek.py} +11 -16
  242. scitex/{ai/genai/google.py → ml/_gen_ai/_Google.py} +7 -15
  243. scitex/{ai/genai/groq.py → ml/_gen_ai/_Groq.py} +1 -8
  244. scitex/{ai/genai/llama.py → ml/_gen_ai/_Llama.py} +3 -16
  245. scitex/{ai/genai/openai.py → ml/_gen_ai/_OpenAI.py} +3 -3
  246. scitex/{ai/genai/params.py → ml/_gen_ai/_PARAMS.py} +51 -65
  247. scitex/{ai/genai/perplexity.py → ml/_gen_ai/_Perplexity.py} +3 -14
  248. scitex/ml/_gen_ai/__init__.py +43 -0
  249. scitex/{ai/genai/calc_cost.py → ml/_gen_ai/_calc_cost.py} +1 -1
  250. scitex/{ai/genai/format_output_func.py → ml/_gen_ai/_format_output_func.py} +4 -4
  251. scitex/{ai/genai/genai_factory.py → ml/_gen_ai/_genai_factory.py} +8 -8
  252. scitex/ml/activation/__init__.py +8 -0
  253. scitex/ml/activation/_define.py +11 -0
  254. scitex/{ai/classifier_server.py → ml/classification/Classifier.py} +5 -5
  255. scitex/ml/classification/CrossValidationExperiment.py +374 -0
  256. scitex/ml/classification/__init__.py +46 -0
  257. scitex/ml/classification/reporters/_BaseClassificationReporter.py +281 -0
  258. scitex/ml/classification/reporters/_ClassificationReporter.py +773 -0
  259. scitex/ml/classification/reporters/_MultiClassificationReporter.py +406 -0
  260. scitex/ml/classification/reporters/_SingleClassificationReporter.py +1834 -0
  261. scitex/ml/classification/reporters/__init__.py +11 -0
  262. scitex/ml/classification/reporters/reporter_utils/_Plotter.py +1028 -0
  263. scitex/ml/classification/reporters/reporter_utils/__init__.py +80 -0
  264. scitex/ml/classification/reporters/reporter_utils/aggregation.py +457 -0
  265. scitex/ml/classification/reporters/reporter_utils/data_models.py +313 -0
  266. scitex/ml/classification/reporters/reporter_utils/reporting.py +1056 -0
  267. scitex/ml/classification/reporters/reporter_utils/storage.py +221 -0
  268. scitex/ml/classification/reporters/reporter_utils/validation.py +395 -0
  269. scitex/ml/classification/timeseries/_TimeSeriesBlockingSplit.py +568 -0
  270. scitex/ml/classification/timeseries/_TimeSeriesCalendarSplit.py +688 -0
  271. scitex/ml/classification/timeseries/_TimeSeriesMetadata.py +139 -0
  272. scitex/ml/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +1716 -0
  273. scitex/ml/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +1685 -0
  274. scitex/ml/classification/timeseries/_TimeSeriesStrategy.py +84 -0
  275. scitex/ml/classification/timeseries/_TimeSeriesStratifiedSplit.py +610 -0
  276. scitex/ml/classification/timeseries/__init__.py +39 -0
  277. scitex/ml/classification/timeseries/_normalize_timestamp.py +436 -0
  278. scitex/ml/clustering/__init__.py +11 -0
  279. scitex/ml/clustering/_pca.py +115 -0
  280. scitex/ml/clustering/_umap.py +376 -0
  281. scitex/ml/feature_extraction/__init__.py +56 -0
  282. scitex/ml/feature_extraction/vit.py +149 -0
  283. scitex/ml/feature_selection/__init__.py +30 -0
  284. scitex/ml/feature_selection/feature_selection.py +364 -0
  285. scitex/ml/loss/_L1L2Losses.py +34 -0
  286. scitex/ml/loss/__init__.py +12 -0
  287. scitex/ml/loss/multi_task_loss.py +47 -0
  288. scitex/ml/metrics/__init__.py +56 -0
  289. scitex/ml/metrics/_calc_bacc.py +61 -0
  290. scitex/ml/metrics/_calc_bacc_from_conf_mat.py +38 -0
  291. scitex/ml/metrics/_calc_clf_report.py +78 -0
  292. scitex/ml/metrics/_calc_conf_mat.py +93 -0
  293. scitex/ml/metrics/_calc_feature_importance.py +183 -0
  294. scitex/ml/metrics/_calc_mcc.py +61 -0
  295. scitex/ml/metrics/_calc_pre_rec_auc.py +116 -0
  296. scitex/ml/metrics/_calc_roc_auc.py +110 -0
  297. scitex/ml/metrics/_calc_seizure_prediction_metrics.py +490 -0
  298. scitex/ml/metrics/_calc_silhouette_score.py +503 -0
  299. scitex/ml/metrics/_normalize_labels.py +83 -0
  300. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/__init__.py +0 -0
  301. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/__init__.py +3 -0
  302. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger.py +207 -0
  303. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger2020.py +238 -0
  304. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger913A.py +215 -0
  305. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/rangerqh.py +184 -0
  306. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/setup.py +24 -0
  307. scitex/ml/optim/__init__.py +13 -0
  308. scitex/ml/optim/_get_set.py +31 -0
  309. scitex/ml/optim/_optimizers.py +71 -0
  310. scitex/ml/plt/__init__.py +60 -0
  311. scitex/ml/plt/_plot_conf_mat.py +663 -0
  312. scitex/ml/plt/_plot_feature_importance.py +323 -0
  313. scitex/ml/plt/_plot_learning_curve.py +345 -0
  314. scitex/ml/plt/_plot_optuna_study.py +225 -0
  315. scitex/ml/plt/_plot_pre_rec_curve.py +290 -0
  316. scitex/ml/plt/_plot_roc_curve.py +255 -0
  317. scitex/ml/sk/__init__.py +11 -0
  318. scitex/ml/sk/_clf.py +58 -0
  319. scitex/ml/sk/_to_sktime.py +100 -0
  320. scitex/ml/sklearn/__init__.py +26 -0
  321. scitex/ml/sklearn/clf.py +58 -0
  322. scitex/ml/sklearn/to_sktime.py +100 -0
  323. scitex/{ai/training/early_stopping.py → ml/training/_EarlyStopping.py} +1 -2
  324. scitex/{ai → ml/training}/_LearningCurveLogger.py +198 -242
  325. scitex/ml/training/__init__.py +7 -0
  326. scitex/ml/utils/__init__.py +22 -0
  327. scitex/ml/utils/_check_params.py +50 -0
  328. scitex/ml/utils/_default_dataset.py +46 -0
  329. scitex/ml/utils/_format_samples_for_sktime.py +26 -0
  330. scitex/ml/utils/_label_encoder.py +134 -0
  331. scitex/ml/utils/_merge_labels.py +22 -0
  332. scitex/ml/utils/_sliding_window_data_augmentation.py +11 -0
  333. scitex/ml/utils/_under_sample.py +51 -0
  334. scitex/ml/utils/_verify_n_gpus.py +16 -0
  335. scitex/ml/utils/grid_search.py +148 -0
  336. scitex/nn/_BNet.py +15 -9
  337. scitex/nn/_Filters.py +2 -2
  338. scitex/nn/_ModulationIndex.py +2 -2
  339. scitex/nn/_PAC.py +1 -1
  340. scitex/nn/_Spectrogram.py +12 -3
  341. scitex/nn/__init__.py +9 -10
  342. scitex/path/__init__.py +18 -0
  343. scitex/path/_clean.py +4 -0
  344. scitex/path/_find.py +9 -4
  345. scitex/path/_symlink.py +348 -0
  346. scitex/path/_version.py +4 -3
  347. scitex/pd/__init__.py +2 -0
  348. scitex/pd/_get_unique.py +99 -0
  349. scitex/plt/__init__.py +114 -5
  350. scitex/plt/_subplots/_AxesWrapper.py +1 -3
  351. scitex/plt/_subplots/_AxisWrapper.py +7 -3
  352. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +47 -13
  353. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +160 -2
  354. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +26 -4
  355. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +322 -0
  356. scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +1 -0
  357. scitex/plt/_subplots/_FigWrapper.py +62 -6
  358. scitex/plt/_subplots/_export_as_csv.py +43 -27
  359. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +5 -4
  360. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +81 -0
  361. scitex/plt/_subplots/_export_as_csv_formatters/_format_bar.py +1 -3
  362. scitex/plt/_subplots/_export_as_csv_formatters/_format_barh.py +20 -5
  363. scitex/plt/_subplots/_export_as_csv_formatters/_format_boxplot.py +1 -3
  364. scitex/plt/_subplots/_export_as_csv_formatters/_format_contour.py +1 -3
  365. scitex/plt/_subplots/_export_as_csv_formatters/_format_errorbar.py +35 -18
  366. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +1 -3
  367. scitex/plt/_subplots/_export_as_csv_formatters/_format_fill.py +1 -3
  368. scitex/plt/_subplots/_export_as_csv_formatters/_format_fill_between.py +1 -3
  369. scitex/plt/_subplots/_export_as_csv_formatters/_format_hist.py +1 -3
  370. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +1 -3
  371. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +1 -3
  372. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +15 -3
  373. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +1 -3
  374. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_conf_mat.py +1 -3
  375. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_ecdf.py +1 -3
  376. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_fillv.py +1 -3
  377. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_heatmap.py +1 -3
  378. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_image.py +1 -3
  379. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_joyplot.py +1 -3
  380. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +1 -3
  381. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_line.py +1 -3
  382. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_mean_ci.py +1 -3
  383. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_mean_std.py +1 -3
  384. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_median_iqr.py +1 -3
  385. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_raster.py +1 -3
  386. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_rectangle.py +1 -3
  387. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +35 -0
  388. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter_hist.py +1 -3
  389. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_shaded_line.py +1 -3
  390. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_violin.py +1 -3
  391. scitex/plt/_subplots/_export_as_csv_formatters/_format_scatter.py +6 -4
  392. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_barplot.py +1 -3
  393. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_boxplot.py +1 -3
  394. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_heatmap.py +1 -3
  395. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_histplot.py +1 -3
  396. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +1 -3
  397. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_kdeplot.py +1 -3
  398. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +1 -3
  399. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +1 -3
  400. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_scatterplot.py +1 -3
  401. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_stripplot.py +1 -3
  402. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_swarmplot.py +1 -3
  403. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_violinplot.py +1 -3
  404. scitex/plt/_subplots/_export_as_csv_formatters/_format_text.py +60 -0
  405. scitex/plt/_subplots/_export_as_csv_formatters/_format_violin.py +1 -3
  406. scitex/plt/_subplots/_export_as_csv_formatters/_format_violinplot.py +1 -3
  407. scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +1 -3
  408. scitex/plt/_subplots/_export_as_csv_formatters.py +56 -59
  409. scitex/plt/ax/_style/_hide_spines.py +1 -3
  410. scitex/plt/ax/_style/_rotate_labels.py +180 -76
  411. scitex/plt/ax/_style/_rotate_labels_v01.py +248 -0
  412. scitex/plt/ax/_style/_set_meta.py +11 -4
  413. scitex/plt/ax/_style/_set_supxyt.py +3 -3
  414. scitex/plt/ax/_style/_set_xyt.py +3 -3
  415. scitex/plt/ax/_style/_share_axes.py +2 -2
  416. scitex/plt/color/__init__.py +4 -4
  417. scitex/plt/color/{_get_colors_from_cmap.py → _get_colors_from_conf_matap.py} +7 -7
  418. scitex/plt/utils/_configure_mpl.py +99 -86
  419. scitex/plt/utils/_histogram_utils.py +1 -3
  420. scitex/plt/utils/_is_valid_axis.py +1 -3
  421. scitex/plt/utils/_scitex_config.py +1 -0
  422. scitex/repro/__init__.py +75 -0
  423. scitex/{reproduce → repro}/_gen_ID.py +1 -1
  424. scitex/{reproduce → repro}/_gen_timestamp.py +1 -1
  425. scitex/repro_rng/_RandomStateManager.py +590 -0
  426. scitex/repro_rng/_RandomStateManager_v01-no-verbose-options.py +414 -0
  427. scitex/repro_rng/__init__.py +39 -0
  428. scitex/reproduce/__init__.py +25 -13
  429. scitex/reproduce/_hash_array.py +22 -0
  430. scitex/resource/_get_processor_usages.py +4 -4
  431. scitex/resource/_get_specs.py +2 -2
  432. scitex/resource/_log_processor_usages.py +2 -2
  433. scitex/rng/_RandomStateManager.py +590 -0
  434. scitex/rng/_RandomStateManager_v01-no-verbose-options.py +414 -0
  435. scitex/rng/__init__.py +39 -0
  436. scitex/scholar/__init__.py +309 -19
  437. scitex/scholar/__main__.py +319 -0
  438. scitex/scholar/auth/ScholarAuthManager.py +308 -0
  439. scitex/scholar/auth/__init__.py +12 -0
  440. scitex/scholar/auth/core/AuthenticationGateway.py +473 -0
  441. scitex/scholar/auth/core/BrowserAuthenticator.py +386 -0
  442. scitex/scholar/auth/core/StrategyResolver.py +309 -0
  443. scitex/scholar/auth/core/__init__.py +16 -0
  444. scitex/scholar/auth/gateway/_OpenURLLinkFinder.py +120 -0
  445. scitex/scholar/auth/gateway/_OpenURLResolver.py +209 -0
  446. scitex/scholar/auth/gateway/__init__.py +38 -0
  447. scitex/scholar/auth/gateway/_resolve_functions.py +101 -0
  448. scitex/scholar/auth/providers/BaseAuthenticator.py +166 -0
  449. scitex/scholar/auth/providers/EZProxyAuthenticator.py +484 -0
  450. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +619 -0
  451. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +686 -0
  452. scitex/scholar/auth/providers/__init__.py +18 -0
  453. scitex/scholar/auth/session/AuthCacheManager.py +189 -0
  454. scitex/scholar/auth/session/SessionManager.py +159 -0
  455. scitex/scholar/auth/session/__init__.py +11 -0
  456. scitex/scholar/auth/sso/BaseSSOAutomator.py +373 -0
  457. scitex/scholar/auth/sso/OpenAthensSSOAutomator.py +378 -0
  458. scitex/scholar/auth/sso/SSOAutomator.py +180 -0
  459. scitex/scholar/auth/sso/UniversityOfMelbourneSSOAutomator.py +380 -0
  460. scitex/scholar/auth/sso/__init__.py +15 -0
  461. scitex/scholar/browser/ScholarBrowserManager.py +705 -0
  462. scitex/scholar/browser/__init__.py +38 -0
  463. scitex/scholar/browser/utils/__init__.py +13 -0
  464. scitex/scholar/browser/utils/click_and_wait.py +205 -0
  465. scitex/scholar/browser/utils/close_unwanted_pages.py +140 -0
  466. scitex/scholar/browser/utils/wait_redirects.py +732 -0
  467. scitex/scholar/config/PublisherRules.py +132 -0
  468. scitex/scholar/config/ScholarConfig.py +126 -0
  469. scitex/scholar/config/__init__.py +17 -0
  470. scitex/scholar/core/Paper.py +627 -0
  471. scitex/scholar/core/Papers.py +722 -0
  472. scitex/scholar/core/Scholar.py +1975 -0
  473. scitex/scholar/core/__init__.py +9 -0
  474. scitex/scholar/impact_factor/ImpactFactorEngine.py +204 -0
  475. scitex/scholar/impact_factor/__init__.py +20 -0
  476. scitex/scholar/impact_factor/estimation/ImpactFactorEstimationEngine.py +0 -0
  477. scitex/scholar/impact_factor/estimation/__init__.py +40 -0
  478. scitex/scholar/impact_factor/estimation/build_database.py +0 -0
  479. scitex/scholar/impact_factor/estimation/core/__init__.py +28 -0
  480. scitex/scholar/impact_factor/estimation/core/cache_manager.py +523 -0
  481. scitex/scholar/impact_factor/estimation/core/calculator.py +355 -0
  482. scitex/scholar/impact_factor/estimation/core/journal_matcher.py +428 -0
  483. scitex/scholar/integration/__init__.py +59 -0
  484. scitex/scholar/integration/base.py +502 -0
  485. scitex/scholar/integration/mendeley/__init__.py +22 -0
  486. scitex/scholar/integration/mendeley/exporter.py +166 -0
  487. scitex/scholar/integration/mendeley/importer.py +236 -0
  488. scitex/scholar/integration/mendeley/linker.py +79 -0
  489. scitex/scholar/integration/mendeley/mapper.py +212 -0
  490. scitex/scholar/integration/zotero/__init__.py +27 -0
  491. scitex/scholar/integration/zotero/__main__.py +264 -0
  492. scitex/scholar/integration/zotero/exporter.py +351 -0
  493. scitex/scholar/integration/zotero/importer.py +372 -0
  494. scitex/scholar/integration/zotero/linker.py +415 -0
  495. scitex/scholar/integration/zotero/mapper.py +286 -0
  496. scitex/scholar/metadata_engines/ScholarEngine.py +588 -0
  497. scitex/scholar/metadata_engines/__init__.py +21 -0
  498. scitex/scholar/metadata_engines/individual/ArXivEngine.py +397 -0
  499. scitex/scholar/metadata_engines/individual/CrossRefEngine.py +274 -0
  500. scitex/scholar/metadata_engines/individual/CrossRefLocalEngine.py +263 -0
  501. scitex/scholar/metadata_engines/individual/OpenAlexEngine.py +350 -0
  502. scitex/scholar/metadata_engines/individual/PubMedEngine.py +329 -0
  503. scitex/scholar/metadata_engines/individual/SemanticScholarEngine.py +438 -0
  504. scitex/scholar/metadata_engines/individual/URLDOIEngine.py +410 -0
  505. scitex/scholar/metadata_engines/individual/_BaseDOIEngine.py +487 -0
  506. scitex/scholar/metadata_engines/individual/__init__.py +7 -0
  507. scitex/scholar/metadata_engines/utils/_PubMedConverter.py +469 -0
  508. scitex/scholar/metadata_engines/utils/_URLDOIExtractor.py +283 -0
  509. scitex/scholar/metadata_engines/utils/__init__.py +30 -0
  510. scitex/scholar/metadata_engines/utils/_metadata2bibtex.py +103 -0
  511. scitex/scholar/metadata_engines/utils/_standardize_metadata.py +376 -0
  512. scitex/scholar/pdf_download/ScholarPDFDownloader.py +579 -0
  513. scitex/scholar/pdf_download/__init__.py +5 -0
  514. scitex/scholar/pdf_download/strategies/__init__.py +38 -0
  515. scitex/scholar/pdf_download/strategies/chrome_pdf_viewer.py +376 -0
  516. scitex/scholar/pdf_download/strategies/direct_download.py +131 -0
  517. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +167 -0
  518. scitex/scholar/pdf_download/strategies/manual_download_utils.py +996 -0
  519. scitex/scholar/pdf_download/strategies/response_body.py +207 -0
  520. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +364 -0
  521. scitex/scholar/pipelines/ScholarPipelineParallel.py +478 -0
  522. scitex/scholar/pipelines/ScholarPipelineSingle.py +767 -0
  523. scitex/scholar/pipelines/__init__.py +49 -0
  524. scitex/scholar/storage/BibTeXHandler.py +1018 -0
  525. scitex/scholar/storage/PaperIO.py +468 -0
  526. scitex/scholar/storage/ScholarLibrary.py +182 -0
  527. scitex/scholar/storage/_DeduplicationManager.py +548 -0
  528. scitex/scholar/storage/_LibraryCacheManager.py +724 -0
  529. scitex/scholar/storage/_LibraryManager.py +1835 -0
  530. scitex/scholar/storage/__init__.py +28 -0
  531. scitex/scholar/url_finder/ScholarURLFinder.py +379 -0
  532. scitex/scholar/url_finder/__init__.py +7 -0
  533. scitex/scholar/url_finder/strategies/__init__.py +33 -0
  534. scitex/scholar/url_finder/strategies/find_pdf_urls_by_direct_links.py +261 -0
  535. scitex/scholar/url_finder/strategies/find_pdf_urls_by_dropdown.py +67 -0
  536. scitex/scholar/url_finder/strategies/find_pdf_urls_by_href.py +204 -0
  537. scitex/scholar/url_finder/strategies/find_pdf_urls_by_navigation.py +256 -0
  538. scitex/scholar/url_finder/strategies/find_pdf_urls_by_publisher_patterns.py +165 -0
  539. scitex/scholar/url_finder/strategies/find_pdf_urls_by_zotero_translators.py +163 -0
  540. scitex/scholar/url_finder/strategies/find_supplementary_urls_by_href.py +70 -0
  541. scitex/scholar/utils/__init__.py +22 -0
  542. scitex/scholar/utils/bibtex/__init__.py +9 -0
  543. scitex/scholar/utils/bibtex/_parse_bibtex.py +71 -0
  544. scitex/scholar/utils/cleanup/__init__.py +8 -0
  545. scitex/scholar/utils/cleanup/_cleanup_scholar_processes.py +96 -0
  546. scitex/scholar/utils/cleanup/cleanup_old_extractions.py +117 -0
  547. scitex/scholar/utils/text/_TextNormalizer.py +407 -0
  548. scitex/scholar/utils/text/__init__.py +9 -0
  549. scitex/scholar/zotero/__init__.py +38 -0
  550. scitex/session/__init__.py +51 -0
  551. scitex/session/_lifecycle.py +736 -0
  552. scitex/session/_manager.py +102 -0
  553. scitex/session/template.py +122 -0
  554. scitex/stats/__init__.py +30 -26
  555. scitex/stats/correct/__init__.py +21 -0
  556. scitex/stats/correct/_correct_bonferroni.py +551 -0
  557. scitex/stats/correct/_correct_fdr.py +634 -0
  558. scitex/stats/correct/_correct_holm.py +548 -0
  559. scitex/stats/correct/_correct_sidak.py +499 -0
  560. scitex/stats/descriptive/__init__.py +85 -0
  561. scitex/stats/descriptive/_circular.py +540 -0
  562. scitex/stats/descriptive/_describe.py +219 -0
  563. scitex/stats/descriptive/_nan.py +518 -0
  564. scitex/stats/descriptive/_real.py +189 -0
  565. scitex/stats/effect_sizes/__init__.py +41 -0
  566. scitex/stats/effect_sizes/_cliffs_delta.py +325 -0
  567. scitex/stats/effect_sizes/_cohens_d.py +342 -0
  568. scitex/stats/effect_sizes/_epsilon_squared.py +315 -0
  569. scitex/stats/effect_sizes/_eta_squared.py +302 -0
  570. scitex/stats/effect_sizes/_prob_superiority.py +296 -0
  571. scitex/stats/posthoc/__init__.py +19 -0
  572. scitex/stats/posthoc/_dunnett.py +463 -0
  573. scitex/stats/posthoc/_games_howell.py +383 -0
  574. scitex/stats/posthoc/_tukey_hsd.py +367 -0
  575. scitex/stats/power/__init__.py +19 -0
  576. scitex/stats/power/_power.py +433 -0
  577. scitex/stats/template.py +119 -0
  578. scitex/stats/utils/__init__.py +62 -0
  579. scitex/stats/utils/_effect_size.py +985 -0
  580. scitex/stats/utils/_formatters.py +270 -0
  581. scitex/stats/utils/_normalizers.py +927 -0
  582. scitex/stats/utils/_power.py +433 -0
  583. scitex/stats_v01/_EffectSizeCalculator.py +488 -0
  584. scitex/stats_v01/_StatisticalValidator.py +411 -0
  585. scitex/stats_v01/__init__.py +60 -0
  586. scitex/stats_v01/_additional_tests.py +415 -0
  587. scitex/{stats → stats_v01}/_p2stars.py +19 -5
  588. scitex/stats_v01/_two_sample_tests.py +141 -0
  589. scitex/stats_v01/desc/__init__.py +83 -0
  590. scitex/stats_v01/desc/_circular.py +540 -0
  591. scitex/stats_v01/desc/_describe.py +219 -0
  592. scitex/stats_v01/desc/_nan.py +518 -0
  593. scitex/{stats/desc/_nan.py → stats_v01/desc/_nan_v01-20250920_145731.py} +23 -12
  594. scitex/stats_v01/desc/_real.py +189 -0
  595. scitex/stats_v01/tests/__corr_test_optimized.py +221 -0
  596. scitex/stats_v01/tests/_corr_test_optimized.py +179 -0
  597. scitex/str/__init__.py +1 -3
  598. scitex/str/_clean_path.py +6 -2
  599. scitex/str/_latex_fallback.py +267 -160
  600. scitex/str/_parse.py +44 -36
  601. scitex/str/_printc.py +1 -3
  602. scitex/template/__init__.py +87 -0
  603. scitex/template/_create_project.py +267 -0
  604. scitex/template/create_pip_project.py +80 -0
  605. scitex/template/create_research.py +80 -0
  606. scitex/template/create_singularity.py +80 -0
  607. scitex/units.py +291 -0
  608. scitex/utils/_compress_hdf5.py +14 -3
  609. scitex/utils/_email.py +21 -2
  610. scitex/utils/_grid.py +6 -4
  611. scitex/utils/_notify.py +13 -10
  612. scitex/utils/_verify_scitex_format.py +589 -0
  613. scitex/utils/_verify_scitex_format_v01.py +370 -0
  614. scitex/utils/template.py +122 -0
  615. scitex/web/_search_pubmed.py +62 -16
  616. scitex-2.1.0.dist-info/LICENSE +21 -0
  617. scitex-2.1.0.dist-info/METADATA +677 -0
  618. scitex-2.1.0.dist-info/RECORD +919 -0
  619. {scitex-2.0.0.dist-info → scitex-2.1.0.dist-info}/WHEEL +1 -1
  620. scitex-2.1.0.dist-info/entry_points.txt +3 -0
  621. scitex/ai/__Classifiers.py +0 -101
  622. scitex/ai/classification/classification_reporter.py +0 -1137
  623. scitex/ai/classification/classifiers.py +0 -101
  624. scitex/ai/classification_reporter.py +0 -1161
  625. scitex/ai/genai/__init__.py +0 -277
  626. scitex/ai/genai/anthropic_provider.py +0 -320
  627. scitex/ai/genai/anthropic_refactored.py +0 -109
  628. scitex/ai/genai/auth_manager.py +0 -200
  629. scitex/ai/genai/base_provider.py +0 -291
  630. scitex/ai/genai/chat_history.py +0 -307
  631. scitex/ai/genai/cost_tracker.py +0 -276
  632. scitex/ai/genai/deepseek_provider.py +0 -251
  633. scitex/ai/genai/google_provider.py +0 -228
  634. scitex/ai/genai/groq_provider.py +0 -248
  635. scitex/ai/genai/image_processor.py +0 -250
  636. scitex/ai/genai/llama_provider.py +0 -214
  637. scitex/ai/genai/mock_provider.py +0 -127
  638. scitex/ai/genai/model_registry.py +0 -304
  639. scitex/ai/genai/openai_provider.py +0 -293
  640. scitex/ai/genai/perplexity_provider.py +0 -205
  641. scitex/ai/genai/provider_base.py +0 -302
  642. scitex/ai/genai/provider_factory.py +0 -370
  643. scitex/ai/genai/response_handler.py +0 -235
  644. scitex/ai/layer/_Pass.py +0 -21
  645. scitex/ai/layer/__init__.py +0 -10
  646. scitex/ai/layer/_switch.py +0 -8
  647. scitex/ai/metrics/_bACC.py +0 -51
  648. scitex/ai/plt/_learning_curve.py +0 -194
  649. scitex/ai/plt/_optuna_study.py +0 -111
  650. scitex/ai/plt/aucs/__init__.py +0 -2
  651. scitex/ai/plt/aucs/example.py +0 -60
  652. scitex/ai/plt/aucs/pre_rec_auc.py +0 -223
  653. scitex/ai/plt/aucs/roc_auc.py +0 -246
  654. scitex/ai/sampling/undersample.py +0 -29
  655. scitex/db/_SQLite3.py +0 -2136
  656. scitex/db/_SQLite3Mixins/_BlobMixin.py +0 -229
  657. scitex/gen/_close.py +0 -222
  658. scitex/gen/_start.py +0 -451
  659. scitex/general/__init__.py +0 -5
  660. scitex/io/_load_modules/_db.py +0 -24
  661. scitex/life/__init__.py +0 -10
  662. scitex/life/_monitor_rain.py +0 -49
  663. scitex/reproduce/_fix_seeds.py +0 -45
  664. scitex/res/__init__.py +0 -5
  665. scitex/scholar/_local_search.py +0 -454
  666. scitex/scholar/_paper.py +0 -244
  667. scitex/scholar/_pdf_downloader.py +0 -325
  668. scitex/scholar/_search.py +0 -393
  669. scitex/scholar/_vector_search.py +0 -370
  670. scitex/scholar/_web_sources.py +0 -457
  671. scitex/stats/desc/__init__.py +0 -40
  672. scitex-2.0.0.dist-info/METADATA +0 -307
  673. scitex-2.0.0.dist-info/RECORD +0 -572
  674. scitex-2.0.0.dist-info/licenses/LICENSE +0 -7
  675. /scitex/ai/{act → activation}/__init__.py +0 -0
  676. /scitex/ai/{act → activation}/_define.py +0 -0
  677. /scitex/ai/{early_stopping.py → training/_EarlyStopping.py} +0 -0
  678. /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_ImportExportMixin.py +0 -0
  679. /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_IndexMixin.py +0 -0
  680. /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_RowMixin.py +0 -0
  681. /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_TableMixin.py +0 -0
  682. /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/__init__.py +0 -0
  683. /scitex/{stats → stats_v01}/_calc_partial_corr.py +0 -0
  684. /scitex/{stats → stats_v01}/_corr_test_multi.py +0 -0
  685. /scitex/{stats → stats_v01}/_corr_test_wrapper.py +0 -0
  686. /scitex/{stats → stats_v01}/_describe_wrapper.py +0 -0
  687. /scitex/{stats → stats_v01}/_multiple_corrections.py +0 -0
  688. /scitex/{stats → stats_v01}/_nan_stats.py +0 -0
  689. /scitex/{stats → stats_v01}/_p2stars_wrapper.py +0 -0
  690. /scitex/{stats → stats_v01}/_statistical_tests.py +0 -0
  691. /scitex/{stats/desc/_describe.py → stats_v01/desc/_describe_v01-20250920_145731.py} +0 -0
  692. /scitex/{stats/desc/_real.py → stats_v01/desc/_real_v01-20250920_145731.py} +0 -0
  693. /scitex/{stats → stats_v01}/multiple/__init__.py +0 -0
  694. /scitex/{stats → stats_v01}/multiple/_bonferroni_correction.py +0 -0
  695. /scitex/{stats → stats_v01}/multiple/_fdr_correction.py +0 -0
  696. /scitex/{stats → stats_v01}/multiple/_multicompair.py +0 -0
  697. /scitex/{stats → stats_v01}/tests/__corr_test.py +0 -0
  698. /scitex/{stats → stats_v01}/tests/__corr_test_multi.py +0 -0
  699. /scitex/{stats → stats_v01}/tests/__corr_test_single.py +0 -0
  700. /scitex/{stats → stats_v01}/tests/__init__.py +0 -0
  701. /scitex/{stats → stats_v01}/tests/_brunner_munzel_test.py +0 -0
  702. /scitex/{stats → stats_v01}/tests/_nocorrelation_test.py +0 -0
  703. /scitex/{stats → stats_v01}/tests/_smirnov_grubbs.py +0 -0
  704. {scitex-2.0.0.dist-info → scitex-2.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,724 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-08-12 14:26:28 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex_repo/src/scitex/scholar/storage/_LibraryCacheManager.py
5
+ # ----------------------------------------
6
+ from __future__ import annotations
7
+ import os
8
+ __FILE__ = __file__
9
+ __DIR__ = os.path.dirname(__FILE__)
10
+ # ----------------------------------------
11
+
12
+ """Result caching and Scholar library management for DOI resolution."""
13
+
14
+ import json
15
+ import shutil
16
+ from datetime import datetime
17
+ from pathlib import Path
18
+ from typing import Any, Dict, List, Optional
19
+
20
+ from scitex import logging
21
+ from scitex.scholar.config import ScholarConfig
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ class LibraryCacheManager:
27
+ """Handles DOI caching, result persistence, and retrieval.
28
+
29
+ Responsibilities:
30
+ - Scholar library checking and DOI retrieval
31
+ - DOI caching and result persistence
32
+ - Unresolved entry tracking
33
+ - Project symlink management
34
+ - Library integration and file management
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ project: Optional[str] = None,
40
+ config: Optional[ScholarConfig] = None,
41
+ ):
42
+ """Initialize result cache manager.
43
+
44
+ Args:
45
+ config: ScholarConfig instance
46
+ project: Project name for library organization
47
+ """
48
+ self.config = config or ScholarConfig()
49
+ self.project = self.config.resolve("project", project)
50
+ logger.debug(f"LibraryCacheManager initialized for project: {project}")
51
+
52
+ def is_doi_stored(
53
+ self,
54
+ title: str,
55
+ year: Optional[int] = None,
56
+ ) -> Optional[str]:
57
+ """Check if DOI already exists in master Scholar library before making API requests.
58
+
59
+ Args:
60
+ title: Paper title to search for
61
+ year: Publication year (optional, for better matching)
62
+
63
+ Returns:
64
+ DOI string if found in library, None otherwise
65
+ """
66
+ try:
67
+ if not title:
68
+ return None
69
+
70
+ master_dir = self.config.get_library_master_dir()
71
+ if not master_dir.exists():
72
+ return None
73
+
74
+ title_lower = title.lower().strip()
75
+ for paper_dir in master_dir.iterdir():
76
+ if paper_dir.is_dir() and len(paper_dir.name) == 8:
77
+ metadata_file = paper_dir / "metadata.json"
78
+ if metadata_file.exists():
79
+ try:
80
+ with open(metadata_file, "r") as f:
81
+ metadata = json.load(f)
82
+ stored_title = (
83
+ metadata.get("title", "").lower().strip()
84
+ )
85
+ stored_year = metadata.get("year")
86
+ stored_doi = metadata.get("doi")
87
+
88
+ title_match = stored_title == title_lower
89
+ year_match = (
90
+ year is None
91
+ or stored_year is None
92
+ or stored_year == year
93
+ )
94
+
95
+ if title_match and year_match and stored_doi:
96
+ logger.info(
97
+ f"DOI found in master Scholar library: {stored_doi} (paper_id: {paper_dir.name})"
98
+ )
99
+ return stored_doi
100
+
101
+ except (json.JSONDecodeError, KeyError) as e:
102
+ logger.debug(
103
+ f"Error reading metadata from {metadata_file}: {e}"
104
+ )
105
+ continue
106
+ return None
107
+
108
+ except Exception as e:
109
+ logger.debug(f"Error checking master Scholar library: {e}")
110
+ return None
111
+
112
+ def save_entry(
113
+ self,
114
+ title: str,
115
+ doi: Optional[str] = None,
116
+ year: Optional[int] = None,
117
+ authors: Optional[List[str]] = None,
118
+ source: Optional[str] = None,
119
+ metadata: Optional[Dict] = None,
120
+ bibtex_source: Optional[str] = None,
121
+ force_symlink: Optional[bool] = True,
122
+ ) -> bool:
123
+ """Save paper entry - automatically routes to resolved or unresolved.
124
+
125
+ Args:
126
+ title: Paper title
127
+ doi: DOI if resolved (None for unresolved)
128
+ year: Publication year
129
+ authors: List of authors
130
+ source: DOI resolution source
131
+ metadata: Additional metadata
132
+ bibtex_source: BibTeX source information
133
+
134
+ Returns:
135
+ True if saved successfully
136
+ """
137
+ if doi:
138
+ return self._save_resolved_entry(
139
+ title,
140
+ doi,
141
+ year,
142
+ authors,
143
+ source,
144
+ metadata,
145
+ bibtex_source,
146
+ force_symlink,
147
+ )
148
+ else:
149
+ return self._save_unresolved_entry(
150
+ title,
151
+ year,
152
+ authors,
153
+ bibtex_source,
154
+ force_symlink,
155
+ )
156
+
157
+ # def _save_resolved_entry(
158
+ # self,
159
+ # title: str,
160
+ # doi: str,
161
+ # year: Optional[int] = None,
162
+ # authors: Optional[List[str]] = None,
163
+ # source: str = None,
164
+ # metadata: Optional[Dict] = None,
165
+ # bibtex_source: Optional[str] = None,
166
+ # force_symlink: Optional[bool] = True,
167
+ # ) -> bool:
168
+ # try:
169
+ # # Extract metadata fields if available
170
+ # journal = metadata.get("journal") if metadata else None
171
+ # publisher = metadata.get("publisher") if metadata else None
172
+ # abstract = metadata.get("abstract") if metadata else None
173
+ # issn = metadata.get("issn") if metadata else None
174
+ # volume = metadata.get("volume") if metadata else None
175
+ # issue = metadata.get("issue") if metadata else None
176
+
177
+ # master_storage_path, master_readable_name, master_paper_id = (
178
+ # self.config.path_manager.get_paper_storage_paths(
179
+ # doi=doi,
180
+ # title=title,
181
+ # authors=authors,
182
+ # journal=journal,
183
+ # year=year,
184
+ # project="MASTER",
185
+ # )
186
+ # )
187
+
188
+ # # Ensure MASTER directory exists BEFORE writing metadata
189
+ # master_storage_path.mkdir(parents=True, exist_ok=True)
190
+
191
+ # master_metadata_file = master_storage_path / "metadata.json"
192
+ # existing_metadata = {}
193
+ # if master_metadata_file.exists():
194
+ # try:
195
+ # with open(master_metadata_file, "r") as file_:
196
+ # existing_metadata = json.load(file_)
197
+ # except Exception as exc_:
198
+ # logger.warning(f"Error loading existing metadata: {exc_}")
199
+
200
+ # comprehensive_metadata = {
201
+ # **existing_metadata,
202
+ # "title": title,
203
+ # "title_source": source,
204
+ # "year": year,
205
+ # "year_source": source,
206
+ # "authors": authors or [],
207
+ # "authors_source": source,
208
+ # "doi": doi,
209
+ # "doi_source": source,
210
+ # "doi_resolved_at": datetime.now().isoformat(),
211
+ # "scholar_id": master_paper_id,
212
+ # "created_at": existing_metadata.get(
213
+ # "created_at", datetime.now().isoformat()
214
+ # ),
215
+ # "updated_at": datetime.now().isoformat(),
216
+ # "journal": journal,
217
+ # "journal_source": source if journal else None,
218
+ # "publisher": publisher,
219
+ # "abstract": abstract,
220
+ # "issn": issn,
221
+ # "volume": volume,
222
+ # "issue": issue,
223
+ # }
224
+
225
+ # with open(master_metadata_file, "w") as file_:
226
+ # json.dump(comprehensive_metadata, file_, indent=2)
227
+
228
+ # logger.success(f"Saved to: {master_metadata_file} ({doi})")
229
+
230
+ # self._ensure_project_symlink(
231
+ # title,
232
+ # authors,
233
+ # year,
234
+ # journal,
235
+ # master_paper_id,
236
+ # force_symlink=force_symlink,
237
+ # )
238
+ # return True
239
+
240
+ # except Exception as exc_:
241
+ # logger.error(f"Error saving to Scholar library: {exc_}")
242
+ # return False
243
+
244
+ def _save_resolved_entry(
245
+ self,
246
+ title: str,
247
+ doi: str,
248
+ year: Optional[int] = None,
249
+ authors: Optional[List[str]] = None,
250
+ source: str = None,
251
+ metadata: Optional[Dict] = None,
252
+ bibtex_source: Optional[str] = None,
253
+ force_symlink: Optional[bool] = True,
254
+ ) -> bool:
255
+ try:
256
+ # Extract metadata fields if available
257
+ journal = metadata.get("journal") if metadata else None
258
+ publisher = metadata.get("publisher") if metadata else None
259
+ abstract = metadata.get("abstract") if metadata else None
260
+ issn = metadata.get("issn") if metadata else None
261
+ volume = metadata.get("volume") if metadata else None
262
+ issue = metadata.get("issue") if metadata else None
263
+
264
+ master_storage_path, master_readable_name, master_paper_id = (
265
+ self.config.path_manager.get_paper_storage_paths(
266
+ doi=doi,
267
+ title=title,
268
+ authors=authors,
269
+ journal=journal,
270
+ year=year,
271
+ project="MASTER",
272
+ )
273
+ )
274
+
275
+ # Ensure MASTER directory exists BEFORE writing metadata
276
+ master_storage_path.mkdir(parents=True, exist_ok=True)
277
+ master_metadata_file = master_storage_path / "metadata.json"
278
+
279
+ existing_metadata = {}
280
+ if master_metadata_file.exists():
281
+ try:
282
+ with open(master_metadata_file, "r") as file_:
283
+ existing_metadata = json.load(file_)
284
+ except Exception as exc_:
285
+ logger.warning(f"Error loading existing metadata: {exc_}")
286
+
287
+ comprehensive_metadata = {
288
+ **existing_metadata,
289
+ "title": title,
290
+ "title_source": source,
291
+ "year": year,
292
+ "year_source": source,
293
+ "authors": authors or [],
294
+ "authors_source": source,
295
+ "doi": doi,
296
+ "doi_source": source,
297
+ "doi_resolved_at": datetime.now().isoformat(),
298
+ "scholar_id": master_paper_id,
299
+ "created_at": existing_metadata.get(
300
+ "created_at", datetime.now().isoformat()
301
+ ),
302
+ "updated_at": datetime.now().isoformat(),
303
+ "journal": journal,
304
+ "journal_source": source if journal else None,
305
+ "publisher": publisher,
306
+ "abstract": abstract,
307
+ "issn": issn if issn else None,
308
+ "volume": volume if volume else None,
309
+ "issue": issue if issue else None,
310
+ }
311
+
312
+ # Check if content changed (excluding timestamps)
313
+ is_new = not existing_metadata
314
+ if not is_new:
315
+ existing_copy = existing_metadata.copy()
316
+ new_copy = comprehensive_metadata.copy()
317
+ # Remove timestamps for comparison
318
+ existing_copy.pop("updated_at", None)
319
+ existing_copy.pop("doi_resolved_at", None)
320
+ new_copy.pop("updated_at", None)
321
+ new_copy.pop("doi_resolved_at", None)
322
+ content_changed = existing_copy != new_copy
323
+ else:
324
+ content_changed = True
325
+
326
+ with open(master_metadata_file, "w") as file_:
327
+ json.dump(comprehensive_metadata, file_, indent=2)
328
+
329
+ if is_new:
330
+ logger.success(f"Saved to: {master_metadata_file} ({doi})")
331
+ elif content_changed:
332
+ logger.success(f"Updated: {master_metadata_file} ({doi})")
333
+
334
+ self._ensure_project_symlink(
335
+ title,
336
+ authors,
337
+ year,
338
+ journal,
339
+ master_paper_id,
340
+ force_symlink=force_symlink,
341
+ )
342
+ return True
343
+
344
+ except Exception as exc_:
345
+ logger.error(f"Error saving to Scholar library: {exc_}")
346
+ return False
347
+
348
+ def _save_unresolved_entry(
349
+ self,
350
+ title: str,
351
+ year: Optional[int] = None,
352
+ authors: Optional[List[str]] = None,
353
+ bibtex_source: Optional[str] = None,
354
+ force_symlink: Optional[bool] = True,
355
+ ) -> bool:
356
+ try:
357
+ storage_path, readable_name, paper_id = (
358
+ self.config.path_manager.get_paper_storage_paths(
359
+ title=title, year=year, authors=authors, project="MASTER"
360
+ )
361
+ )
362
+
363
+ # Ensure MASTER directory exists BEFORE writing metadata
364
+ storage_path.mkdir(parents=True, exist_ok=True)
365
+
366
+ metadata_file = storage_path / "metadata.json"
367
+ if metadata_file.exists():
368
+ logger.debug(f"Unresolved entry already exists: {paper_id}")
369
+ return True
370
+
371
+ unresolved_metadata = {
372
+ "title": title,
373
+ "title_source": bibtex_source if bibtex_source else "input",
374
+ "year": year,
375
+ "year_source": (
376
+ bibtex_source if bibtex_source and year else "input"
377
+ ),
378
+ "authors": authors or [],
379
+ "authors_source": (
380
+ bibtex_source if bibtex_source and authors else "input"
381
+ ),
382
+ "doi": None,
383
+ "doi_source": None,
384
+ "doi_resolution_failed": True,
385
+ "doi_last_attempt": datetime.now().isoformat(),
386
+ "scholar_id": paper_id,
387
+ "created_at": datetime.now().isoformat(),
388
+ "resolution_status": "unresolved",
389
+ "journal": None,
390
+ }
391
+
392
+ with open(metadata_file, "w") as file_:
393
+ json.dump(unresolved_metadata, file_, indent=2)
394
+
395
+ logger.info(
396
+ f"Saved unresolved entry: {paper_id} ({title[:50]}...)"
397
+ )
398
+
399
+ self._ensure_project_symlink(
400
+ title,
401
+ authors,
402
+ year,
403
+ None,
404
+ paper_id,
405
+ force_symlink=force_symlink,
406
+ )
407
+ return True
408
+
409
+ except Exception as exc_:
410
+ logger.error(f"Error saving unresolved entry: {exc_}")
411
+ return False
412
+
413
+ # # Loud
414
+ # def _ensure_project_symlink(
415
+ # self,
416
+ # title: str,
417
+ # authors: Optional[List[str]] = None,
418
+ # year: Optional[int] = None,
419
+ # journal: Optional[str] = None,
420
+ # paper_id: Optional[str] = None,
421
+ # force_symlink: Optional[bool] = True,
422
+ # ) -> bool:
423
+ # try:
424
+ # if not paper_id:
425
+ # _, _, paper_id = (
426
+ # self.config.path_manager.get_paper_storage_paths(
427
+ # title=title,
428
+ # year=year,
429
+ # authors=authors,
430
+ # project="MASTER",
431
+ # )
432
+ # )
433
+
434
+ # readable_name = self._generate_readable_name(
435
+ # authors, year, journal
436
+ # )
437
+ # project_dir = self.config.path_manager.get_library_project_dir(
438
+ # self.project
439
+ # )
440
+ # symlink_path = project_dir / readable_name
441
+ # relative_path = f"../MASTER/{paper_id}"
442
+
443
+ # if symlink_path.exists() or symlink_path.is_symlink():
444
+ # symlink_path.unlink()
445
+
446
+ # symlink_path.symlink_to(relative_path)
447
+ # logger.success(
448
+ # f"Created symlink:\n{symlink_path} -> {relative_path}"
449
+ # )
450
+ # return True
451
+
452
+ # except Exception as exc_:
453
+ # logger.error(f"Error ensuring project symlink: {exc_}")
454
+ # return False
455
+
456
+ def _ensure_project_symlink(
457
+ self,
458
+ title: str,
459
+ authors: Optional[List[str]] = None,
460
+ year: Optional[int] = None,
461
+ journal: Optional[str] = None,
462
+ paper_id: Optional[str] = None,
463
+ force_symlink: Optional[bool] = True,
464
+ ) -> bool:
465
+ try:
466
+ if not paper_id:
467
+ _, _, paper_id = (
468
+ self.config.path_manager.get_paper_storage_paths(
469
+ title=title,
470
+ year=year,
471
+ authors=authors,
472
+ project="MASTER",
473
+ )
474
+ )
475
+
476
+ readable_name = self._generate_readable_name(
477
+ authors, year, journal
478
+ )
479
+ project_dir = self.config.path_manager.get_library_project_dir(
480
+ self.project
481
+ )
482
+ symlink_path = project_dir / readable_name
483
+ relative_path = f"../MASTER/{paper_id}"
484
+
485
+ # Check if symlink already points to correct target
486
+ if symlink_path.is_symlink():
487
+ existing_target = symlink_path.readlink()
488
+ if str(existing_target) == relative_path:
489
+ return True
490
+ symlink_path.unlink()
491
+ elif symlink_path.exists():
492
+ symlink_path.unlink()
493
+
494
+ symlink_path.symlink_to(relative_path)
495
+ logger.success(
496
+ f"Created symlink:\n{symlink_path} -> {relative_path}"
497
+ )
498
+ return True
499
+
500
+ except Exception as exc_:
501
+ logger.error(f"Error ensuring project symlink: {exc_}")
502
+ return False
503
+
504
+ def _generate_readable_name(
505
+ self,
506
+ authors: Optional[List[str]] = None,
507
+ year: Optional[int] = None,
508
+ journal: Optional[str] = None,
509
+ ) -> str:
510
+ """Generate human-readable name for symlink.
511
+
512
+ Args:
513
+ title: Paper title
514
+ year: Publication year
515
+ journal: Journal name
516
+
517
+ Returns:
518
+ Human-readable name
519
+ """
520
+ # Get first author's last name
521
+ if authors and len(authors) > 0:
522
+ author_parts = authors[0].split()
523
+ if len(author_parts) > 1:
524
+ first_author = author_parts[-1] # Last name
525
+ else:
526
+ first_author = author_parts[0]
527
+ else:
528
+ first_author = "Unknown"
529
+
530
+ # Year
531
+ year_str = str(year) if year else "Unknown"
532
+
533
+ # Journal (clean up for filename usage)
534
+ if journal:
535
+ # Clean journal name for filename usage, keep it as-is but remove problematic chars
536
+ journal_cleaned = "".join(
537
+ c for c in journal if c.isalnum() or c in "._-"
538
+ )
539
+ journal = journal_cleaned if journal_cleaned else "Unknown"
540
+ else:
541
+ journal = "Unknown"
542
+
543
+ # Format: AUTHOR-YEAR-JOURNAL
544
+ readable_name = f"{first_author}-{year_str}-{journal}"
545
+
546
+ # Clean up filename
547
+ readable_name = "".join(
548
+ c for c in readable_name if c.isalnum() or c in "._-"
549
+ )
550
+ return readable_name
551
+
552
+ def get_unresolved_entries(
553
+ self, project_name: Optional[str] = None
554
+ ) -> List[Dict]:
555
+ """Get list of unresolved entries from Scholar library.
556
+
557
+ Args:
558
+ project_name: Project name (None for current project)
559
+
560
+ Returns:
561
+ List of unresolved entry dictionaries
562
+ """
563
+ try:
564
+ collection_name = project_name or self.project
565
+ collection_dir = self.config.path_manager.get_library_project_dir(
566
+ collection_name
567
+ )
568
+
569
+ if not collection_dir.exists():
570
+ return []
571
+
572
+ unresolved_entries = []
573
+
574
+ # Search through all paper directories
575
+ for paper_dir in collection_dir.iterdir():
576
+ if paper_dir.is_dir() and len(paper_dir.name) == 8:
577
+ metadata_file = paper_dir / "metadata.json"
578
+ if metadata_file.exists():
579
+ try:
580
+ with open(metadata_file, "r") as f:
581
+ metadata = json.load(f)
582
+
583
+ # Check if entry is unresolved
584
+ if (
585
+ metadata.get("doi_resolution_failed")
586
+ or metadata.get("resolution_status")
587
+ == "unresolved"
588
+ or not metadata.get("doi")
589
+ ):
590
+
591
+ unresolved_entries.append(
592
+ {
593
+ "paper_id": paper_dir.name,
594
+ "title": metadata.get(
595
+ "title", "Unknown"
596
+ ),
597
+ "year": metadata.get("year"),
598
+ "authors": metadata.get(
599
+ "authors", []
600
+ ),
601
+ "last_attempt": metadata.get(
602
+ "doi_last_attempt"
603
+ ),
604
+ "metadata_file": str(
605
+ metadata_file
606
+ ),
607
+ }
608
+ )
609
+
610
+ except (json.JSONDecodeError, KeyError) as e:
611
+ logger.debug(
612
+ f"Error reading metadata from {metadata_file}: {e}"
613
+ )
614
+ continue
615
+
616
+ logger.info(
617
+ f"Found {len(unresolved_entries)} unresolved entries in {collection_name}"
618
+ )
619
+ return unresolved_entries
620
+
621
+ except Exception as e:
622
+ logger.error(f"Error getting unresolved entries: {e}")
623
+ return []
624
+
625
+ def copy_bibtex_to_library(
626
+ self, bibtex_path: str, project_name: Optional[str] = None
627
+ ) -> str:
628
+ """Copy BibTeX file to Scholar library for reference.
629
+
630
+ Args:
631
+ bibtex_path: Path to BibTeX file
632
+ project_name: Project name (None for current project)
633
+
634
+ Returns:
635
+ Path to copied BibTeX file in library
636
+ """
637
+ try:
638
+ collection_name = project_name or self.project
639
+ collection_dir = self.config.get_library_project_info_dir(collection_name)
640
+
641
+ # Copy BibTeX file to collection directory
642
+ bibtex_source = Path(bibtex_path)
643
+ bibtex_dest = collection_dir / f"{bibtex_source.name}"
644
+
645
+ shutil.copy2(bibtex_source, bibtex_dest)
646
+ logger.info(f"Copied BibTeX to library: {bibtex_dest}")
647
+
648
+ return str(bibtex_dest)
649
+
650
+ except Exception as e:
651
+ logger.error(f"Error copying BibTeX to library: {e}")
652
+ return ""
653
+
654
+ def get_cache_statistics(self) -> Dict[str, Any]:
655
+ """Get cache statistics.
656
+
657
+ Returns:
658
+ Dictionary with cache statistics
659
+ """
660
+ try:
661
+ master_dir = self.config.get_library_master_dir()
662
+
663
+ if not master_dir.exists():
664
+ return {
665
+ "total_papers": 0,
666
+ "resolved_papers": 0,
667
+ "unresolved_papers": 0,
668
+ }
669
+
670
+ total_papers = 0
671
+ resolved_papers = 0
672
+ unresolved_papers = 0
673
+
674
+ for paper_dir in master_dir.iterdir():
675
+ if paper_dir.is_dir() and len(paper_dir.name) == 8:
676
+ total_papers += 1
677
+ metadata_file = paper_dir / "metadata.json"
678
+
679
+ if metadata_file.exists():
680
+ try:
681
+ with open(metadata_file, "r") as f:
682
+ metadata = json.load(f)
683
+
684
+ if metadata.get("doi"):
685
+ resolved_papers += 1
686
+ else:
687
+ unresolved_papers += 1
688
+ except:
689
+ unresolved_papers += 1
690
+ else:
691
+ unresolved_papers += 1
692
+
693
+ return {
694
+ "total_papers": total_papers,
695
+ "resolved_papers": resolved_papers,
696
+ "unresolved_papers": unresolved_papers,
697
+ "resolution_rate": (
698
+ resolved_papers / total_papers if total_papers > 0 else 0
699
+ ),
700
+ }
701
+
702
+ except Exception as e:
703
+ logger.error(f"Error getting cache statistics: {e}")
704
+ return {"error": str(e)}
705
+
706
+
707
+ if __name__ == "__main__":
708
+ # Example usage
709
+ from pathlib import Path
710
+
711
+ print("LibraryCacheManager Test:")
712
+
713
+ # This would require a real config in practice
714
+ # manager = LibraryCacheManager(config, "test_project")
715
+
716
+ print("Note: Full testing requires ScholarConfig instance")
717
+ print("Core functionality:")
718
+ print("- is_doi_stored(): Search for existing DOIs")
719
+ print("- save_to_scholar_library(): Cache resolved DOIs")
720
+ print("- save_unresolved_entry(): Track failed resolutions")
721
+ print("- get_unresolved_entries(): List papers needing resolution")
722
+ print("- get_cache_statistics(): Get cache metrics")
723
+
724
+ # EOF