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,386 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-10-11 01:05:03 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex_repo/src/scitex/scholar/auth/core/BrowserAuthenticator.py
5
+ # ----------------------------------------
6
+ from __future__ import annotations
7
+ import os
8
+ __FILE__ = (
9
+ "./src/scitex/scholar/auth/core/BrowserAuthenticator.py"
10
+ )
11
+ __DIR__ = os.path.dirname(__FILE__)
12
+ # ----------------------------------------
13
+
14
+ """Browser-based authentication operations.
15
+
16
+ This module handles browser interactions for authentication,
17
+ including login detection, navigation, and session verification.
18
+ """
19
+
20
+ import asyncio
21
+ from typing import Any, Dict, List, Optional
22
+
23
+ from playwright.async_api import Page, async_playwright
24
+
25
+ from scitex import logging
26
+ from scitex.browser.core import BrowserMixin
27
+ from scitex.browser.interaction import (
28
+ click_with_fallbacks_async,
29
+ fill_with_fallbacks_async,
30
+ )
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ class BrowserAuthenticator(BrowserMixin):
36
+ """Handles browser-based authentication operations."""
37
+
38
+ def __init__(
39
+ self, mode: str = "interactive", timeout: int = 300, sso_automator=None
40
+ ):
41
+ """Initialize browser authenticator.
42
+
43
+ Args:
44
+ mode: Browser mode - 'interactive' for authentication, 'stealth' for scraping
45
+ timeout: Timeout for browser operations in seconds
46
+ sso_automator: Optional SSO automator instance for institution-specific handling
47
+ """
48
+ super().__init__(mode=mode)
49
+ self.name = self.__class__.__name__
50
+ self.timeout = timeout
51
+ self.sso_automator = sso_automator
52
+
53
+ async def navigate_to_login_async(self, url: str) -> Page:
54
+ """Navigate to login URL and return page.
55
+
56
+ Args:
57
+ url: Login URL to navigate to
58
+
59
+ Returns:
60
+ Page object for further operations
61
+ """
62
+ # Use the BrowserMixin's get_browser_async method to get properly configured browser
63
+ browser = await self.get_browser_async()
64
+
65
+ # Create context with proper interactive viewport settings
66
+ context_options = {}
67
+ if self.mode == "interactive":
68
+ context_options["viewport"] = {"width": 1280, "height": 720}
69
+ else:
70
+ context_options["viewport"] = {"width": 1, "height": 1}
71
+
72
+ context = await browser.new_context(**context_options)
73
+ await context.add_init_script(
74
+ self.cookie_acceptor.get_auto_acceptor_script()
75
+ )
76
+ # await self.cookie_acceptor.inject_auto_acceptor_async(context)
77
+ page = await context.new_page()
78
+
79
+ logger.info(f"{self.name}: Navigating to: {url}")
80
+ await page.goto(url, wait_until="domcontentloaded")
81
+
82
+ # Check for cookie banner and warn if present
83
+ if await self.cookie_acceptor.check_cookie_banner_exists_async(page):
84
+ logger.warning(
85
+ "{self.name}: Cookie banner detected - may need manual acceptance"
86
+ )
87
+
88
+ return page
89
+
90
+ async def wait_for_login_completion_async(
91
+ self, page: Page, success_indicators: List[str]
92
+ ) -> bool:
93
+ """Wait for login completion by monitoring URL changes and handling SSO automation.
94
+
95
+ Args:
96
+ page: Browser page to monitor
97
+ success_indicators: List of URL patterns indicating successful login
98
+
99
+ Returns:
100
+ True if login successful, False if timed out
101
+ """
102
+ max_wait_time = self.timeout
103
+ check_interval = 2
104
+ elapsed_time = 0
105
+ seen_sso_page = False
106
+ openathens_automated = False
107
+
108
+ while elapsed_time < max_wait_time:
109
+ current_url = page.url
110
+
111
+ # Track SSO navigation
112
+ if self._is_sso_page(current_url):
113
+ seen_sso_page = True
114
+ logger.debug(
115
+ f"{self.name}: Detected SSO/login page: {current_url}"
116
+ )
117
+
118
+ # Handle different authentication flows based on current URL
119
+ automation_attempted = False
120
+
121
+ # Priority 1: OpenAthens page automation (if on OpenAthens and not yet automated)
122
+ if not openathens_automated and self._is_openathens_page(
123
+ current_url
124
+ ):
125
+ logger.debug(
126
+ f"{self.name}: OpenAthens page detected - attempting automation"
127
+ )
128
+ automation_attempted = True
129
+
130
+ try:
131
+ from ..sso.OpenAthensSSOAutomator import (
132
+ OpenAthensSSOAutomator,
133
+ )
134
+
135
+ openathens_automator = OpenAthensSSOAutomator()
136
+
137
+ if openathens_automator.is_sso_page(current_url):
138
+ oa_success = await openathens_automator.handle_sso_redirect_async(
139
+ page
140
+ )
141
+ if oa_success:
142
+ logger.info(
143
+ f"{self.name}: OpenAthens page automation completed"
144
+ )
145
+ openathens_automated = True
146
+ else:
147
+ logger.warning(
148
+ f"{self.name}: OpenAthens page automation failed"
149
+ )
150
+ except Exception as e:
151
+ logger.warning(
152
+ f"{self.name}: OpenAthens automation failed: {e}"
153
+ )
154
+
155
+ # Priority 2: Institution-specific SSO automation (if available and on SSO page)
156
+ elif self.sso_automator and self.sso_automator.is_sso_page(
157
+ current_url
158
+ ):
159
+ institution_name = self.sso_automator.get_institution_name()
160
+ logger.info(
161
+ f"{self.name}: {institution_name} SSO detected - attempting automation"
162
+ )
163
+ automation_attempted = True
164
+
165
+ sso_success = (
166
+ await self.sso_automator.handle_sso_redirect_async(page)
167
+ )
168
+ if sso_success:
169
+ logger.info(
170
+ f"{self.name}: {institution_name} SSO automation completed"
171
+ )
172
+ else:
173
+ logger.warning(
174
+ f"{self.name}: {institution_name} SSO automation failed"
175
+ )
176
+
177
+ # Priority 3: Generic automation attempt for unknown SSO pages
178
+ elif self._is_sso_page(current_url) and not automation_attempted:
179
+ logger.info(
180
+ f"{self.name}: Generic SSO page detected - attempting basic automation"
181
+ )
182
+ automation_attempted = True
183
+
184
+ try:
185
+ generic_success = (
186
+ await self._attempt_generic_sso_automation(page)
187
+ )
188
+ if generic_success:
189
+ logger.info(
190
+ f"{self.name}: Generic SSO automation completed"
191
+ )
192
+ else:
193
+ logger.info(
194
+ f"{self.name}: Generic SSO automation not applicable"
195
+ )
196
+ except Exception as e:
197
+ logger.debug(
198
+ f"{self.name}: Generic SSO automation failed: {e}"
199
+ )
200
+
201
+ # If automation was attempted but we're still on the same page,
202
+ # it likely failed and requires manual intervention
203
+ if automation_attempted and elapsed_time > 30:
204
+ if elapsed_time % 30 == 0: # Show message every 30 seconds
205
+ logger.info(
206
+ f"{self.name}: Automation completed - waiting for manual completion if needed"
207
+ )
208
+
209
+ # Check for success
210
+ if self._check_success_indicators(current_url, success_indicators):
211
+ if await self._verify_login_success_async(
212
+ page, seen_sso_page, elapsed_time
213
+ ):
214
+ logger.info(
215
+ f"{self.name}: Login successful detected at URL: {current_url}"
216
+ )
217
+ logger.info(
218
+ f"{self.name}: Login detected! Capturing session..."
219
+ )
220
+ return True
221
+
222
+ # Show progress
223
+ if elapsed_time % 10 == 0 and elapsed_time > 0:
224
+ logger.info(
225
+ f"{self.name}: Waiting for login... ({elapsed_time}s elapsed)"
226
+ )
227
+
228
+ await asyncio.sleep(check_interval)
229
+ elapsed_time += check_interval
230
+
231
+ logger.error(f"{self.name}: Login timeout - please try again")
232
+ return False
233
+
234
+ async def verify_authentication_async(
235
+ self, verification_url: str, cookies: List[Dict[str, Any]]
236
+ ) -> bool:
237
+ """Verify authentication by checking access to protected page.
238
+
239
+ Args:
240
+ verification_url: URL to test access to
241
+ cookies: Authentication cookies to use
242
+
243
+ Returns:
244
+ True if authentication verified, False otherwise
245
+ """
246
+ try:
247
+ # Use stealth mode for verification (minimal viewport)
248
+ original_mode = self.mode
249
+ self.mode = "stealth"
250
+
251
+ async with async_playwright() as p:
252
+ browser, context = await self.create_browser_context_async(p)
253
+
254
+ # Add cookies
255
+ if cookies:
256
+ await context.add_cookies(cookies)
257
+
258
+ page = await context.new_page()
259
+
260
+ # Navigate to verification URL
261
+ response = await page.goto(
262
+ verification_url,
263
+ wait_until="domcontentloaded",
264
+ timeout=15000,
265
+ )
266
+
267
+ current_url = page.url
268
+ is_authenticate_async = self._check_authenticate_async_page(
269
+ current_url
270
+ )
271
+
272
+ await browser.close()
273
+
274
+ if is_authenticate_async:
275
+ logger.info(
276
+ f"{self.name}: Verified live authentication at {current_url}"
277
+ )
278
+ else:
279
+ logger.debug(
280
+ f"{self.name}: Authentication verification failed at {current_url}"
281
+ )
282
+
283
+ return is_authenticate_async
284
+
285
+ except Exception as e:
286
+ logger.error(
287
+ f"{self.name}: Authentication verification failed: {e}"
288
+ )
289
+ return False
290
+ finally:
291
+ # Restore original mode setting
292
+ self.mode = original_mode
293
+
294
+ async def extract_session_cookies_async(
295
+ self, page: Page
296
+ ) -> tuple[Dict[str, str], List[Dict[str, Any]]]:
297
+ """Extract session cookies from browser page.
298
+
299
+ Args:
300
+ page: Browser page to extract cookies from
301
+
302
+ Returns:
303
+ Tuple of (simple_cookies_dict, full_cookies_list)
304
+ """
305
+ cookies = await page.context.cookies()
306
+ simple_cookies = {c["name"]: c["value"] for c in cookies}
307
+ return simple_cookies, cookies
308
+
309
+ async def reliable_click_async(self, page: Page, selector: str) -> bool:
310
+ """Perform reliable click using shared utility."""
311
+ return await click_with_fallbacks_async(page, selector)
312
+
313
+ async def reliable_fill_async(
314
+ self, page: Page, selector: str, value: str
315
+ ) -> bool:
316
+ """Perform reliable form fill using shared utility."""
317
+ return await fill_with_fallbacks_async(page, selector, value)
318
+
319
+ def display_login_instructions(
320
+ self, email: Optional[str], timeout: int
321
+ ) -> None:
322
+ """Display login instructions to user using proper logging.
323
+
324
+ Args:
325
+ email: User email to display
326
+ timeout: Timeout to display
327
+ """
328
+ logger.info(f"{self.name}: OpenAthens Authentication Required")
329
+ logger.info(f"{self.name}: MyAthens login page is opening...")
330
+ if email:
331
+ logger.info(f"{self.name}: Account: {email}")
332
+ logger.info(f"{self.name}: Please complete the login process:")
333
+ logger.info(f"{self.name}: 1. Enter your institutional email")
334
+ logger.info(f"{self.name}: 2. Click your institution when it appears")
335
+ logger.info(
336
+ f"{self.name}: 3. Complete login on your institution's page"
337
+ )
338
+ logger.info(
339
+ f"{self.name}: 4. You'll be redirected back to OpenAthens when done"
340
+ )
341
+ logger.info(f"{self.name}: 5. Timeout is {timeout} seconds")
342
+ logger.info(f"{self.name}: 6. Close the window after successful login")
343
+
344
+ # Show environment variables
345
+ logger.debug(f"{self.name}: OpenAthens Environment Variables:")
346
+ for key, value in os.environ.items():
347
+ if "SCITEX_SCHOLAR_OPENATHENS" in key:
348
+ logger.debug(f"{self.name}: {key}: {value}")
349
+
350
+ def _is_sso_page(self, url: str) -> bool:
351
+ """Check if URL indicates SSO/login page."""
352
+ return "sso" in url.lower() or "login" in url.lower()
353
+
354
+ def _is_openathens_page(self, url: str) -> bool:
355
+ """Check if URL is OpenAthens page."""
356
+ return (
357
+ "my.openathens.net" in url.lower()
358
+ or "openathens.net" in url.lower()
359
+ )
360
+
361
+ def _check_success_indicators(
362
+ self, url: str, indicators: List[str]
363
+ ) -> bool:
364
+ """Check if URL matches any success indicators."""
365
+ return any(indicator in url for indicator in indicators)
366
+
367
+ async def _verify_login_success_async(
368
+ self, page: Page, seen_sso_page: bool, elapsed_time: int
369
+ ) -> bool:
370
+ """Verify login success with additional checks."""
371
+ # Additional verification logic can be added here
372
+ return seen_sso_page or elapsed_time > 30
373
+
374
+ def _check_authenticate_async_page(self, url: str) -> bool:
375
+ """Check if URL indicates authenticate_async page."""
376
+ authenticate_async_patterns = ["/account", "/app", "/library"]
377
+ unauthenticate_async_patterns = ["login", "signin"]
378
+
379
+ # Check for unauthenticate_async patterns first
380
+ if any(pattern in url for pattern in unauthenticate_async_patterns):
381
+ return False
382
+
383
+ # Check for authenticate_async patterns
384
+ return any(pattern in url for pattern in authenticate_async_patterns)
385
+
386
+ # EOF
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-10-10 00:37:47 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex_repo/src/scitex/scholar/auth/_AuthenticationStrategyResolver.py
5
+ # ----------------------------------------
6
+ from __future__ import annotations
7
+ import os
8
+ __FILE__ = (
9
+ "./src/scitex/scholar/auth/core/StrategyResolver.py"
10
+ )
11
+ __DIR__ = os.path.dirname(__FILE__)
12
+ # ----------------------------------------
13
+
14
+ """Authentication strategy resolver that determines the best approach based on user information."""
15
+
16
+ from dataclasses import dataclass
17
+ from enum import Enum
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 AuthenticationMethod(Enum):
27
+ """Available authentication methods."""
28
+
29
+ DIRECT_SSO = "direct_sso" # Direct to institution SSO
30
+ OPENATHENS_ONLY = "openathens_only" # OpenAthens without SSO redirect
31
+ OPENATHENS_TO_SSO = "openathens_sso" # OpenAthens that redirects to SSO
32
+ MANUAL = "manual" # Manual intervention required
33
+
34
+
35
+ @dataclass
36
+ class AuthenticationStrategy:
37
+ """Authentication strategy configuration."""
38
+
39
+ method: AuthenticationMethod
40
+ primary_url: str
41
+ openathens_email: Optional[str] = None
42
+ sso_automator_available: bool = False
43
+ institution_name: Optional[str] = None
44
+ confidence: float = 0.0 # 0.0 to 1.0
45
+ fallback_methods: List[AuthenticationMethod] = None
46
+
47
+ def __post_init__(self):
48
+ if self.fallback_methods is None:
49
+ self.fallback_methods = [AuthenticationMethod.MANUAL]
50
+
51
+
52
+ class AuthenticationStrategyResolver:
53
+ """Resolves the best authentication strategy based on user information."""
54
+
55
+ # Known institution configurations
56
+ INSTITUTION_CONFIGS = {
57
+ # University of Melbourne
58
+ "unimelb.edu.au": {
59
+ "name": "University of Melbourne",
60
+ "openathens_supported": True,
61
+ "direct_sso_url": "https://sso.unimelb.edu.au/",
62
+ "openathens_redirects_to_sso": True,
63
+ "sso_automator": "UniversityOfMelbourne",
64
+ },
65
+ # Add more institutions as needed
66
+ "stanford.edu": {
67
+ "name": "Stanford University",
68
+ "openathens_supported": True,
69
+ "direct_sso_url": "https://login.stanford.edu/",
70
+ "openathens_redirects_to_sso": True,
71
+ "sso_automator": None,
72
+ },
73
+ "mit.edu": {
74
+ "name": "MIT",
75
+ "openathens_supported": False,
76
+ "direct_sso_url": "https://oidc.mit.edu/",
77
+ "openathens_redirects_to_sso": False,
78
+ "sso_automator": None,
79
+ },
80
+ }
81
+
82
+ def __init__(self, config: Optional[ScholarConfig] = None):
83
+ """Initialize resolver with configuration."""
84
+ self.name = self.__class__.__name__
85
+ self.config = config or ScholarConfig()
86
+
87
+ def resolve_strategy(
88
+ self,
89
+ openathens_email: Optional[str] = None,
90
+ target_url: Optional[str] = None,
91
+ institution_name: Optional[str] = None,
92
+ prefer_openathens: bool = True,
93
+ ) -> AuthenticationStrategy:
94
+ """Resolve the best authentication strategy.
95
+
96
+ Args:
97
+ openathens_email: User's institutional email
98
+ target_url: Target URL being accessed
99
+ institution_name: Explicit institution name
100
+ prefer_openathens: Whether to prefer OpenAthens when available
101
+
102
+ Returns:
103
+ AuthenticationStrategy with the recommended approach
104
+ """
105
+ logger.info(f"{self.name}: Resolving authentication strategy...")
106
+
107
+ # Get email from config if not provided
108
+ if not openathens_email:
109
+ openathens_email = self.config.resolve(
110
+ "openathens_email", None, None, str
111
+ )
112
+
113
+ if not openathens_email:
114
+ logger.warning(
115
+ f"{self.name}: No email provided - defaulting to manual authentication"
116
+ )
117
+ return AuthenticationStrategy(
118
+ method=AuthenticationMethod.MANUAL,
119
+ primary_url=target_url
120
+ or "https://my.openathens.net/?passiveLogin=false",
121
+ confidence=0.0,
122
+ )
123
+
124
+ # Extract domain from email
125
+ email_domain = self._extract_domain(openathens_email)
126
+ institution_config = self.INSTITUTION_CONFIGS.get(email_domain)
127
+
128
+ if institution_config:
129
+ return self._resolve_known_institution_strategy(
130
+ openathens_email,
131
+ email_domain,
132
+ institution_config,
133
+ target_url,
134
+ prefer_openathens,
135
+ )
136
+ else:
137
+ return self._resolve_unknown_institution_strategy(
138
+ openathens_email, email_domain, target_url, prefer_openathens
139
+ )
140
+
141
+ def _extract_domain(self, email: str) -> str:
142
+ """Extract domain from email address."""
143
+ if "@" not in email:
144
+ return email.lower()
145
+ return email.split("@")[1].lower()
146
+
147
+ def _resolve_known_institution_strategy(
148
+ self,
149
+ openathens_email: str,
150
+ email_domain: str,
151
+ institution_config: Dict[str, Any],
152
+ target_url: Optional[str],
153
+ prefer_openathens: bool,
154
+ ) -> AuthenticationStrategy:
155
+ """Resolve strategy for known institution."""
156
+
157
+ institution_name = institution_config["name"]
158
+ sso_automator_available = (
159
+ institution_config.get("sso_automator") is not None
160
+ )
161
+
162
+ logger.info(
163
+ f"{self.name}: Resolving strategy for known institution: {institution_name}"
164
+ )
165
+
166
+ # Determine primary method based on preferences and capabilities
167
+ if prefer_openathens and institution_config.get(
168
+ "openathens_supported", False
169
+ ):
170
+ if institution_config.get("openathens_redirects_to_sso", False):
171
+ # OpenAthens → SSO flow
172
+ method = AuthenticationMethod.OPENATHENS_TO_SSO
173
+ primary_url = "https://my.openathens.net/?passiveLogin=false"
174
+ confidence = 0.9 if sso_automator_available else 0.7
175
+ fallback_methods = [
176
+ AuthenticationMethod.DIRECT_SSO,
177
+ AuthenticationMethod.MANUAL,
178
+ ]
179
+ else:
180
+ # OpenAthens only
181
+ method = AuthenticationMethod.OPENATHENS_ONLY
182
+ primary_url = "https://my.openathens.net/?passiveLogin=false"
183
+ confidence = 0.8
184
+ fallback_methods = [
185
+ AuthenticationMethod.DIRECT_SSO,
186
+ AuthenticationMethod.MANUAL,
187
+ ]
188
+ else:
189
+ # Direct SSO
190
+ method = AuthenticationMethod.DIRECT_SSO
191
+ primary_url = institution_config.get(
192
+ "direct_sso_url", target_url or ""
193
+ )
194
+ confidence = 0.8 if sso_automator_available else 0.5
195
+ fallback_methods = (
196
+ [
197
+ AuthenticationMethod.OPENATHENS_TO_SSO,
198
+ AuthenticationMethod.MANUAL,
199
+ ]
200
+ if institution_config.get("openathens_supported", False)
201
+ else [AuthenticationMethod.MANUAL]
202
+ )
203
+
204
+ return AuthenticationStrategy(
205
+ method=method,
206
+ primary_url=primary_url,
207
+ openathens_email=openathens_email,
208
+ sso_automator_available=sso_automator_available,
209
+ institution_name=institution_name,
210
+ confidence=confidence,
211
+ fallback_methods=fallback_methods,
212
+ )
213
+
214
+ def _resolve_unknown_institution_strategy(
215
+ self,
216
+ openathens_email: str,
217
+ email_domain: str,
218
+ target_url: Optional[str],
219
+ prefer_openathens: bool,
220
+ ) -> AuthenticationStrategy:
221
+ """Resolve strategy for unknown institution."""
222
+
223
+ logger.info(
224
+ f"{self.name}: Resolving strategy for unknown institution: {email_domain}"
225
+ )
226
+
227
+ # For unknown institutions, try OpenAthens first as it's most generic
228
+ if prefer_openathens:
229
+ method = AuthenticationMethod.OPENATHENS_TO_SSO
230
+ primary_url = "https://my.openathens.net/?passiveLogin=false"
231
+ confidence = 0.6 # Lower confidence for unknown institutions
232
+ fallback_methods = [AuthenticationMethod.MANUAL]
233
+ else:
234
+ method = AuthenticationMethod.MANUAL
235
+ primary_url = (
236
+ target_url or "https://my.openathens.net/?passiveLogin=false"
237
+ )
238
+ confidence = 0.3
239
+ fallback_methods = []
240
+
241
+ return AuthenticationStrategy(
242
+ method=method,
243
+ primary_url=primary_url,
244
+ openathens_email=openathens_email,
245
+ sso_automator_available=False,
246
+ institution_name=f"Unknown ({email_domain})",
247
+ confidence=confidence,
248
+ fallback_methods=fallback_methods,
249
+ )
250
+
251
+ def get_supported_institutions(self) -> List[str]:
252
+ """Get list of supported institutions."""
253
+ return [config["name"] for config in self.INSTITUTION_CONFIGS.values()]
254
+
255
+ def add_institution_config(
256
+ self,
257
+ domain: str,
258
+ name: str,
259
+ openathens_supported: bool = True,
260
+ direct_sso_url: Optional[str] = None,
261
+ openathens_redirects_to_sso: bool = True,
262
+ sso_automator: Optional[str] = None,
263
+ ) -> None:
264
+ """Add configuration for a new institution."""
265
+ self.INSTITUTION_CONFIGS[domain.lower()] = {
266
+ "name": name,
267
+ "openathens_supported": openathens_supported,
268
+ "direct_sso_url": direct_sso_url,
269
+ "openathens_redirects_to_sso": openathens_redirects_to_sso,
270
+ "sso_automator": sso_automator,
271
+ }
272
+ logger.info(
273
+ f"{self.name}: Added institution configuration: {name} ({domain})"
274
+ )
275
+
276
+
277
+ if __name__ == "__main__":
278
+
279
+ def main():
280
+ """Test the authentication strategy resolver."""
281
+ resolver = AuthenticationStrategyResolver()
282
+
283
+ # Test known institution
284
+ strategy = resolver.resolve_strategy(
285
+ openathens_email="test@unimelb.edu.au"
286
+ )
287
+ print(
288
+ f"UniMelb Strategy: {strategy.method.value}, URL: {strategy.primary_url}, Confidence: {strategy.confidence}"
289
+ )
290
+
291
+ # Test unknown institution
292
+ strategy = resolver.resolve_strategy(
293
+ openathens_email="test@unknown.edu"
294
+ )
295
+ print(
296
+ f"Unknown Strategy: {strategy.method.value}, URL: {strategy.primary_url}, Confidence: {strategy.confidence}"
297
+ )
298
+
299
+ # Test manual fallback
300
+ strategy = resolver.resolve_strategy()
301
+ print(
302
+ f"No Email Strategy: {strategy.method.value}, URL: {strategy.primary_url}, Confidence: {strategy.confidence}"
303
+ )
304
+
305
+ main()
306
+
307
+ # python -m scitex.scholar.auth._AuthenticationStrategyResolver
308
+
309
+ # EOF