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,848 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-10-18 09:55:59 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/capture/capture.py
5
+ # ----------------------------------------
6
+ from __future__ import annotations
7
+ import os
8
+ __FILE__ = (
9
+ "./src/scitex/capture/capture.py"
10
+ )
11
+ __DIR__ = os.path.dirname(__FILE__)
12
+ # ----------------------------------------
13
+
14
+ """
15
+ Core screenshot capture functionality.
16
+ Optimized for WSL to Windows host screen capture.
17
+ """
18
+
19
+ import subprocess
20
+ import sys
21
+ import threading
22
+ import time
23
+ from datetime import datetime
24
+ from pathlib import Path
25
+ from typing import Optional
26
+
27
+
28
+ class ScreenshotWorker:
29
+ """
30
+ Independent worker thread for continuous screenshot capture.
31
+ Takes screenshots at configurable intervals with compression options.
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ output_dir: str = "/tmp/scitex_capture_screenshots",
37
+ interval_sec: float = 1.0,
38
+ verbose: bool = False,
39
+ use_jpeg: bool = True,
40
+ jpeg_quality: int = 60,
41
+ on_capture=None,
42
+ on_error=None,
43
+ ):
44
+ """
45
+ Initialize screenshot worker.
46
+
47
+ Parameters
48
+ ----------
49
+ output_dir : str
50
+ Directory for saving screenshots
51
+ interval_sec : float
52
+ Seconds between screenshots (default: 1.0)
53
+ verbose : bool
54
+ Print screenshot paths in runtime log
55
+ use_jpeg : bool
56
+ Use JPEG compression for smaller files (default: True)
57
+ jpeg_quality : int
58
+ JPEG quality 1-100, lower = smaller files (default: 60)
59
+ on_capture : callable, optional
60
+ Callback function called with filepath after each capture
61
+ on_error : callable, optional
62
+ Callback function called with exception on errors
63
+ """
64
+ self.output_dir = Path(output_dir)
65
+ self.interval_sec = interval_sec
66
+ self.verbose = verbose
67
+ self.use_jpeg = use_jpeg
68
+ self.jpeg_quality = jpeg_quality
69
+ self.on_capture = on_capture
70
+ self.on_error = on_error
71
+
72
+ # Worker state
73
+ self.running = False
74
+ self.worker_thread = None
75
+ self.screenshot_count = 0
76
+ self.session_id = None
77
+
78
+ # Monitor capture settings
79
+ self.monitor = 0 # Default to primary monitor (0-based indexing)
80
+ self.capture_all = False # Default to single monitor
81
+
82
+ # Create output directory
83
+ self.output_dir.mkdir(parents=True, exist_ok=True)
84
+
85
+ def start(self, session_id: str = None):
86
+ """Start the screenshot worker thread."""
87
+ if self.running:
88
+ if self.verbose:
89
+ print("⚠️ Worker already running")
90
+ return
91
+
92
+ self.running = True
93
+ self.screenshot_count = 0
94
+ self.session_id = session_id or datetime.now().strftime(
95
+ "%Y%m%d_%H%M%S"
96
+ )
97
+
98
+ # Start worker thread
99
+ self.worker_thread = threading.Thread(
100
+ target=self._worker_loop, daemon=True, name="ScreenshotWorker"
101
+ )
102
+ self.worker_thread.start()
103
+
104
+ if self.verbose:
105
+ ext = "jpg" if self.use_jpeg else "png"
106
+ print(
107
+ f"📸 Started: {self.output_dir}/{self.session_id}_NNNN_*.{ext} (interval: {self.interval_sec}s)"
108
+ )
109
+
110
+ def stop(self):
111
+ """Stop the screenshot worker thread."""
112
+ if not self.running:
113
+ return
114
+
115
+ self.running = False
116
+
117
+ if self.worker_thread and self.worker_thread.is_alive():
118
+ self.worker_thread.join(timeout=2)
119
+
120
+ if self.verbose:
121
+ print(
122
+ f"📸 Stopped: {self.screenshot_count} screenshots in {self.output_dir}"
123
+ )
124
+
125
+ def _worker_loop(self):
126
+ """Main worker loop that takes screenshots."""
127
+
128
+ next_capture_time = time.time()
129
+
130
+ while self.running:
131
+ current_time = time.time()
132
+
133
+ # Check if it's time for next capture
134
+ if current_time >= next_capture_time:
135
+ try:
136
+ screenshot_path = self._take_screenshot()
137
+
138
+ if screenshot_path:
139
+ if self.verbose:
140
+ # Simple one-line output
141
+ print(f"📸 {screenshot_path}")
142
+
143
+ # Call on_capture callback if provided
144
+ if self.on_capture:
145
+ try:
146
+ self.on_capture(screenshot_path)
147
+ except Exception as cb_error:
148
+ if self.verbose:
149
+ print(f"⚠️ Callback error: {cb_error}")
150
+
151
+ except Exception as e:
152
+ if self.verbose:
153
+ print(f"❌ Error: {e}")
154
+
155
+ # Call on_error callback if provided
156
+ if self.on_error:
157
+ try:
158
+ self.on_error(e)
159
+ except Exception as cb_error:
160
+ if self.verbose:
161
+ print(f"⚠️ Error callback failed: {cb_error}")
162
+
163
+ # Schedule next capture
164
+ next_capture_time = current_time + self.interval_sec
165
+
166
+ # Short sleep to avoid busy waiting, but allow responsive stopping
167
+ time.sleep(0.01)
168
+
169
+ def _take_screenshot(self) -> Optional[str]:
170
+ """Take a single screenshot."""
171
+ try:
172
+ # Generate filename with timestamp
173
+ now = datetime.now()
174
+ timestamp = now.strftime("%Y%m%d_%H%M%S_%f")[:-3]
175
+ ext = "jpg" if self.use_jpeg else "png"
176
+ filename = f"{self.session_id}_{self.screenshot_count:04d}_{timestamp}.{ext}"
177
+ filepath = self.output_dir / filename
178
+
179
+ # Try Windows PowerShell method for WSL
180
+ if self._is_wsl():
181
+ if self._capture_windows_screen(
182
+ filepath,
183
+ monitor=self.monitor,
184
+ capture_all=self.capture_all,
185
+ ):
186
+ self.screenshot_count += 1
187
+ return str(filepath)
188
+
189
+ # Fallback to native screenshot tools
190
+ if self._capture_native_screen(filepath):
191
+ self.screenshot_count += 1
192
+ return str(filepath)
193
+
194
+ return None
195
+
196
+ except Exception as e:
197
+ if self.verbose:
198
+ print(f"❌ Screenshot failed: {e}")
199
+ return None
200
+
201
+ def _is_wsl(self) -> bool:
202
+ """Check if running in WSL."""
203
+ return (
204
+ sys.platform == "linux"
205
+ and "microsoft" in os.uname().release.lower()
206
+ )
207
+
208
+ def _capture_windows_screen(
209
+ self, filepath: Path, monitor: int = 1, capture_all: bool = False
210
+ ) -> bool:
211
+ """Capture Windows host screen from WSL with DPI awareness using external PowerShell scripts.
212
+
213
+ Args:
214
+ filepath: Path to save the screenshot
215
+ monitor: Monitor number to capture (1-based index)
216
+ capture_all: If True, capture all monitors combined
217
+ """
218
+ try:
219
+ # Try using external PowerShell script first
220
+ script_dir = Path(__file__).parent / "powershell"
221
+ if capture_all:
222
+ script_path = script_dir / "capture_all_monitors.ps1"
223
+ else:
224
+ script_path = script_dir / "capture_single_monitor.ps1"
225
+
226
+ # Check if script exists
227
+ if script_path.exists():
228
+
229
+ # Find PowerShell executable
230
+ ps_paths = [
231
+ "powershell.exe",
232
+ "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe",
233
+ "/mnt/c/Windows/SysWOW64/WindowsPowerShell/v1.0/powershell.exe",
234
+ ]
235
+
236
+ ps_exe = None
237
+ for path in ps_paths:
238
+ try:
239
+ test_result = subprocess.run(
240
+ [path, "-Command", "echo test"],
241
+ capture_output=True,
242
+ timeout=1,
243
+ )
244
+ if test_result.returncode == 0:
245
+ ps_exe = path
246
+ break
247
+ except:
248
+ continue
249
+
250
+ if ps_exe:
251
+ # Build PowerShell command
252
+ if capture_all:
253
+ cmd = [
254
+ ps_exe,
255
+ "-NoProfile",
256
+ "-ExecutionPolicy",
257
+ "Bypass",
258
+ "-File",
259
+ str(script_path),
260
+ "-OutputFormat",
261
+ "base64",
262
+ ]
263
+ else:
264
+ # Pass 0-based monitor index directly to PowerShell
265
+ cmd = [
266
+ ps_exe,
267
+ "-NoProfile",
268
+ "-ExecutionPolicy",
269
+ "Bypass",
270
+ "-File",
271
+ str(script_path),
272
+ "-MonitorNumber",
273
+ str(monitor),
274
+ "-OutputFormat",
275
+ "base64",
276
+ ]
277
+
278
+ result = subprocess.run(
279
+ cmd, capture_output=True, text=True, timeout=5
280
+ )
281
+
282
+ if result.returncode == 0 and result.stdout.strip():
283
+ # Decode base64 PNG data
284
+ import base64
285
+
286
+ png_data = base64.b64decode(result.stdout.strip())
287
+
288
+ # Save directly as JPEG if requested, otherwise as PNG
289
+ if self.use_jpeg:
290
+ try:
291
+ import io
292
+
293
+ from PIL import Image
294
+
295
+ img = Image.open(io.BytesIO(png_data))
296
+ # Convert RGBA to RGB for JPEG
297
+ if img.mode == "RGBA":
298
+ rgb_img = Image.new(
299
+ "RGB", img.size, (255, 255, 255)
300
+ )
301
+ rgb_img.paste(
302
+ img, mask=img.split()[3]
303
+ ) # Use alpha channel as mask
304
+ img = rgb_img
305
+ img.save(
306
+ str(filepath),
307
+ "JPEG",
308
+ quality=self.jpeg_quality,
309
+ optimize=True,
310
+ )
311
+ except ImportError:
312
+ # PIL not available, save as PNG
313
+ with open(str(filepath), "wb") as f:
314
+ f.write(png_data)
315
+ else:
316
+ with open(str(filepath), "wb") as f:
317
+ f.write(png_data)
318
+
319
+ return filepath.exists()
320
+
321
+ # Fallback to inline script
322
+ return self._capture_windows_screen_inline(filepath)
323
+
324
+ except Exception as e:
325
+ if self.verbose:
326
+ print(f"❌ Windows screen capture error: {e}")
327
+ import traceback
328
+
329
+ traceback.print_exc()
330
+ return False
331
+
332
+ def _capture_windows_screen_inline(self, filepath: Path) -> bool:
333
+ """Fallback inline PowerShell capture (when .ps1 files not available)."""
334
+ try:
335
+ if self.verbose:
336
+ print("🔍 Attempting inline PowerShell capture...")
337
+ # Use base64 encoding to avoid path issues (most reliable for WSL)
338
+ # Now with DPI awareness for proper high-resolution capture
339
+ ps_script = """
340
+ Add-Type -AssemblyName System.Windows.Forms
341
+ Add-Type -AssemblyName System.Drawing
342
+
343
+ # Enable DPI awareness for proper high-resolution capture
344
+ Add-Type @'
345
+ using System;
346
+ using System.Runtime.InteropServices;
347
+ public class User32 {
348
+ [DllImport("user32.dll")]
349
+ public static extern bool SetProcessDPIAware();
350
+ }
351
+ '@
352
+ $null = [User32]::SetProcessDPIAware()
353
+
354
+ $screen = [System.Windows.Forms.Screen]::PrimaryScreen
355
+ $bitmap = New-Object System.Drawing.Bitmap $screen.Bounds.Width, $screen.Bounds.Height
356
+ $graphics = [System.Drawing.Graphics]::FromImage($bitmap)
357
+
358
+ # Set high quality rendering
359
+ $graphics.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality
360
+ $graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
361
+ $graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality
362
+ $graphics.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
363
+
364
+ $graphics.CopyFromScreen($screen.Bounds.X, $screen.Bounds.Y, 0, 0, $bitmap.Size)
365
+
366
+ $stream = New-Object System.IO.MemoryStream
367
+ $bitmap.Save($stream, [System.Drawing.Imaging.ImageFormat]::Png)
368
+ $bytes = $stream.ToArray()
369
+ [Convert]::ToBase64String($bytes)
370
+
371
+ $graphics.Dispose()
372
+ $bitmap.Dispose()
373
+ $stream.Dispose()
374
+ """
375
+
376
+ # Find PowerShell executable
377
+ ps_paths = [
378
+ # Check PATH first (might be in .win-bin or similar)
379
+ "powershell.exe",
380
+ # Standard WSL path
381
+ "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe",
382
+ # Alternative locations
383
+ "/mnt/c/Windows/SysWOW64/WindowsPowerShell/v1.0/powershell.exe",
384
+ ]
385
+
386
+ ps_exe = None
387
+ for path in ps_paths:
388
+ try:
389
+ # Just check if the file exists and is executable
390
+ test_path = (
391
+ Path(path)
392
+ if not path.startswith("/mnt/")
393
+ else Path(path)
394
+ )
395
+ if path == "powershell.exe":
396
+ # In PATH - use it directly
397
+ ps_exe = path
398
+ if self.verbose:
399
+ print(f"✓ Found PowerShell in PATH")
400
+ break
401
+ elif test_path.exists() or Path(path).exists():
402
+ ps_exe = path
403
+ if self.verbose:
404
+ print(f"✓ Found PowerShell at {path}")
405
+ break
406
+ except:
407
+ continue
408
+
409
+ if not ps_exe:
410
+ if self.verbose:
411
+ print("❌ PowerShell executable not found")
412
+ return False
413
+
414
+ if self.verbose:
415
+ print(f"✓ Using PowerShell: {ps_exe}")
416
+
417
+ # Execute PowerShell
418
+ cmd = [ps_exe, "-NoProfile", "-Command", ps_script]
419
+
420
+ if self.verbose:
421
+ print("🔄 Executing PowerShell script...")
422
+
423
+ try:
424
+ result = subprocess.run(
425
+ cmd, capture_output=True, text=True, timeout=10
426
+ )
427
+
428
+ if self.verbose:
429
+ print(f"✓ PowerShell return code: {result.returncode}")
430
+ if result.stderr:
431
+ print(f"PowerShell stderr: {result.stderr[:500]}")
432
+ if result.stdout:
433
+ print(
434
+ f"✓ PowerShell stdout length: {len(result.stdout)} chars"
435
+ )
436
+ except subprocess.TimeoutExpired as e:
437
+ if self.verbose:
438
+ print(f"❌ PowerShell timeout after 10s")
439
+ return False
440
+
441
+ if result.returncode == 0 and result.stdout.strip():
442
+ # Decode base64 PNG data
443
+ import base64
444
+
445
+ png_data = base64.b64decode(result.stdout.strip())
446
+
447
+ # Save directly as JPEG if requested, otherwise as PNG
448
+ if self.use_jpeg:
449
+ try:
450
+ import io
451
+
452
+ from PIL import Image
453
+
454
+ # Load PNG from memory
455
+ img = Image.open(io.BytesIO(png_data))
456
+ # Convert RGBA to RGB for JPEG
457
+ if img.mode == "RGBA":
458
+ rgb_img = Image.new(
459
+ "RGB", img.size, (255, 255, 255)
460
+ )
461
+ rgb_img.paste(img, mask=img.split()[3])
462
+ img = rgb_img
463
+ # Save as JPEG with quality
464
+ img.save(
465
+ str(filepath),
466
+ "JPEG",
467
+ quality=self.jpeg_quality,
468
+ optimize=True,
469
+ )
470
+ except ImportError:
471
+ # PIL not available, save as PNG
472
+ with open(str(filepath), "wb") as f:
473
+ f.write(png_data)
474
+ else:
475
+ # Save as PNG
476
+ with open(str(filepath), "wb") as f:
477
+ f.write(png_data)
478
+
479
+ return filepath.exists()
480
+ except Exception:
481
+ pass
482
+ return False
483
+
484
+ def _capture_native_screen(self, filepath: Path) -> bool:
485
+ """Capture screen using native tools."""
486
+ try:
487
+ # Try mss first
488
+ try:
489
+ import mss
490
+
491
+ with mss.mss() as sct:
492
+ # Capture primary monitor
493
+ monitor = (
494
+ sct.monitors[1]
495
+ if len(sct.monitors) > 1
496
+ else sct.monitors[0]
497
+ )
498
+ screenshot = sct.grab(monitor)
499
+
500
+ if self.use_jpeg:
501
+ # Convert to PIL for JPEG saving
502
+ from PIL import Image
503
+
504
+ img = Image.frombytes(
505
+ "RGB",
506
+ screenshot.size,
507
+ screenshot.bgra,
508
+ "raw",
509
+ "BGRX",
510
+ )
511
+ img.save(
512
+ str(filepath), "JPEG", quality=self.jpeg_quality
513
+ )
514
+ else:
515
+ mss.tools.to_png(
516
+ screenshot.rgb,
517
+ screenshot.size,
518
+ output=str(filepath),
519
+ )
520
+
521
+ return filepath.exists()
522
+ except ImportError:
523
+ pass
524
+
525
+ # Try scrot
526
+ if self.use_jpeg:
527
+ cmd = [
528
+ "scrot",
529
+ "-z",
530
+ "-q",
531
+ str(self.jpeg_quality),
532
+ str(filepath),
533
+ ]
534
+ else:
535
+ cmd = ["scrot", "-z", str(filepath)]
536
+
537
+ result = subprocess.run(cmd, capture_output=True, timeout=2)
538
+ return result.returncode == 0 and filepath.exists()
539
+
540
+ except Exception as e:
541
+ if self.verbose:
542
+ print(f"❌ Native screen capture failed: {e}")
543
+ return False
544
+
545
+ def get_status(self) -> dict:
546
+ """Get current worker status."""
547
+ return {
548
+ "running": self.running,
549
+ "session_id": self.session_id,
550
+ "screenshot_count": self.screenshot_count,
551
+ "output_dir": str(self.output_dir),
552
+ "interval_sec": self.interval_sec,
553
+ "use_jpeg": self.use_jpeg,
554
+ "jpeg_quality": self.jpeg_quality,
555
+ }
556
+
557
+
558
+ class CaptureManager:
559
+ """High-level interface for managing screen capture."""
560
+
561
+ def __init__(self):
562
+ self.worker = None
563
+
564
+ def start_capture(
565
+ self,
566
+ output_dir: str = "/tmp/scitex_capture_screenshots",
567
+ interval: float = 1.0,
568
+ jpeg: bool = True,
569
+ quality: int = 60,
570
+ on_capture=None,
571
+ on_error=None,
572
+ verbose: bool = False,
573
+ monitor_id: int = 0,
574
+ capture_all: bool = False,
575
+ ) -> ScreenshotWorker:
576
+ """Start continuous screen capture."""
577
+ if self.worker and self.worker.running:
578
+ print("Capture already running")
579
+ return self.worker
580
+
581
+ self.worker = ScreenshotWorker(
582
+ output_dir=output_dir,
583
+ interval_sec=interval,
584
+ use_jpeg=jpeg,
585
+ jpeg_quality=quality,
586
+ on_capture=on_capture,
587
+ on_error=on_error,
588
+ verbose=verbose,
589
+ )
590
+ # Set monitor parameters
591
+ self.worker.monitor = monitor_id
592
+ self.worker.capture_all = capture_all
593
+ self.worker.start()
594
+ return self.worker
595
+
596
+ def stop_capture(self):
597
+ """Stop screen capture."""
598
+ if self.worker:
599
+ self.worker.stop()
600
+ self.worker = None
601
+
602
+ def take_single_screenshot(
603
+ self,
604
+ output_path: str = None,
605
+ jpeg: bool = True,
606
+ quality: int = 85,
607
+ monitor_id: int = 0,
608
+ capture_all_monitors: bool = False,
609
+ ) -> Optional[str]:
610
+ """
611
+ Take a single screenshot.
612
+
613
+ Args:
614
+ output_path: Path to save screenshot
615
+ jpeg: Use JPEG compression
616
+ quality: JPEG quality (1-100)
617
+ monitor_id: Monitor index to capture (0-based)
618
+ capture_all_monitors: Capture all monitors combined
619
+
620
+ Returns:
621
+ Path to saved screenshot
622
+ """
623
+ if output_path is None:
624
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
625
+ ext = "jpg" if jpeg else "png"
626
+ output_path = f"/tmp/screenshot_{timestamp}.{ext}"
627
+
628
+ worker = ScreenshotWorker(
629
+ output_dir=str(Path(output_path).parent),
630
+ use_jpeg=jpeg,
631
+ jpeg_quality=quality,
632
+ verbose=False,
633
+ )
634
+
635
+ # Set monitor parameters
636
+ worker.monitor = monitor_id
637
+ worker.capture_all = capture_all_monitors
638
+
639
+ # Take single screenshot
640
+ worker.session_id = "single"
641
+ worker.screenshot_count = 0
642
+ screenshot_path = worker._take_screenshot()
643
+
644
+ if screenshot_path:
645
+ # Rename to desired path
646
+ Path(screenshot_path).rename(output_path)
647
+ return output_path
648
+
649
+ return None
650
+
651
+ def get_info(self) -> dict:
652
+ """
653
+ Enumerate all available monitors and virtual desktops.
654
+
655
+ Returns:
656
+ Dictionary with monitor information
657
+ """
658
+ try:
659
+ script_dir = Path(__file__).parent / "powershell"
660
+ script_path = script_dir / "detect_monitors_and_desktops.ps1"
661
+
662
+ if not script_path.exists():
663
+ return {"error": "Detection script not found"}
664
+
665
+ # Find PowerShell
666
+ ps_paths = [
667
+ "powershell.exe",
668
+ "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe",
669
+ ]
670
+
671
+ ps_exe = None
672
+ for path in ps_paths:
673
+ try:
674
+ result = subprocess.run(
675
+ [path, "-Command", "echo test"],
676
+ capture_output=True,
677
+ timeout=1,
678
+ )
679
+ if result.returncode == 0:
680
+ ps_exe = path
681
+ break
682
+ except:
683
+ continue
684
+
685
+ if not ps_exe:
686
+ return {"error": "PowerShell not found"}
687
+
688
+ # Execute detection script
689
+ cmd = [
690
+ ps_exe,
691
+ "-NoProfile",
692
+ "-ExecutionPolicy",
693
+ "Bypass",
694
+ "-File",
695
+ str(script_path),
696
+ ]
697
+
698
+ result = subprocess.run(
699
+ cmd, capture_output=True, text=True, timeout=10
700
+ )
701
+
702
+ if result.returncode == 0 and result.stdout.strip():
703
+ # Parse JSON from output (skip non-JSON lines)
704
+ import json
705
+
706
+ lines = result.stdout.strip().split("\n")
707
+ for line in lines:
708
+ line = line.strip()
709
+ if line.startswith("{"):
710
+ return json.loads(line)
711
+
712
+ return {"error": "No JSON in output"}
713
+ else:
714
+ return {
715
+ "error": (
716
+ result.stderr if result.stderr else "Detection failed"
717
+ )
718
+ }
719
+
720
+ except Exception as e:
721
+ return {"error": str(e)}
722
+
723
+ def capture_window(
724
+ self,
725
+ window_handle: int,
726
+ output_path: str = None,
727
+ jpeg: bool = True,
728
+ quality: int = 85,
729
+ ) -> Optional[str]:
730
+ """
731
+ Capture a specific window by its handle.
732
+
733
+ Args:
734
+ window_handle: Window handle (from get_info)
735
+ output_path: Path to save screenshot
736
+ jpeg: Use JPEG compression
737
+ quality: JPEG quality (1-100)
738
+
739
+ Returns:
740
+ Path to saved screenshot or None
741
+ """
742
+ try:
743
+ script_dir = Path(__file__).parent / "powershell"
744
+ script_path = script_dir / "capture_window_by_handle.ps1"
745
+
746
+ if not script_path.exists():
747
+ return None
748
+
749
+ # Find PowerShell
750
+ ps_paths = [
751
+ "powershell.exe",
752
+ "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe",
753
+ ]
754
+
755
+ ps_exe = None
756
+ for path in ps_paths:
757
+ try:
758
+ result = subprocess.run(
759
+ [path, "-Command", "echo test"],
760
+ capture_output=True,
761
+ timeout=1,
762
+ )
763
+ if result.returncode == 0:
764
+ ps_exe = path
765
+ break
766
+ except:
767
+ continue
768
+
769
+ if not ps_exe:
770
+ return None
771
+
772
+ # Execute window capture script
773
+ cmd = [
774
+ ps_exe,
775
+ "-NoProfile",
776
+ "-ExecutionPolicy",
777
+ "Bypass",
778
+ "-File",
779
+ str(script_path),
780
+ "-WindowHandle",
781
+ str(window_handle),
782
+ ]
783
+
784
+ result = subprocess.run(
785
+ cmd, capture_output=True, text=True, timeout=10
786
+ )
787
+
788
+ if result.returncode == 0 and result.stdout.strip():
789
+ # Parse JSON from output
790
+ import base64
791
+ import json
792
+
793
+ lines = result.stdout.strip().split("\n")
794
+ for line in lines:
795
+ line = line.strip()
796
+ if line.startswith("{"):
797
+ data = json.loads(line)
798
+ break
799
+ else:
800
+ return None
801
+
802
+ if not data.get("Success"):
803
+ return None
804
+
805
+ # Generate output path if not provided
806
+ if output_path is None:
807
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
808
+ ext = "jpg" if jpeg else "png"
809
+ output_path = (
810
+ f"/tmp/window_{window_handle}_{timestamp}.{ext}"
811
+ )
812
+
813
+ # Decode base64 image
814
+ img_data = base64.b64decode(data.get("Base64Data", ""))
815
+
816
+ # Save as JPEG or PNG
817
+ if jpeg:
818
+ try:
819
+ import io
820
+
821
+ from PIL import Image
822
+
823
+ img = Image.open(io.BytesIO(img_data))
824
+ if img.mode == "RGBA":
825
+ rgb_img = Image.new(
826
+ "RGB", img.size, (255, 255, 255)
827
+ )
828
+ rgb_img.paste(img, mask=img.split()[3])
829
+ img = rgb_img
830
+ img.save(
831
+ output_path, "JPEG", quality=quality, optimize=True
832
+ )
833
+ except ImportError:
834
+ output_path = output_path.replace(
835
+ ".jpg", ".png"
836
+ ).replace(".jpeg", ".png")
837
+ with open(output_path, "wb") as f:
838
+ f.write(img_data)
839
+ else:
840
+ with open(output_path, "wb") as f:
841
+ f.write(img_data)
842
+
843
+ return output_path if Path(output_path).exists() else None
844
+
845
+ except Exception as e:
846
+ return None
847
+
848
+ # EOF