scitex 2.4.3__py3-none-any.whl → 2.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1092) hide show
  1. scitex/__init__.py +19 -8
  2. scitex/__main__.py +2 -1
  3. scitex/__version__.py +1 -1
  4. scitex/_optional_deps.py +13 -20
  5. scitex/ai/__init__.py +5 -0
  6. scitex/ai/_gen_ai/_Anthropic.py +3 -1
  7. scitex/ai/_gen_ai/_BaseGenAI.py +3 -2
  8. scitex/ai/_gen_ai/_DeepSeek.py +1 -1
  9. scitex/ai/_gen_ai/_Google.py +3 -2
  10. scitex/ai/_gen_ai/_Llama.py +4 -2
  11. scitex/ai/_gen_ai/_OpenAI.py +3 -1
  12. scitex/ai/_gen_ai/_PARAMS.py +1 -0
  13. scitex/ai/_gen_ai/_Perplexity.py +3 -1
  14. scitex/ai/_gen_ai/__init__.py +1 -0
  15. scitex/ai/_gen_ai/_format_output_func.py +3 -1
  16. scitex/ai/classification/CrossValidationExperiment.py +8 -14
  17. scitex/ai/classification/examples/timeseries_cv_demo.py +128 -112
  18. scitex/ai/classification/reporters/_BaseClassificationReporter.py +2 -0
  19. scitex/ai/classification/reporters/_ClassificationReporter.py +30 -45
  20. scitex/ai/classification/reporters/_MultiClassificationReporter.py +8 -11
  21. scitex/ai/classification/reporters/_SingleClassificationReporter.py +126 -182
  22. scitex/ai/classification/reporters/__init__.py +1 -1
  23. scitex/ai/classification/reporters/reporter_utils/_Plotter.py +213 -119
  24. scitex/ai/classification/reporters/reporter_utils/__init__.py +28 -36
  25. scitex/ai/classification/reporters/reporter_utils/aggregation.py +125 -143
  26. scitex/ai/classification/reporters/reporter_utils/data_models.py +128 -120
  27. scitex/ai/classification/reporters/reporter_utils/reporting.py +507 -340
  28. scitex/ai/classification/reporters/reporter_utils/storage.py +4 -1
  29. scitex/ai/classification/reporters/reporter_utils/validation.py +141 -154
  30. scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +204 -129
  31. scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +215 -171
  32. scitex/ai/classification/timeseries/_TimeSeriesMetadata.py +17 -17
  33. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +67 -143
  34. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +67 -143
  35. scitex/ai/classification/timeseries/_TimeSeriesStrategy.py +12 -13
  36. scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +231 -144
  37. scitex/ai/classification/timeseries/__init__.py +2 -4
  38. scitex/ai/classification/timeseries/_normalize_timestamp.py +3 -0
  39. scitex/ai/clustering/_pca.py +0 -1
  40. scitex/ai/clustering/_umap.py +1 -2
  41. scitex/ai/feature_extraction/__init__.py +10 -8
  42. scitex/ai/feature_extraction/vit.py +0 -1
  43. scitex/ai/feature_selection/feature_selection.py +3 -8
  44. scitex/ai/metrics/_calc_conf_mat.py +2 -0
  45. scitex/ai/metrics/_calc_feature_importance.py +3 -7
  46. scitex/ai/metrics/_calc_pre_rec_auc.py +5 -5
  47. scitex/ai/metrics/_calc_roc_auc.py +4 -2
  48. scitex/ai/metrics/_calc_seizure_prediction_metrics.py +35 -20
  49. scitex/ai/metrics/_calc_silhouette_score.py +1 -3
  50. scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger.py +0 -3
  51. scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger2020.py +0 -3
  52. scitex/ai/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger913A.py +0 -3
  53. scitex/ai/optim/_optimizers.py +1 -1
  54. scitex/ai/plt/__init__.py +6 -1
  55. scitex/ai/plt/_plot_feature_importance.py +1 -3
  56. scitex/ai/plt/_plot_learning_curve.py +9 -24
  57. scitex/ai/plt/_plot_optuna_study.py +4 -3
  58. scitex/ai/plt/_plot_pre_rec_curve.py +9 -15
  59. scitex/ai/plt/_plot_roc_curve.py +6 -8
  60. scitex/ai/plt/_stx_conf_mat.py +121 -122
  61. scitex/ai/sampling/undersample.py +3 -2
  62. scitex/ai/sklearn/__init__.py +2 -2
  63. scitex/ai/training/_LearningCurveLogger.py +23 -10
  64. scitex/ai/utils/_check_params.py +0 -1
  65. scitex/benchmark/__init__.py +15 -25
  66. scitex/benchmark/benchmark.py +124 -117
  67. scitex/benchmark/monitor.py +117 -107
  68. scitex/benchmark/profiler.py +61 -58
  69. scitex/bridge/__init__.py +110 -0
  70. scitex/bridge/_helpers.py +149 -0
  71. scitex/bridge/_plt_vis.py +529 -0
  72. scitex/bridge/_protocol.py +283 -0
  73. scitex/bridge/_stats_plt.py +261 -0
  74. scitex/bridge/_stats_vis.py +265 -0
  75. scitex/browser/__init__.py +0 -2
  76. scitex/browser/auth/__init__.py +0 -0
  77. scitex/browser/auth/google.py +16 -11
  78. scitex/browser/automation/CookieHandler.py +2 -3
  79. scitex/browser/collaboration/__init__.py +3 -0
  80. scitex/browser/collaboration/auth_helpers.py +3 -1
  81. scitex/browser/collaboration/collaborative_agent.py +2 -0
  82. scitex/browser/collaboration/interactive_panel.py +2 -2
  83. scitex/browser/collaboration/shared_session.py +20 -11
  84. scitex/browser/collaboration/standard_interactions.py +1 -0
  85. scitex/browser/core/BrowserMixin.py +12 -30
  86. scitex/browser/core/ChromeProfileManager.py +9 -24
  87. scitex/browser/debugging/_browser_logger.py +15 -25
  88. scitex/browser/debugging/_failure_capture.py +9 -2
  89. scitex/browser/debugging/_highlight_element.py +15 -6
  90. scitex/browser/debugging/_show_grid.py +5 -6
  91. scitex/browser/debugging/_sync_session.py +4 -3
  92. scitex/browser/debugging/_test_monitor.py +14 -5
  93. scitex/browser/debugging/_visual_cursor.py +46 -35
  94. scitex/browser/interaction/click_center.py +4 -3
  95. scitex/browser/interaction/click_with_fallbacks.py +7 -10
  96. scitex/browser/interaction/close_popups.py +79 -66
  97. scitex/browser/interaction/fill_with_fallbacks.py +8 -8
  98. scitex/browser/pdf/__init__.py +3 -1
  99. scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +11 -10
  100. scitex/browser/pdf/detect_chrome_pdf_viewer.py +3 -6
  101. scitex/browser/remote/CaptchaHandler.py +109 -96
  102. scitex/browser/remote/ZenRowsAPIClient.py +91 -97
  103. scitex/browser/remote/ZenRowsBrowserManager.py +138 -112
  104. scitex/browser/stealth/HumanBehavior.py +4 -9
  105. scitex/browser/stealth/StealthManager.py +11 -26
  106. scitex/capture/__init__.py +17 -17
  107. scitex/capture/__main__.py +2 -3
  108. scitex/capture/capture.py +23 -51
  109. scitex/capture/cli.py +14 -39
  110. scitex/capture/gif.py +5 -9
  111. scitex/capture/mcp_server.py +7 -20
  112. scitex/capture/session.py +4 -3
  113. scitex/capture/utils.py +18 -53
  114. scitex/cli/__init__.py +1 -1
  115. scitex/cli/cloud.py +158 -116
  116. scitex/cli/config.py +224 -0
  117. scitex/cli/main.py +41 -40
  118. scitex/cli/scholar.py +60 -27
  119. scitex/cli/security.py +14 -20
  120. scitex/cli/web.py +87 -90
  121. scitex/cli/writer.py +51 -45
  122. scitex/cloud/__init__.py +14 -11
  123. scitex/cloud/_matplotlib_hook.py +6 -6
  124. scitex/config/README.md +313 -0
  125. scitex/config/{PriorityConfig.py → _PriorityConfig.py} +114 -17
  126. scitex/config/_ScitexConfig.py +319 -0
  127. scitex/config/__init__.py +41 -9
  128. scitex/config/_paths.py +325 -0
  129. scitex/config/default.yaml +81 -0
  130. scitex/context/_suppress_output.py +2 -3
  131. scitex/db/_BaseMixins/_BaseBackupMixin.py +3 -1
  132. scitex/db/_BaseMixins/_BaseBatchMixin.py +3 -1
  133. scitex/db/_BaseMixins/_BaseBlobMixin.py +3 -1
  134. scitex/db/_BaseMixins/_BaseImportExportMixin.py +1 -3
  135. scitex/db/_BaseMixins/_BaseIndexMixin.py +3 -1
  136. scitex/db/_BaseMixins/_BaseMaintenanceMixin.py +1 -3
  137. scitex/db/_BaseMixins/_BaseQueryMixin.py +3 -1
  138. scitex/db/_BaseMixins/_BaseRowMixin.py +3 -1
  139. scitex/db/_BaseMixins/_BaseTableMixin.py +3 -1
  140. scitex/db/_BaseMixins/_BaseTransactionMixin.py +1 -3
  141. scitex/db/_BaseMixins/__init__.py +1 -1
  142. scitex/db/__init__.py +9 -1
  143. scitex/db/__main__.py +8 -21
  144. scitex/db/_check_health.py +15 -31
  145. scitex/db/_delete_duplicates.py +7 -4
  146. scitex/db/_inspect.py +22 -38
  147. scitex/db/_inspect_optimized.py +89 -85
  148. scitex/db/_postgresql/_PostgreSQL.py +0 -1
  149. scitex/db/_postgresql/_PostgreSQLMixins/_BlobMixin.py +3 -1
  150. scitex/db/_postgresql/_PostgreSQLMixins/_ConnectionMixin.py +1 -3
  151. scitex/db/_postgresql/_PostgreSQLMixins/_ImportExportMixin.py +1 -3
  152. scitex/db/_postgresql/_PostgreSQLMixins/_MaintenanceMixin.py +1 -4
  153. scitex/db/_postgresql/_PostgreSQLMixins/_QueryMixin.py +3 -3
  154. scitex/db/_postgresql/_PostgreSQLMixins/_RowMixin.py +3 -1
  155. scitex/db/_postgresql/_PostgreSQLMixins/_TransactionMixin.py +1 -3
  156. scitex/db/_postgresql/__init__.py +1 -1
  157. scitex/db/_sqlite3/_SQLite3.py +2 -4
  158. scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin.py +11 -12
  159. scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin_v01-need-_hash-col.py +19 -14
  160. scitex/db/_sqlite3/_SQLite3Mixins/_BatchMixin.py +3 -1
  161. scitex/db/_sqlite3/_SQLite3Mixins/_BlobMixin.py +7 -7
  162. scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin.py +118 -111
  163. scitex/db/_sqlite3/_SQLite3Mixins/_ConnectionMixin.py +8 -10
  164. scitex/db/_sqlite3/_SQLite3Mixins/_GitMixin.py +17 -45
  165. scitex/db/_sqlite3/_SQLite3Mixins/_ImportExportMixin.py +1 -3
  166. scitex/db/_sqlite3/_SQLite3Mixins/_IndexMixin.py +3 -1
  167. scitex/db/_sqlite3/_SQLite3Mixins/_QueryMixin.py +3 -4
  168. scitex/db/_sqlite3/_SQLite3Mixins/_RowMixin.py +9 -9
  169. scitex/db/_sqlite3/_SQLite3Mixins/_TableMixin.py +18 -11
  170. scitex/db/_sqlite3/_SQLite3Mixins/__init__.py +1 -0
  171. scitex/db/_sqlite3/__init__.py +1 -1
  172. scitex/db/_sqlite3/_delete_duplicates.py +13 -11
  173. scitex/decorators/__init__.py +29 -4
  174. scitex/decorators/_auto_order.py +43 -43
  175. scitex/decorators/_batch_fn.py +12 -6
  176. scitex/decorators/_cache_disk.py +8 -9
  177. scitex/decorators/_cache_disk_async.py +8 -7
  178. scitex/decorators/_combined.py +19 -13
  179. scitex/decorators/_converters.py +16 -3
  180. scitex/decorators/_deprecated.py +32 -22
  181. scitex/decorators/_numpy_fn.py +18 -4
  182. scitex/decorators/_pandas_fn.py +17 -5
  183. scitex/decorators/_signal_fn.py +17 -3
  184. scitex/decorators/_torch_fn.py +32 -15
  185. scitex/decorators/_xarray_fn.py +23 -9
  186. scitex/dev/_analyze_code_flow.py +0 -2
  187. scitex/dict/_DotDict.py +15 -19
  188. scitex/dict/_flatten.py +1 -0
  189. scitex/dict/_listed_dict.py +1 -0
  190. scitex/dict/_pop_keys.py +1 -0
  191. scitex/dict/_replace.py +1 -0
  192. scitex/dict/_safe_merge.py +1 -0
  193. scitex/dict/_to_str.py +2 -3
  194. scitex/dsp/__init__.py +13 -4
  195. scitex/dsp/_crop.py +3 -1
  196. scitex/dsp/_detect_ripples.py +3 -1
  197. scitex/dsp/_modulation_index.py +3 -1
  198. scitex/dsp/_time.py +3 -1
  199. scitex/dsp/_wavelet.py +0 -1
  200. scitex/dsp/example.py +0 -5
  201. scitex/dsp/filt.py +4 -0
  202. scitex/dsp/utils/__init__.py +4 -1
  203. scitex/dsp/utils/pac.py +3 -3
  204. scitex/dt/_normalize_timestamp.py +4 -1
  205. scitex/errors.py +3 -6
  206. scitex/etc/__init__.py +1 -1
  207. scitex/gen/_DimHandler.py +6 -6
  208. scitex/gen/__init__.py +5 -1
  209. scitex/gen/_deprecated_close.py +1 -0
  210. scitex/gen/_deprecated_start.py +5 -3
  211. scitex/gen/_detect_environment.py +44 -41
  212. scitex/gen/_detect_notebook_path.py +51 -47
  213. scitex/gen/_embed.py +1 -1
  214. scitex/gen/_get_notebook_path.py +81 -62
  215. scitex/gen/_inspect_module.py +0 -1
  216. scitex/gen/_norm.py +16 -7
  217. scitex/gen/_norm_cache.py +78 -65
  218. scitex/gen/_print_config.py +0 -3
  219. scitex/gen/_src.py +2 -3
  220. scitex/gen/_title_case.py +3 -2
  221. scitex/gen/_to_even.py +8 -8
  222. scitex/gen/_transpose.py +3 -3
  223. scitex/gen/misc.py +0 -3
  224. scitex/gists/_SigMacro_processFigure_S.py +2 -2
  225. scitex/gists/_SigMacro_toBlue.py +2 -2
  226. scitex/gists/__init__.py +4 -1
  227. scitex/git/_branch.py +19 -11
  228. scitex/git/_clone.py +23 -15
  229. scitex/git/_commit.py +10 -12
  230. scitex/git/_init.py +15 -38
  231. scitex/git/_remote.py +9 -3
  232. scitex/git/_result.py +3 -0
  233. scitex/git/_retry.py +2 -5
  234. scitex/git/_types.py +4 -0
  235. scitex/git/_validation.py +8 -8
  236. scitex/git/_workflow.py +4 -4
  237. scitex/io/__init__.py +2 -1
  238. scitex/io/_glob.py +2 -2
  239. scitex/io/_json2md.py +3 -3
  240. scitex/io/_load.py +11 -8
  241. scitex/io/_load_cache.py +71 -71
  242. scitex/io/_load_configs.py +2 -3
  243. scitex/io/_load_modules/_H5Explorer.py +6 -12
  244. scitex/io/_load_modules/_ZarrExplorer.py +3 -3
  245. scitex/io/_load_modules/_bibtex.py +62 -63
  246. scitex/io/_load_modules/_canvas.py +166 -0
  247. scitex/io/_load_modules/_catboost.py +7 -2
  248. scitex/io/_load_modules/_hdf5.py +2 -0
  249. scitex/io/_load_modules/_image.py +5 -1
  250. scitex/io/_load_modules/_matlab.py +3 -1
  251. scitex/io/_load_modules/_optuna.py +0 -1
  252. scitex/io/_load_modules/_pdf.py +38 -29
  253. scitex/io/_load_modules/_sqlite3.py +1 -0
  254. scitex/io/_load_modules/_txt.py +2 -0
  255. scitex/io/_load_modules/_xml.py +9 -9
  256. scitex/io/_load_modules/_zarr.py +12 -10
  257. scitex/io/_metadata.py +76 -37
  258. scitex/io/_qr_utils.py +18 -13
  259. scitex/io/_save.py +228 -63
  260. scitex/io/_save_modules/__init__.py +7 -2
  261. scitex/io/_save_modules/_bibtex.py +66 -61
  262. scitex/io/_save_modules/_canvas.py +355 -0
  263. scitex/io/_save_modules/_catboost.py +2 -2
  264. scitex/io/_save_modules/_csv.py +4 -4
  265. scitex/io/_save_modules/_excel.py +5 -9
  266. scitex/io/_save_modules/_hdf5.py +9 -21
  267. scitex/io/_save_modules/_html.py +5 -5
  268. scitex/io/_save_modules/_image.py +105 -8
  269. scitex/io/_save_modules/_joblib.py +2 -2
  270. scitex/io/_save_modules/_json.py +51 -6
  271. scitex/io/_save_modules/_listed_dfs_as_csv.py +2 -1
  272. scitex/io/_save_modules/_listed_scalars_as_csv.py +2 -1
  273. scitex/io/_save_modules/_matlab.py +2 -2
  274. scitex/io/_save_modules/_numpy.py +6 -8
  275. scitex/io/_save_modules/_pickle.py +4 -4
  276. scitex/io/_save_modules/_plotly.py +3 -3
  277. scitex/io/_save_modules/_tex.py +23 -25
  278. scitex/io/_save_modules/_text.py +2 -2
  279. scitex/io/_save_modules/_yaml.py +9 -9
  280. scitex/io/_save_modules/_zarr.py +15 -15
  281. scitex/io/utils/__init__.py +2 -1
  282. scitex/io/utils/h5_to_zarr.py +173 -155
  283. scitex/linalg/__init__.py +1 -1
  284. scitex/linalg/_geometric_median.py +4 -3
  285. scitex/logging/_Tee.py +5 -7
  286. scitex/logging/__init__.py +18 -19
  287. scitex/logging/_config.py +4 -1
  288. scitex/logging/_context.py +6 -5
  289. scitex/logging/_formatters.py +2 -3
  290. scitex/logging/_handlers.py +19 -20
  291. scitex/logging/_levels.py +9 -17
  292. scitex/logging/_logger.py +74 -15
  293. scitex/logging/_print_capture.py +17 -17
  294. scitex/nn/_BNet.py +1 -3
  295. scitex/nn/_Filters.py +6 -2
  296. scitex/nn/_ModulationIndex.py +3 -1
  297. scitex/nn/_PAC.py +3 -2
  298. scitex/nn/_PSD.py +0 -1
  299. scitex/nn/__init__.py +16 -3
  300. scitex/path/_clean.py +10 -8
  301. scitex/path/_find.py +1 -1
  302. scitex/path/_get_spath.py +1 -2
  303. scitex/path/_mk_spath.py +1 -1
  304. scitex/path/_symlink.py +5 -10
  305. scitex/pd/__init__.py +4 -1
  306. scitex/pd/_force_df.py +24 -24
  307. scitex/pd/_get_unique.py +1 -0
  308. scitex/pd/_merge_columns.py +1 -1
  309. scitex/pd/_round.py +11 -7
  310. scitex/pd/_to_xy.py +0 -1
  311. scitex/plt/REQUESTS.md +191 -0
  312. scitex/plt/__init__.py +185 -87
  313. scitex/plt/_subplots/_AxesWrapper.py +22 -6
  314. scitex/plt/_subplots/_AxisWrapper.py +100 -39
  315. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +74 -52
  316. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +183 -73
  317. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +61 -45
  318. scitex/plt/_subplots/_AxisWrapperMixins/_TrackingMixin.py +26 -14
  319. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +80 -73
  320. scitex/plt/_subplots/_FigWrapper.py +93 -60
  321. scitex/plt/_subplots/_SubplotsWrapper.py +135 -68
  322. scitex/plt/_subplots/__init__.py +10 -0
  323. scitex/plt/_subplots/_export_as_csv.py +89 -47
  324. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +1 -0
  325. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +6 -4
  326. scitex/plt/_subplots/_export_as_csv_formatters/_format_bar.py +88 -38
  327. scitex/plt/_subplots/_export_as_csv_formatters/_format_barh.py +25 -31
  328. scitex/plt/_subplots/_export_as_csv_formatters/_format_boxplot.py +53 -23
  329. scitex/plt/_subplots/_export_as_csv_formatters/_format_contour.py +38 -25
  330. scitex/plt/_subplots/_export_as_csv_formatters/_format_contourf.py +17 -9
  331. scitex/plt/_subplots/_export_as_csv_formatters/_format_errorbar.py +70 -124
  332. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +12 -10
  333. scitex/plt/_subplots/_export_as_csv_formatters/_format_fill.py +31 -17
  334. scitex/plt/_subplots/_export_as_csv_formatters/_format_fill_between.py +33 -21
  335. scitex/plt/_subplots/_export_as_csv_formatters/_format_hexbin.py +14 -4
  336. scitex/plt/_subplots/_export_as_csv_formatters/_format_hist.py +43 -29
  337. scitex/plt/_subplots/_export_as_csv_formatters/_format_hist2d.py +14 -4
  338. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +27 -11
  339. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +7 -5
  340. scitex/plt/_subplots/_export_as_csv_formatters/_format_matshow.py +9 -7
  341. scitex/plt/_subplots/_export_as_csv_formatters/_format_pie.py +15 -6
  342. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +128 -34
  343. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +52 -27
  344. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_imshow.py +1 -0
  345. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +16 -17
  346. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +7 -5
  347. scitex/plt/_subplots/_export_as_csv_formatters/_format_quiver.py +10 -8
  348. scitex/plt/_subplots/_export_as_csv_formatters/_format_scatter.py +17 -6
  349. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_barplot.py +43 -26
  350. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_boxplot.py +68 -47
  351. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_heatmap.py +52 -64
  352. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_histplot.py +55 -50
  353. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +9 -11
  354. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_kdeplot.py +63 -29
  355. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +4 -4
  356. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +6 -4
  357. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_scatterplot.py +44 -40
  358. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_stripplot.py +46 -39
  359. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_swarmplot.py +46 -39
  360. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_violinplot.py +75 -94
  361. scitex/plt/_subplots/_export_as_csv_formatters/_format_stem.py +12 -3
  362. scitex/plt/_subplots/_export_as_csv_formatters/_format_step.py +12 -3
  363. scitex/plt/_subplots/_export_as_csv_formatters/_format_streamplot.py +10 -8
  364. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_conf_mat.py +17 -15
  365. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_ecdf.py +10 -9
  366. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_fillv.py +35 -31
  367. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_heatmap.py +18 -18
  368. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_image.py +24 -18
  369. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_joyplot.py +9 -7
  370. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_line.py +34 -23
  371. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_ci.py +15 -13
  372. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_mean_std.py +12 -10
  373. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_median_iqr.py +15 -13
  374. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_raster.py +11 -9
  375. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_rectangle.py +84 -56
  376. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_scatter_hist.py +35 -32
  377. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_shaded_line.py +46 -30
  378. scitex/plt/_subplots/_export_as_csv_formatters/_format_stx_violin.py +51 -51
  379. scitex/plt/_subplots/_export_as_csv_formatters/_format_text.py +32 -31
  380. scitex/plt/_subplots/_export_as_csv_formatters/_format_violin.py +34 -31
  381. scitex/plt/_subplots/_export_as_csv_formatters/_format_violinplot.py +44 -37
  382. scitex/plt/_subplots/_export_as_csv_formatters/verify_formatters.py +91 -74
  383. scitex/plt/_tpl.py +6 -5
  384. scitex/plt/ax/_plot/__init__.py +24 -0
  385. scitex/plt/ax/_plot/_add_fitted_line.py +12 -11
  386. scitex/plt/ax/_plot/_plot_circular_hist.py +3 -1
  387. scitex/plt/ax/_plot/_plot_statistical_shaded_line.py +25 -19
  388. scitex/plt/ax/_plot/_stx_conf_mat.py +6 -3
  389. scitex/plt/ax/_plot/_stx_ecdf.py +5 -3
  390. scitex/plt/ax/_plot/_stx_fillv.py +4 -2
  391. scitex/plt/ax/_plot/_stx_heatmap.py +7 -4
  392. scitex/plt/ax/_plot/_stx_image.py +7 -5
  393. scitex/plt/ax/_plot/_stx_joyplot.py +32 -10
  394. scitex/plt/ax/_plot/_stx_raster.py +26 -11
  395. scitex/plt/ax/_plot/_stx_rectangle.py +2 -2
  396. scitex/plt/ax/_plot/_stx_shaded_line.py +15 -11
  397. scitex/plt/ax/_plot/_stx_violin.py +3 -1
  398. scitex/plt/ax/_style/_add_marginal_ax.py +6 -4
  399. scitex/plt/ax/_style/_auto_scale_axis.py +14 -10
  400. scitex/plt/ax/_style/_extend.py +3 -1
  401. scitex/plt/ax/_style/_force_aspect.py +5 -3
  402. scitex/plt/ax/_style/_format_units.py +2 -2
  403. scitex/plt/ax/_style/_hide_spines.py +5 -1
  404. scitex/plt/ax/_style/_map_ticks.py +5 -3
  405. scitex/plt/ax/_style/_rotate_labels.py +5 -4
  406. scitex/plt/ax/_style/_rotate_labels_v01.py +73 -63
  407. scitex/plt/ax/_style/_set_log_scale.py +120 -85
  408. scitex/plt/ax/_style/_set_meta.py +99 -76
  409. scitex/plt/ax/_style/_set_supxyt.py +33 -16
  410. scitex/plt/ax/_style/_set_xyt.py +27 -18
  411. scitex/plt/ax/_style/_share_axes.py +15 -5
  412. scitex/plt/ax/_style/_show_spines.py +58 -57
  413. scitex/plt/ax/_style/_style_barplot.py +1 -1
  414. scitex/plt/ax/_style/_style_boxplot.py +25 -14
  415. scitex/plt/ax/_style/_style_errorbar.py +0 -0
  416. scitex/plt/ax/_style/_style_scatter.py +1 -1
  417. scitex/plt/ax/_style/_style_suptitles.py +3 -3
  418. scitex/plt/ax/_style/_style_violinplot.py +8 -2
  419. scitex/plt/color/__init__.py +34 -2
  420. scitex/plt/color/_add_hue_col.py +1 -0
  421. scitex/plt/color/_colors.py +0 -1
  422. scitex/plt/color/_get_colors_from_conf_matap.py +3 -1
  423. scitex/plt/color/_vizualize_colors.py +0 -1
  424. scitex/plt/docs/FIGURE_ARCHITECTURE.md +315 -0
  425. scitex/plt/gallery/README.md +75 -0
  426. scitex/plt/gallery/__init__.py +29 -0
  427. scitex/plt/gallery/_generate.py +153 -0
  428. scitex/plt/gallery/_plots.py +594 -0
  429. scitex/plt/gallery/_registry.py +153 -0
  430. scitex/plt/styles/__init__.py +9 -9
  431. scitex/plt/styles/_plot_defaults.py +62 -61
  432. scitex/plt/styles/_plot_postprocess.py +126 -77
  433. scitex/plt/styles/_style_loader.py +0 -0
  434. scitex/plt/styles/presets.py +43 -18
  435. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_between.json +110 -0
  436. scitex/plt/templates/research-master/scitex/vis/gallery/area/fill_betweenx.json +88 -0
  437. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fill_between.json +103 -0
  438. scitex/plt/templates/research-master/scitex/vis/gallery/area/stx_fillv.json +106 -0
  439. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/bar.json +92 -0
  440. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/barh.json +92 -0
  441. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/boxplot.json +92 -0
  442. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_bar.json +84 -0
  443. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_barh.json +84 -0
  444. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_box.json +83 -0
  445. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_boxplot.json +93 -0
  446. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violin.json +91 -0
  447. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/stx_violinplot.json +91 -0
  448. scitex/plt/templates/research-master/scitex/vis/gallery/categorical/violinplot.json +91 -0
  449. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contour.json +97 -0
  450. scitex/plt/templates/research-master/scitex/vis/gallery/contour/contourf.json +98 -0
  451. scitex/plt/templates/research-master/scitex/vis/gallery/contour/stx_contour.json +84 -0
  452. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist.json +101 -0
  453. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/hist2d.json +96 -0
  454. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_ecdf.json +95 -0
  455. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_joyplot.json +95 -0
  456. scitex/plt/templates/research-master/scitex/vis/gallery/distribution/stx_kde.json +93 -0
  457. scitex/plt/templates/research-master/scitex/vis/gallery/grid/imshow.json +95 -0
  458. scitex/plt/templates/research-master/scitex/vis/gallery/grid/matshow.json +95 -0
  459. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_conf_mat.json +83 -0
  460. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_heatmap.json +92 -0
  461. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_image.json +121 -0
  462. scitex/plt/templates/research-master/scitex/vis/gallery/grid/stx_imshow.json +84 -0
  463. scitex/plt/templates/research-master/scitex/vis/gallery/line/plot.json +110 -0
  464. scitex/plt/templates/research-master/scitex/vis/gallery/line/step.json +92 -0
  465. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_line.json +95 -0
  466. scitex/plt/templates/research-master/scitex/vis/gallery/line/stx_shaded_line.json +96 -0
  467. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/hexbin.json +95 -0
  468. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/scatter.json +95 -0
  469. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stem.json +92 -0
  470. scitex/plt/templates/research-master/scitex/vis/gallery/scatter/stx_scatter.json +84 -0
  471. scitex/plt/templates/research-master/scitex/vis/gallery/special/pie.json +94 -0
  472. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_raster.json +109 -0
  473. scitex/plt/templates/research-master/scitex/vis/gallery/special/stx_rectangle.json +108 -0
  474. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/errorbar.json +93 -0
  475. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_errorbar.json +84 -0
  476. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_ci.json +96 -0
  477. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_mean_std.json +96 -0
  478. scitex/plt/templates/research-master/scitex/vis/gallery/statistical/stx_median_iqr.json +96 -0
  479. scitex/plt/templates/research-master/scitex/vis/gallery/vector/quiver.json +99 -0
  480. scitex/plt/templates/research-master/scitex/vis/gallery/vector/streamplot.json +100 -0
  481. scitex/plt/utils/__init__.py +39 -2
  482. scitex/plt/utils/_close.py +8 -3
  483. scitex/plt/utils/_collect_figure_metadata.py +3033 -265
  484. scitex/plt/utils/_colorbar.py +15 -17
  485. scitex/plt/utils/_configure_mpl.py +22 -14
  486. scitex/plt/utils/_crop.py +60 -27
  487. scitex/plt/utils/_csv_column_naming.py +288 -0
  488. scitex/plt/utils/_dimension_viewer.py +7 -19
  489. scitex/plt/utils/_figure_from_axes_mm.py +70 -16
  490. scitex/plt/utils/_figure_mm.py +3 -2
  491. scitex/plt/utils/_get_actual_font.py +5 -4
  492. scitex/plt/utils/_histogram_utils.py +52 -48
  493. scitex/plt/utils/_is_valid_axis.py +19 -13
  494. scitex/plt/utils/_mk_colorbar.py +3 -3
  495. scitex/plt/utils/_scientific_captions.py +202 -139
  496. scitex/plt/utils/_scitex_config.py +98 -98
  497. scitex/plt/utils/_units.py +0 -0
  498. scitex/plt/utils/metadata/__init__.py +36 -0
  499. scitex/plt/utils/metadata/_artist_extraction.py +119 -0
  500. scitex/plt/utils/metadata/_axes_metadata.py +93 -0
  501. scitex/plt/utils/metadata/_collection_artists.py +292 -0
  502. scitex/plt/utils/metadata/_core.py +208 -0
  503. scitex/plt/utils/metadata/_csv_column_extraction.py +186 -0
  504. scitex/plt/utils/metadata/_csv_hash.py +115 -0
  505. scitex/plt/utils/metadata/_csv_verification.py +95 -0
  506. scitex/plt/utils/metadata/_data_linkage.py +263 -0
  507. scitex/plt/utils/metadata/_dimensions.py +239 -0
  508. scitex/plt/utils/metadata/_figure_metadata.py +58 -0
  509. scitex/plt/utils/metadata/_image_text_artists.py +168 -0
  510. scitex/plt/utils/metadata/_label_parsing.py +82 -0
  511. scitex/plt/utils/metadata/_legend_extraction.py +120 -0
  512. scitex/plt/utils/metadata/_line_artists.py +367 -0
  513. scitex/plt/utils/metadata/_line_semantic_handling.py +173 -0
  514. scitex/plt/utils/metadata/_patch_artists.py +211 -0
  515. scitex/plt/utils/metadata/_plot_content.py +26 -0
  516. scitex/plt/utils/metadata/_plot_type_detection.py +184 -0
  517. scitex/plt/utils/metadata/_precision.py +134 -0
  518. scitex/plt/utils/metadata/_precision_config.py +68 -0
  519. scitex/plt/utils/metadata/_precision_sections.py +211 -0
  520. scitex/plt/utils/metadata/_recipe_extraction.py +267 -0
  521. scitex/plt/utils/metadata/_style_parsing.py +174 -0
  522. scitex/repro/_RandomStateManager.py +33 -38
  523. scitex/repro/__init__.py +16 -7
  524. scitex/repro/_gen_ID.py +7 -9
  525. scitex/repro/_gen_timestamp.py +7 -6
  526. scitex/repro/_hash_array.py +8 -12
  527. scitex/reproduce/__init__.py +1 -1
  528. scitex/resource/_get_processor_usages.py +3 -1
  529. scitex/resource/_log_processor_usages.py +3 -1
  530. scitex/rng/__init__.py +1 -1
  531. scitex/schema/README.md +178 -0
  532. scitex/schema/__init__.py +144 -0
  533. scitex/schema/_canvas.py +444 -0
  534. scitex/schema/_stats.py +762 -0
  535. scitex/schema/_validation.py +590 -0
  536. scitex/scholar/.legacy/Scholar.py +5 -12
  537. scitex/scholar/.legacy/_Scholar.py +66 -99
  538. scitex/scholar/.legacy/_ScholarAPI.py +75 -66
  539. scitex/scholar/.legacy/_tmp/search_engine/_BaseSearchEngine.py +3 -3
  540. scitex/scholar/.legacy/_tmp/search_engine/_UnifiedSearcher.py +4 -9
  541. scitex/scholar/.legacy/_tmp/search_engine/__init__.py +14 -21
  542. scitex/scholar/.legacy/_tmp/search_engine/local/_LocalSearchEngine.py +40 -37
  543. scitex/scholar/.legacy/_tmp/search_engine/local/_VectorSearchEngine.py +31 -28
  544. scitex/scholar/.legacy/_tmp/search_engine/web/_ArxivSearchEngine.py +74 -65
  545. scitex/scholar/.legacy/_tmp/search_engine/web/_CrossRefSearchEngine.py +122 -116
  546. scitex/scholar/.legacy/_tmp/search_engine/web/_GoogleScholarSearchEngine.py +65 -59
  547. scitex/scholar/.legacy/_tmp/search_engine/web/_PubMedSearchEngine.py +121 -107
  548. scitex/scholar/.legacy/_tmp/search_engine/web/_SemanticScholarSearchEngine.py +5 -12
  549. scitex/scholar/.legacy/database/_DatabaseEntry.py +49 -45
  550. scitex/scholar/.legacy/database/_DatabaseIndex.py +131 -94
  551. scitex/scholar/.legacy/database/_LibraryManager.py +65 -63
  552. scitex/scholar/.legacy/database/_PaperDatabase.py +138 -124
  553. scitex/scholar/.legacy/database/_ScholarDatabaseIntegration.py +14 -36
  554. scitex/scholar/.legacy/database/_StorageIntegratedDB.py +192 -156
  555. scitex/scholar/.legacy/database/_ZoteroCompatibleDB.py +300 -237
  556. scitex/scholar/.legacy/database/__init__.py +2 -1
  557. scitex/scholar/.legacy/database/manage.py +92 -84
  558. scitex/scholar/.legacy/lookup/_LookupIndex.py +157 -101
  559. scitex/scholar/.legacy/lookup/__init__.py +2 -1
  560. scitex/scholar/.legacy/metadata/doi/batch/_MetadataHandlerForBatchDOIResolution.py +4 -9
  561. scitex/scholar/.legacy/metadata/doi/batch/_ProgressManagerForBatchDOIResolution.py +10 -23
  562. scitex/scholar/.legacy/metadata/doi/batch/_SourceStatsManagerForBatchDOIResolution.py +4 -9
  563. scitex/scholar/.legacy/metadata/doi/batch/__init__.py +3 -1
  564. scitex/scholar/.legacy/metadata/doi/resolvers/_BatchDOIResolver.py +10 -25
  565. scitex/scholar/.legacy/metadata/doi/resolvers/_BibTeXDOIResolver.py +19 -49
  566. scitex/scholar/.legacy/metadata/doi/resolvers/_DOIResolver.py +1 -0
  567. scitex/scholar/.legacy/metadata/doi/resolvers/_SingleDOIResolver.py +8 -20
  568. scitex/scholar/.legacy/metadata/doi/sources/.combined-SemanticScholarSource/_SemanticScholarSource.py +37 -35
  569. scitex/scholar/.legacy/metadata/doi/sources/.combined-SemanticScholarSource/_SemanticScholarSourceEnhanced.py +49 -37
  570. scitex/scholar/.legacy/metadata/doi/sources/_ArXivSource.py +11 -30
  571. scitex/scholar/.legacy/metadata/doi/sources/_BaseDOISource.py +19 -47
  572. scitex/scholar/.legacy/metadata/doi/sources/_CrossRefLocalSource.py +1 -0
  573. scitex/scholar/.legacy/metadata/doi/sources/_CrossRefSource.py +12 -33
  574. scitex/scholar/.legacy/metadata/doi/sources/_OpenAlexSource.py +8 -20
  575. scitex/scholar/.legacy/metadata/doi/sources/_PubMedSource.py +10 -27
  576. scitex/scholar/.legacy/metadata/doi/sources/_SemanticScholarSource.py +11 -29
  577. scitex/scholar/.legacy/metadata/doi/sources/_SourceManager.py +8 -21
  578. scitex/scholar/.legacy/metadata/doi/sources/_SourceResolutionStrategy.py +24 -55
  579. scitex/scholar/.legacy/metadata/doi/sources/_SourceRotationManager.py +8 -21
  580. scitex/scholar/.legacy/metadata/doi/sources/_URLDOISource.py +9 -16
  581. scitex/scholar/.legacy/metadata/doi/sources/_UnifiedSource.py +8 -22
  582. scitex/scholar/.legacy/metadata/doi/sources/__init__.py +1 -0
  583. scitex/scholar/.legacy/metadata/doi/utils/_PubMedConverter.py +4 -8
  584. scitex/scholar/.legacy/metadata/doi/utils/_RateLimitHandler.py +17 -43
  585. scitex/scholar/.legacy/metadata/doi/utils/_TextNormalizer.py +8 -18
  586. scitex/scholar/.legacy/metadata/doi/utils/_URLDOIExtractor.py +4 -8
  587. scitex/scholar/.legacy/metadata/doi/utils/__init__.py +1 -0
  588. scitex/scholar/.legacy/metadata/doi/utils/_to_complete_metadata_structure.py +1 -0
  589. scitex/scholar/.legacy/metadata/enrichment/_LibraryEnricher.py +2 -3
  590. scitex/scholar/.legacy/metadata/enrichment/enrichers/_ImpactFactorEnricher.py +6 -12
  591. scitex/scholar/.legacy/metadata/enrichment/enrichers/_SmartEnricher.py +5 -10
  592. scitex/scholar/.legacy/metadata/enrichment/sources/_UnifiedMetadataSource.py +4 -5
  593. scitex/scholar/.legacy/metadata/query_to_full_meta_json.py +8 -12
  594. scitex/scholar/.legacy/metadata/urls/_URLMetadataHandler.py +3 -3
  595. scitex/scholar/.legacy/metadata/urls/_ZoteroTranslatorRunner.py +15 -21
  596. scitex/scholar/.legacy/metadata/urls/__init__.py +3 -3
  597. scitex/scholar/.legacy/metadata/urls/_finder.py +4 -6
  598. scitex/scholar/.legacy/metadata/urls/_handler.py +7 -15
  599. scitex/scholar/.legacy/metadata/urls/_resolver.py +6 -12
  600. scitex/scholar/.legacy/search/_Embedder.py +74 -69
  601. scitex/scholar/.legacy/search/_SemanticSearch.py +91 -90
  602. scitex/scholar/.legacy/search/_SemanticSearchEngine.py +104 -109
  603. scitex/scholar/.legacy/search/_UnifiedSearcher.py +530 -471
  604. scitex/scholar/.legacy/search/_VectorDatabase.py +111 -92
  605. scitex/scholar/.legacy/search/__init__.py +1 -0
  606. scitex/scholar/.legacy/storage/_EnhancedStorageManager.py +182 -154
  607. scitex/scholar/.legacy/storage/__init__.py +2 -1
  608. scitex/scholar/__init__.py +0 -2
  609. scitex/scholar/__main__.py +1 -3
  610. scitex/scholar/auth/ScholarAuthManager.py +13 -36
  611. scitex/scholar/auth/core/AuthenticationGateway.py +15 -29
  612. scitex/scholar/auth/core/BrowserAuthenticator.py +22 -57
  613. scitex/scholar/auth/core/StrategyResolver.py +10 -27
  614. scitex/scholar/auth/core/__init__.py +5 -1
  615. scitex/scholar/auth/gateway/_OpenURLLinkFinder.py +11 -21
  616. scitex/scholar/auth/gateway/_OpenURLResolver.py +10 -18
  617. scitex/scholar/auth/gateway/_resolve_functions.py +3 -3
  618. scitex/scholar/auth/providers/BaseAuthenticator.py +1 -0
  619. scitex/scholar/auth/providers/EZProxyAuthenticator.py +7 -14
  620. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +29 -57
  621. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +87 -73
  622. scitex/scholar/auth/session/AuthCacheManager.py +12 -22
  623. scitex/scholar/auth/session/SessionManager.py +4 -6
  624. scitex/scholar/auth/sso/BaseSSOAutomator.py +13 -19
  625. scitex/scholar/auth/sso/OpenAthensSSOAutomator.py +16 -45
  626. scitex/scholar/auth/sso/SSOAutomator.py +8 -15
  627. scitex/scholar/auth/sso/UniversityOfMelbourneSSOAutomator.py +13 -23
  628. scitex/scholar/browser/ScholarBrowserManager.py +31 -56
  629. scitex/scholar/browser/__init__.py +1 -0
  630. scitex/scholar/browser/utils/click_and_wait.py +3 -4
  631. scitex/scholar/browser/utils/close_unwanted_pages.py +4 -7
  632. scitex/scholar/browser/utils/wait_redirects.py +15 -40
  633. scitex/scholar/citation_graph/__init__.py +0 -0
  634. scitex/scholar/citation_graph/builder.py +3 -7
  635. scitex/scholar/citation_graph/database.py +4 -11
  636. scitex/scholar/citation_graph/example.py +5 -10
  637. scitex/scholar/citation_graph/models.py +0 -0
  638. scitex/scholar/cli/_url_utils.py +1 -1
  639. scitex/scholar/cli/chrome.py +5 -3
  640. scitex/scholar/cli/download_pdf.py +13 -14
  641. scitex/scholar/cli/handlers/bibtex_handler.py +4 -12
  642. scitex/scholar/cli/handlers/doi_handler.py +1 -3
  643. scitex/scholar/cli/handlers/project_handler.py +6 -20
  644. scitex/scholar/cli/open_browser.py +41 -39
  645. scitex/scholar/cli/open_browser_auto.py +31 -39
  646. scitex/scholar/cli/open_browser_monitored.py +27 -24
  647. scitex/scholar/config/ScholarConfig.py +5 -8
  648. scitex/scholar/config/__init__.py +1 -0
  649. scitex/scholar/config/core/_CascadeConfig.py +3 -3
  650. scitex/scholar/config/core/_PathManager.py +16 -28
  651. scitex/scholar/core/Paper.py +79 -78
  652. scitex/scholar/core/Papers.py +16 -27
  653. scitex/scholar/core/Scholar.py +98 -229
  654. scitex/scholar/core/journal_normalizer.py +52 -49
  655. scitex/scholar/core/oa_cache.py +27 -23
  656. scitex/scholar/core/open_access.py +17 -8
  657. scitex/scholar/docs/template.py +4 -3
  658. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/clf_svm.py +0 -0
  659. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/download.py +0 -0
  660. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_conf_mat.py +0 -0
  661. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_digits.py +0 -0
  662. scitex/scholar/docs/to_claude/examples/example-python-project-scitex/scripts/mnist/plot_umap_space.py +0 -0
  663. scitex/scholar/examples/00_config.py +10 -9
  664. scitex/scholar/examples/01_auth.py +3 -0
  665. scitex/scholar/examples/02_browser.py +14 -10
  666. scitex/scholar/examples/03_01-engine.py +3 -0
  667. scitex/scholar/examples/03_02-engine-for-bibtex.py +4 -3
  668. scitex/scholar/examples/04_01-url.py +9 -9
  669. scitex/scholar/examples/04_02-url-for-bibtex.py +7 -3
  670. scitex/scholar/examples/04_02-url-for-dois.py +87 -97
  671. scitex/scholar/examples/05_download_pdf.py +10 -4
  672. scitex/scholar/examples/06_find_and_download.py +6 -6
  673. scitex/scholar/examples/06_parse_bibtex.py +17 -17
  674. scitex/scholar/examples/07_storage_integration.py +6 -9
  675. scitex/scholar/examples/99_fullpipeline-for-bibtex.py +14 -15
  676. scitex/scholar/examples/99_fullpipeline-for-one-entry.py +31 -23
  677. scitex/scholar/examples/99_maintenance.py +3 -0
  678. scitex/scholar/examples/dev.py +2 -3
  679. scitex/scholar/examples/zotero_integration.py +11 -18
  680. scitex/scholar/impact_factor/ImpactFactorEngine.py +7 -9
  681. scitex/scholar/impact_factor/estimation/__init__.py +4 -4
  682. scitex/scholar/impact_factor/estimation/core/__init__.py +3 -7
  683. scitex/scholar/impact_factor/estimation/core/cache_manager.py +223 -211
  684. scitex/scholar/impact_factor/estimation/core/calculator.py +165 -131
  685. scitex/scholar/impact_factor/estimation/core/journal_matcher.py +217 -172
  686. scitex/scholar/impact_factor/jcr/ImpactFactorJCREngine.py +6 -14
  687. scitex/scholar/impact_factor/jcr/build_database.py +4 -3
  688. scitex/scholar/integration/base.py +9 -17
  689. scitex/scholar/integration/mendeley/exporter.py +2 -4
  690. scitex/scholar/integration/mendeley/importer.py +3 -3
  691. scitex/scholar/integration/mendeley/linker.py +3 -3
  692. scitex/scholar/integration/mendeley/mapper.py +9 -6
  693. scitex/scholar/integration/zotero/__main__.py +26 -43
  694. scitex/scholar/integration/zotero/exporter.py +15 -11
  695. scitex/scholar/integration/zotero/importer.py +12 -10
  696. scitex/scholar/integration/zotero/linker.py +8 -12
  697. scitex/scholar/integration/zotero/mapper.py +17 -12
  698. scitex/scholar/metadata_engines/.combined-SemanticScholarSource/_SemanticScholarSource.py +37 -35
  699. scitex/scholar/metadata_engines/.combined-SemanticScholarSource/_SemanticScholarSourceEnhanced.py +47 -35
  700. scitex/scholar/metadata_engines/ScholarEngine.py +21 -43
  701. scitex/scholar/metadata_engines/__init__.py +1 -0
  702. scitex/scholar/metadata_engines/individual/ArXivEngine.py +15 -37
  703. scitex/scholar/metadata_engines/individual/CrossRefEngine.py +15 -42
  704. scitex/scholar/metadata_engines/individual/CrossRefLocalEngine.py +24 -45
  705. scitex/scholar/metadata_engines/individual/OpenAlexEngine.py +11 -21
  706. scitex/scholar/metadata_engines/individual/PubMedEngine.py +10 -27
  707. scitex/scholar/metadata_engines/individual/SemanticScholarEngine.py +28 -35
  708. scitex/scholar/metadata_engines/individual/URLDOIEngine.py +11 -22
  709. scitex/scholar/metadata_engines/individual/_BaseDOIEngine.py +20 -49
  710. scitex/scholar/metadata_engines/utils/_PubMedConverter.py +4 -8
  711. scitex/scholar/metadata_engines/utils/_URLDOIExtractor.py +5 -10
  712. scitex/scholar/metadata_engines/utils/__init__.py +2 -0
  713. scitex/scholar/metadata_engines/utils/_metadata2bibtex.py +3 -0
  714. scitex/scholar/metadata_engines/utils/_standardize_metadata.py +2 -3
  715. scitex/scholar/pdf_download/ScholarPDFDownloader.py +25 -37
  716. scitex/scholar/pdf_download/strategies/chrome_pdf_viewer.py +11 -19
  717. scitex/scholar/pdf_download/strategies/direct_download.py +5 -9
  718. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +3 -3
  719. scitex/scholar/pdf_download/strategies/manual_download_utils.py +6 -13
  720. scitex/scholar/pdf_download/strategies/open_access_download.py +49 -31
  721. scitex/scholar/pdf_download/strategies/response_body.py +8 -19
  722. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +9 -18
  723. scitex/scholar/pipelines/ScholarPipelineMetadataParallel.py +25 -26
  724. scitex/scholar/pipelines/ScholarPipelineMetadataSingle.py +62 -23
  725. scitex/scholar/pipelines/ScholarPipelineParallel.py +13 -30
  726. scitex/scholar/pipelines/ScholarPipelineSearchParallel.py +299 -220
  727. scitex/scholar/pipelines/ScholarPipelineSearchSingle.py +202 -165
  728. scitex/scholar/pipelines/ScholarPipelineSingle.py +25 -51
  729. scitex/scholar/pipelines/SearchQueryParser.py +55 -55
  730. scitex/scholar/search_engines/ScholarSearchEngine.py +31 -27
  731. scitex/scholar/search_engines/_BaseSearchEngine.py +20 -23
  732. scitex/scholar/search_engines/individual/ArXivSearchEngine.py +53 -35
  733. scitex/scholar/search_engines/individual/CrossRefSearchEngine.py +47 -40
  734. scitex/scholar/search_engines/individual/OpenAlexSearchEngine.py +55 -50
  735. scitex/scholar/search_engines/individual/PubMedSearchEngine.py +8 -10
  736. scitex/scholar/search_engines/individual/SemanticScholarSearchEngine.py +55 -49
  737. scitex/scholar/storage/BibTeXHandler.py +150 -95
  738. scitex/scholar/storage/PaperIO.py +3 -6
  739. scitex/scholar/storage/ScholarLibrary.py +70 -49
  740. scitex/scholar/storage/_DeduplicationManager.py +52 -25
  741. scitex/scholar/storage/_LibraryCacheManager.py +19 -46
  742. scitex/scholar/storage/_LibraryManager.py +65 -175
  743. scitex/scholar/url_finder/ScholarURLFinder.py +9 -25
  744. scitex/scholar/url_finder/strategies/find_pdf_urls_by_direct_links.py +1 -1
  745. scitex/scholar/url_finder/strategies/find_pdf_urls_by_href.py +6 -10
  746. scitex/scholar/url_finder/strategies/find_pdf_urls_by_navigation.py +4 -6
  747. scitex/scholar/url_finder/strategies/find_pdf_urls_by_publisher_patterns.py +8 -15
  748. scitex/scholar/url_finder/strategies/find_pdf_urls_by_zotero_translators.py +3 -3
  749. scitex/scholar/url_finder/strategies/find_supplementary_urls_by_href.py +3 -3
  750. scitex/scholar/url_finder/translators/core/patterns.py +6 -4
  751. scitex/scholar/url_finder/translators/core/registry.py +6 -9
  752. scitex/scholar/url_finder/translators/individual/BOFiP_Impots.py +60 -52
  753. scitex/scholar/url_finder/translators/individual/Baidu_Scholar.py +54 -62
  754. scitex/scholar/url_finder/translators/individual/Bangkok_Post.py +38 -44
  755. scitex/scholar/url_finder/translators/individual/Baruch_Foundation.py +43 -47
  756. scitex/scholar/url_finder/translators/individual/Beobachter.py +46 -50
  757. scitex/scholar/url_finder/translators/individual/Bezneng_Gajit.py +37 -41
  758. scitex/scholar/url_finder/translators/individual/BibLaTeX.py +59 -52
  759. scitex/scholar/url_finder/translators/individual/BibTeX.py +83 -79
  760. scitex/scholar/url_finder/translators/individual/Biblio_com.py +48 -51
  761. scitex/scholar/url_finder/translators/individual/Bibliontology_RDF.py +58 -56
  762. scitex/scholar/url_finder/translators/individual/Camara_Brasileira_do_Livro_ISBN.py +102 -99
  763. scitex/scholar/url_finder/translators/individual/CanLII.py +49 -43
  764. scitex/scholar/url_finder/translators/individual/Canada_com.py +36 -40
  765. scitex/scholar/url_finder/translators/individual/Canadian_Letters_and_Images.py +43 -43
  766. scitex/scholar/url_finder/translators/individual/Canadiana_ca.py +77 -66
  767. scitex/scholar/url_finder/translators/individual/Cascadilla_Proceedings_Project.py +68 -62
  768. scitex/scholar/url_finder/translators/individual/Central_and_Eastern_European_Online_Library_Journals.py +60 -60
  769. scitex/scholar/url_finder/translators/individual/Champlain_Society_Collection.py +63 -61
  770. scitex/scholar/url_finder/translators/individual/Chicago_Journal_of_Theoretical_Computer_Science.py +74 -58
  771. scitex/scholar/url_finder/translators/individual/Christian_Science_Monitor.py +32 -38
  772. scitex/scholar/url_finder/translators/individual/Columbia_University_Press.py +51 -47
  773. scitex/scholar/url_finder/translators/individual/Common_Place.py +66 -57
  774. scitex/scholar/url_finder/translators/individual/Cornell_LII.py +66 -62
  775. scitex/scholar/url_finder/translators/individual/Cornell_University_Press.py +38 -45
  776. scitex/scholar/url_finder/translators/individual/CourtListener.py +52 -56
  777. scitex/scholar/url_finder/translators/individual/DAI_Zenon.py +53 -54
  778. scitex/scholar/url_finder/translators/individual/access_medicine.py +27 -33
  779. scitex/scholar/url_finder/translators/individual/acm.py +1 -1
  780. scitex/scholar/url_finder/translators/individual/acm_digital_library.py +93 -63
  781. scitex/scholar/url_finder/translators/individual/airiti.py +3 -1
  782. scitex/scholar/url_finder/translators/individual/aosic.py +3 -1
  783. scitex/scholar/url_finder/translators/individual/archive_ouverte_aosic.py +3 -1
  784. scitex/scholar/url_finder/translators/individual/archive_ouverte_en_sciences_de_l_information_et_de_la_communication___aosic_.py +6 -2
  785. scitex/scholar/url_finder/translators/individual/artforum.py +35 -27
  786. scitex/scholar/url_finder/translators/individual/arxiv.py +1 -1
  787. scitex/scholar/url_finder/translators/individual/arxiv_org.py +8 -4
  788. scitex/scholar/url_finder/translators/individual/atlanta_journal_constitution.py +22 -18
  789. scitex/scholar/url_finder/translators/individual/atypon_journals.py +19 -11
  790. scitex/scholar/url_finder/translators/individual/austlii_and_nzlii.py +48 -44
  791. scitex/scholar/url_finder/translators/individual/australian_dictionary_of_biography.py +21 -17
  792. scitex/scholar/url_finder/translators/individual/bailii.py +22 -19
  793. scitex/scholar/url_finder/translators/individual/bbc.py +46 -42
  794. scitex/scholar/url_finder/translators/individual/bbc_genome.py +37 -25
  795. scitex/scholar/url_finder/translators/individual/biblioteca_nacional_de_maestros.py +24 -20
  796. scitex/scholar/url_finder/translators/individual/bibliotheque_archives_nationale_quebec_pistard.py +42 -43
  797. scitex/scholar/url_finder/translators/individual/bibliotheque_archives_nationales_quebec.py +87 -81
  798. scitex/scholar/url_finder/translators/individual/bibliotheque_nationale_france.py +39 -37
  799. scitex/scholar/url_finder/translators/individual/bibsys.py +32 -28
  800. scitex/scholar/url_finder/translators/individual/bioconductor.py +58 -52
  801. scitex/scholar/url_finder/translators/individual/biomed_central.py +23 -15
  802. scitex/scholar/url_finder/translators/individual/biorxiv.py +26 -13
  803. scitex/scholar/url_finder/translators/individual/blogger.py +39 -43
  804. scitex/scholar/url_finder/translators/individual/bloomberg.py +48 -52
  805. scitex/scholar/url_finder/translators/individual/bloomsbury_food_library.py +37 -37
  806. scitex/scholar/url_finder/translators/individual/bluesky.py +30 -28
  807. scitex/scholar/url_finder/translators/individual/bnf_isbn.py +1 -1
  808. scitex/scholar/url_finder/translators/individual/bocc.py +66 -60
  809. scitex/scholar/url_finder/translators/individual/boe.py +52 -52
  810. scitex/scholar/url_finder/translators/individual/brill.py +3 -1
  811. scitex/scholar/url_finder/translators/individual/business_standard.py +36 -38
  812. scitex/scholar/url_finder/translators/individual/cabi_cab_abstracts.py +39 -41
  813. scitex/scholar/url_finder/translators/individual/cambridge.py +3 -1
  814. scitex/scholar/url_finder/translators/individual/cambridge_core.py +30 -24
  815. scitex/scholar/url_finder/translators/individual/caod.py +50 -46
  816. scitex/scholar/url_finder/translators/individual/cbc.py +91 -67
  817. scitex/scholar/url_finder/translators/individual/ccfr_bnf.py +49 -53
  818. scitex/scholar/url_finder/translators/individual/cia_world_factbook.py +43 -33
  819. scitex/scholar/url_finder/translators/individual/crossref_rest.py +208 -174
  820. scitex/scholar/url_finder/translators/individual/current_affairs.py +29 -35
  821. scitex/scholar/url_finder/translators/individual/dabi.py +70 -66
  822. scitex/scholar/url_finder/translators/individual/dagens_nyheter.py +3 -1
  823. scitex/scholar/url_finder/translators/individual/dagstuhl.py +10 -15
  824. scitex/scholar/url_finder/translators/individual/dar_almandumah.py +13 -9
  825. scitex/scholar/url_finder/translators/individual/dart_europe.py +19 -22
  826. scitex/scholar/url_finder/translators/individual/data_gov.py +2 -2
  827. scitex/scholar/url_finder/translators/individual/databrary.py +27 -28
  828. scitex/scholar/url_finder/translators/individual/datacite_json.py +152 -137
  829. scitex/scholar/url_finder/translators/individual/dataverse.py +68 -64
  830. scitex/scholar/url_finder/translators/individual/daum_news.py +38 -38
  831. scitex/scholar/url_finder/translators/individual/dblp.py +4 -8
  832. scitex/scholar/url_finder/translators/individual/dblp_computer_science_bibliography.py +8 -3
  833. scitex/scholar/url_finder/translators/individual/dbpia.py +5 -3
  834. scitex/scholar/url_finder/translators/individual/defense_technical_information_center.py +30 -28
  835. scitex/scholar/url_finder/translators/individual/delpher.py +102 -79
  836. scitex/scholar/url_finder/translators/individual/demographic_research.py +35 -31
  837. scitex/scholar/url_finder/translators/individual/denik_cz.py +58 -54
  838. scitex/scholar/url_finder/translators/individual/depatisnet.py +7 -10
  839. scitex/scholar/url_finder/translators/individual/der_freitag.py +81 -66
  840. scitex/scholar/url_finder/translators/individual/der_spiegel.py +56 -54
  841. scitex/scholar/url_finder/translators/individual/digibib_net.py +3 -1
  842. scitex/scholar/url_finder/translators/individual/digizeitschriften.py +3 -1
  843. scitex/scholar/url_finder/translators/individual/dpla.py +13 -14
  844. scitex/scholar/url_finder/translators/individual/dspace.py +2 -2
  845. scitex/scholar/url_finder/translators/individual/ebrary.py +3 -1
  846. scitex/scholar/url_finder/translators/individual/ebscohost.py +3 -1
  847. scitex/scholar/url_finder/translators/individual/electronic_colloquium_on_computational_complexity.py +3 -1
  848. scitex/scholar/url_finder/translators/individual/elife.py +3 -1
  849. scitex/scholar/url_finder/translators/individual/elsevier_health_journals.py +3 -1
  850. scitex/scholar/url_finder/translators/individual/emerald.py +3 -1
  851. scitex/scholar/url_finder/translators/individual/emerald_insight.py +3 -1
  852. scitex/scholar/url_finder/translators/individual/epicurious.py +3 -1
  853. scitex/scholar/url_finder/translators/individual/eurogamerusgamer.py +3 -1
  854. scitex/scholar/url_finder/translators/individual/fachportal_padagogik.py +3 -1
  855. scitex/scholar/url_finder/translators/individual/frontiers.py +1 -1
  856. scitex/scholar/url_finder/translators/individual/gale_databases.py +3 -1
  857. scitex/scholar/url_finder/translators/individual/gms_german_medical_science.py +6 -2
  858. scitex/scholar/url_finder/translators/individual/ieee_computer_society.py +6 -2
  859. scitex/scholar/url_finder/translators/individual/ieee_xplore.py +41 -35
  860. scitex/scholar/url_finder/translators/individual/inter_research_science_center.py +6 -2
  861. scitex/scholar/url_finder/translators/individual/jisc_historical_texts.py +3 -1
  862. scitex/scholar/url_finder/translators/individual/jstor.py +14 -12
  863. scitex/scholar/url_finder/translators/individual/korean_national_library.py +3 -1
  864. scitex/scholar/url_finder/translators/individual/la_times.py +3 -1
  865. scitex/scholar/url_finder/translators/individual/landesbibliographie_baden_wurttemberg.py +3 -1
  866. scitex/scholar/url_finder/translators/individual/legislative_insight.py +3 -1
  867. scitex/scholar/url_finder/translators/individual/libraries_tasmania.py +3 -1
  868. scitex/scholar/url_finder/translators/individual/library_catalog__koha_.py +3 -1
  869. scitex/scholar/url_finder/translators/individual/lingbuzz.py +2 -2
  870. scitex/scholar/url_finder/translators/individual/max_planck_institute_for_the_history_of_science_virtual_laboratory_library.py +3 -1
  871. scitex/scholar/url_finder/translators/individual/mdpi.py +12 -6
  872. scitex/scholar/url_finder/translators/individual/microbiology_society_journals.py +3 -1
  873. scitex/scholar/url_finder/translators/individual/midas_journals.py +3 -1
  874. scitex/scholar/url_finder/translators/individual/nagoya_university_opac.py +3 -1
  875. scitex/scholar/url_finder/translators/individual/nature_publishing_group.py +32 -19
  876. scitex/scholar/url_finder/translators/individual/ntsb_accident_reports.py +3 -1
  877. scitex/scholar/url_finder/translators/individual/openedition_journals.py +8 -4
  878. scitex/scholar/url_finder/translators/individual/orcid.py +16 -15
  879. scitex/scholar/url_finder/translators/individual/oxford.py +25 -19
  880. scitex/scholar/url_finder/translators/individual/oxford_dictionaries_premium.py +3 -1
  881. scitex/scholar/url_finder/translators/individual/ozon_ru.py +3 -1
  882. scitex/scholar/url_finder/translators/individual/plos.py +9 -12
  883. scitex/scholar/url_finder/translators/individual/polygon.py +3 -1
  884. scitex/scholar/url_finder/translators/individual/primo.py +3 -1
  885. scitex/scholar/url_finder/translators/individual/project_muse.py +3 -1
  886. scitex/scholar/url_finder/translators/individual/pubfactory_journals.py +3 -1
  887. scitex/scholar/url_finder/translators/individual/pubmed.py +71 -65
  888. scitex/scholar/url_finder/translators/individual/pubmed_central.py +8 -6
  889. scitex/scholar/url_finder/translators/individual/rechtspraak_nl.py +3 -1
  890. scitex/scholar/url_finder/translators/individual/sage_journals.py +25 -17
  891. scitex/scholar/url_finder/translators/individual/sciencedirect.py +36 -17
  892. scitex/scholar/url_finder/translators/individual/semantics_visual_library.py +3 -1
  893. scitex/scholar/url_finder/translators/individual/silverchair.py +70 -52
  894. scitex/scholar/url_finder/translators/individual/sora.py +3 -1
  895. scitex/scholar/url_finder/translators/individual/springer.py +15 -11
  896. scitex/scholar/url_finder/translators/individual/ssrn.py +3 -3
  897. scitex/scholar/url_finder/translators/individual/stanford_encyclopedia_of_philosophy.py +3 -1
  898. scitex/scholar/url_finder/translators/individual/superlib.py +3 -1
  899. scitex/scholar/url_finder/translators/individual/treesearch.py +3 -1
  900. scitex/scholar/url_finder/translators/individual/university_of_chicago_press_books.py +3 -1
  901. scitex/scholar/url_finder/translators/individual/vlex.py +3 -1
  902. scitex/scholar/url_finder/translators/individual/web_of_science.py +3 -1
  903. scitex/scholar/url_finder/translators/individual/web_of_science_nextgen.py +3 -1
  904. scitex/scholar/url_finder/translators/individual/wiley.py +31 -25
  905. scitex/scholar/url_finder/translators/individual/wilson_center_digital_archive.py +3 -1
  906. scitex/scholar/utils/bibtex/_parse_bibtex.py +3 -3
  907. scitex/scholar/utils/cleanup/_cleanup_scholar_processes.py +5 -9
  908. scitex/scholar/utils/text/_TextNormalizer.py +249 -176
  909. scitex/scholar/utils/validation/DOIValidator.py +31 -28
  910. scitex/scholar/utils/validation/__init__.py +0 -0
  911. scitex/scholar/utils/validation/validate_library_dois.py +61 -57
  912. scitex/scholar/zotero/__init__.py +1 -1
  913. scitex/security/cli.py +7 -20
  914. scitex/security/github.py +45 -32
  915. scitex/session/__init__.py +8 -9
  916. scitex/session/_decorator.py +60 -41
  917. scitex/session/_lifecycle.py +39 -39
  918. scitex/session/_manager.py +24 -20
  919. scitex/sh/__init__.py +4 -3
  920. scitex/sh/_execute.py +10 -7
  921. scitex/sh/_security.py +3 -3
  922. scitex/sh/_types.py +2 -3
  923. scitex/stats/__init__.py +57 -6
  924. scitex/stats/_schema.py +42 -569
  925. scitex/stats/auto/__init__.py +188 -0
  926. scitex/stats/auto/_context.py +331 -0
  927. scitex/stats/auto/_formatting.py +679 -0
  928. scitex/stats/auto/_rules.py +901 -0
  929. scitex/stats/auto/_selector.py +554 -0
  930. scitex/stats/auto/_styles.py +721 -0
  931. scitex/stats/correct/__init__.py +4 -4
  932. scitex/stats/correct/_correct_bonferroni.py +43 -34
  933. scitex/stats/correct/_correct_fdr.py +14 -40
  934. scitex/stats/correct/_correct_fdr_.py +39 -46
  935. scitex/stats/correct/_correct_holm.py +14 -32
  936. scitex/stats/correct/_correct_sidak.py +36 -21
  937. scitex/stats/descriptive/_circular.py +20 -21
  938. scitex/stats/descriptive/_describe.py +19 -5
  939. scitex/stats/descriptive/_nan.py +5 -7
  940. scitex/stats/descriptive/_real.py +4 -3
  941. scitex/stats/effect_sizes/__init__.py +10 -11
  942. scitex/stats/effect_sizes/_cliffs_delta.py +35 -32
  943. scitex/stats/effect_sizes/_cohens_d.py +30 -31
  944. scitex/stats/effect_sizes/_epsilon_squared.py +19 -22
  945. scitex/stats/effect_sizes/_eta_squared.py +23 -27
  946. scitex/stats/effect_sizes/_prob_superiority.py +18 -21
  947. scitex/stats/posthoc/__init__.py +3 -3
  948. scitex/stats/posthoc/_dunnett.py +75 -55
  949. scitex/stats/posthoc/_games_howell.py +61 -43
  950. scitex/stats/posthoc/_tukey_hsd.py +42 -34
  951. scitex/stats/power/__init__.py +2 -2
  952. scitex/stats/power/_power.py +56 -56
  953. scitex/stats/tests/__init__.py +1 -1
  954. scitex/stats/tests/correlation/__init__.py +1 -1
  955. scitex/stats/tests/correlation/_test_pearson.py +28 -38
  956. scitex/stats/utils/__init__.py +14 -17
  957. scitex/stats/utils/_effect_size.py +85 -78
  958. scitex/stats/utils/_formatters.py +49 -43
  959. scitex/stats/utils/_normalizers.py +7 -14
  960. scitex/stats/utils/_power.py +56 -56
  961. scitex/str/__init__.py +1 -0
  962. scitex/str/_clean_path.py +3 -3
  963. scitex/str/_factor_out_digits.py +86 -58
  964. scitex/str/_format_plot_text.py +180 -111
  965. scitex/str/_latex.py +19 -19
  966. scitex/str/_latex_fallback.py +9 -10
  967. scitex/str/_parse.py +3 -6
  968. scitex/str/_print_debug.py +13 -13
  969. scitex/str/_printc.py +2 -0
  970. scitex/str/_search.py +3 -3
  971. scitex/template/.legacy/_clone_project.py +9 -13
  972. scitex/template/__init__.py +10 -2
  973. scitex/template/_clone_project.py +7 -2
  974. scitex/template/_copy.py +1 -0
  975. scitex/template/_customize.py +3 -6
  976. scitex/template/_git_strategy.py +2 -3
  977. scitex/template/_rename.py +1 -0
  978. scitex/template/clone_pip_project.py +6 -7
  979. scitex/template/clone_research.py +7 -10
  980. scitex/template/clone_singularity.py +6 -7
  981. scitex/template/clone_writer_directory.py +6 -7
  982. scitex/tex/_preview.py +26 -11
  983. scitex/tex/_to_vec.py +10 -7
  984. scitex/torch/__init__.py +11 -1
  985. scitex/types/_ArrayLike.py +2 -0
  986. scitex/types/_is_listed_X.py +3 -3
  987. scitex/units.py +110 -77
  988. scitex/utils/_compress_hdf5.py +3 -3
  989. scitex/utils/_email.py +8 -4
  990. scitex/utils/_notify.py +14 -8
  991. scitex/utils/_search.py +6 -6
  992. scitex/utils/_verify_scitex_format.py +17 -42
  993. scitex/utils/_verify_scitex_format_v01.py +12 -34
  994. scitex/utils/template.py +4 -3
  995. scitex/vis/README.md +246 -615
  996. scitex/vis/__init__.py +138 -78
  997. scitex/vis/backend/__init__.py +3 -3
  998. scitex/vis/backend/{export.py → _export.py} +1 -1
  999. scitex/vis/backend/{parser.py → _parser.py} +1 -3
  1000. scitex/vis/backend/{render.py → _render.py} +1 -1
  1001. scitex/vis/canvas.py +435 -0
  1002. scitex/vis/docs/CANVAS_ARCHITECTURE.md +307 -0
  1003. scitex/vis/editor/__init__.py +1 -1
  1004. scitex/vis/editor/_dearpygui_editor.py +1976 -0
  1005. scitex/vis/editor/_defaults.py +140 -110
  1006. scitex/vis/editor/_edit.py +90 -42
  1007. scitex/vis/editor/_flask_editor.py +37 -0
  1008. scitex/vis/editor/_mpl_editor.py +63 -48
  1009. scitex/vis/editor/_qt_editor.py +916 -0
  1010. scitex/vis/editor/_tkinter_editor.py +146 -89
  1011. scitex/vis/editor/flask_editor/__init__.py +21 -0
  1012. scitex/vis/editor/flask_editor/_bbox.py +529 -0
  1013. scitex/vis/editor/flask_editor/_core.py +168 -0
  1014. scitex/vis/editor/flask_editor/_plotter.py +567 -0
  1015. scitex/vis/editor/flask_editor/_renderer.py +393 -0
  1016. scitex/vis/editor/flask_editor/_utils.py +80 -0
  1017. scitex/vis/editor/flask_editor/templates/__init__.py +33 -0
  1018. scitex/vis/editor/flask_editor/templates/_html.py +513 -0
  1019. scitex/vis/editor/flask_editor/templates/_scripts.py +1261 -0
  1020. scitex/vis/editor/flask_editor/templates/_styles.py +739 -0
  1021. scitex/vis/io/__init__.py +84 -21
  1022. scitex/vis/io/_canvas.py +230 -0
  1023. scitex/vis/io/_data.py +208 -0
  1024. scitex/vis/io/_directory.py +205 -0
  1025. scitex/vis/io/_export.py +463 -0
  1026. scitex/vis/io/{load.py → _load.py} +1 -1
  1027. scitex/vis/io/_panel.py +432 -0
  1028. scitex/vis/io/{save.py → _save.py} +0 -0
  1029. scitex/vis/model/__init__.py +7 -7
  1030. scitex/vis/model/{annotations.py → _annotations.py} +2 -4
  1031. scitex/vis/model/{axes.py → _axes.py} +1 -1
  1032. scitex/vis/model/{figure.py → _figure.py} +0 -0
  1033. scitex/vis/model/{guides.py → _guides.py} +1 -1
  1034. scitex/vis/model/{plot.py → _plot.py} +2 -4
  1035. scitex/vis/model/{plot_types.py → _plot_types.py} +0 -0
  1036. scitex/vis/model/{styles.py → _styles.py} +0 -0
  1037. scitex/vis/utils/__init__.py +2 -2
  1038. scitex/vis/utils/{defaults.py → _defaults.py} +1 -2
  1039. scitex/vis/utils/{validate.py → _validate.py} +3 -9
  1040. scitex/web/__init__.py +7 -1
  1041. scitex/web/_scraping.py +54 -38
  1042. scitex/web/_search_pubmed.py +30 -14
  1043. scitex/writer/.legacy/Writer_v01-refactored.py +4 -4
  1044. scitex/writer/.legacy/_compile.py +18 -28
  1045. scitex/writer/Writer.py +8 -21
  1046. scitex/writer/__init__.py +11 -11
  1047. scitex/writer/_clone_writer_project.py +2 -6
  1048. scitex/writer/_compile/__init__.py +1 -0
  1049. scitex/writer/_compile/_parser.py +1 -0
  1050. scitex/writer/_compile/_runner.py +35 -38
  1051. scitex/writer/_compile/_validator.py +1 -0
  1052. scitex/writer/_compile/manuscript.py +1 -0
  1053. scitex/writer/_compile/revision.py +1 -0
  1054. scitex/writer/_compile/supplementary.py +1 -0
  1055. scitex/writer/_compile_async.py +5 -12
  1056. scitex/writer/_project/__init__.py +1 -0
  1057. scitex/writer/_project/_create.py +10 -25
  1058. scitex/writer/_project/_trees.py +4 -9
  1059. scitex/writer/_project/_validate.py +2 -3
  1060. scitex/writer/_validate_tree_structures.py +7 -18
  1061. scitex/writer/dataclasses/__init__.py +8 -10
  1062. scitex/writer/dataclasses/config/_CONSTANTS.py +2 -3
  1063. scitex/writer/dataclasses/config/_WriterConfig.py +4 -9
  1064. scitex/writer/dataclasses/contents/_ManuscriptContents.py +14 -25
  1065. scitex/writer/dataclasses/contents/_RevisionContents.py +21 -16
  1066. scitex/writer/dataclasses/contents/_SupplementaryContents.py +21 -24
  1067. scitex/writer/dataclasses/core/_Document.py +2 -3
  1068. scitex/writer/dataclasses/core/_DocumentSection.py +8 -23
  1069. scitex/writer/dataclasses/results/_CompilationResult.py +2 -3
  1070. scitex/writer/dataclasses/results/_LaTeXIssue.py +3 -6
  1071. scitex/writer/dataclasses/results/_SaveSectionsResponse.py +20 -9
  1072. scitex/writer/dataclasses/results/_SectionReadResponse.py +24 -10
  1073. scitex/writer/dataclasses/tree/_ConfigTree.py +7 -4
  1074. scitex/writer/dataclasses/tree/_ManuscriptTree.py +10 -13
  1075. scitex/writer/dataclasses/tree/_RevisionTree.py +16 -17
  1076. scitex/writer/dataclasses/tree/_ScriptsTree.py +10 -5
  1077. scitex/writer/dataclasses/tree/_SharedTree.py +10 -13
  1078. scitex/writer/dataclasses/tree/_SupplementaryTree.py +15 -14
  1079. scitex/writer/utils/.legacy_git_retry.py +3 -8
  1080. scitex/writer/utils/_parse_latex_logs.py +2 -3
  1081. scitex/writer/utils/_parse_script_args.py +20 -23
  1082. scitex/writer/utils/_watch.py +5 -5
  1083. {scitex-2.4.3.dist-info → scitex-2.7.0.dist-info}/METADATA +12 -11
  1084. {scitex-2.4.3.dist-info → scitex-2.7.0.dist-info}/RECORD +1075 -958
  1085. scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin_v01-indentation-issues.py +0 -583
  1086. scitex/plt/_subplots/_export_as_csv_formatters.py +0 -112
  1087. scitex/vis/DJANGO_INTEGRATION.md +0 -677
  1088. scitex/vis/editor/_web_editor.py +0 -1440
  1089. scitex/vis/tmp.txt +0 -239
  1090. {scitex-2.4.3.dist-info → scitex-2.7.0.dist-info}/WHEEL +0 -0
  1091. {scitex-2.4.3.dist-info → scitex-2.7.0.dist-info}/entry_points.txt +0 -0
  1092. {scitex-2.4.3.dist-info → scitex-2.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,1440 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # File: ./src/scitex/vis/editor/_web_editor.py
4
- """Web-based figure editor using Flask."""
5
-
6
- from pathlib import Path
7
- from typing import Dict, Any, Optional
8
- import copy
9
- import json
10
- import io
11
- import base64
12
- import webbrowser
13
- import threading
14
-
15
-
16
- def _find_available_port(start_port: int = 5050, max_attempts: int = 10) -> int:
17
- """Find an available port, starting from start_port."""
18
- import socket
19
-
20
- for offset in range(max_attempts):
21
- port = start_port + offset
22
- try:
23
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
24
- s.bind(('127.0.0.1', port))
25
- return port
26
- except OSError:
27
- continue
28
-
29
- raise RuntimeError(f"Could not find available port in range {start_port}-{start_port + max_attempts}")
30
-
31
-
32
- def _kill_process_on_port(port: int) -> bool:
33
- """Try to kill process using the specified port. Returns True if successful."""
34
- import subprocess
35
- import sys
36
-
37
- try:
38
- if sys.platform == 'win32':
39
- # Windows: netstat + taskkill
40
- result = subprocess.run(
41
- f'netstat -ano | findstr :{port}',
42
- shell=True, capture_output=True, text=True
43
- )
44
- if result.stdout:
45
- for line in result.stdout.strip().split('\n'):
46
- parts = line.split()
47
- if len(parts) >= 5:
48
- pid = parts[-1]
49
- subprocess.run(f'taskkill /F /PID {pid}', shell=True, capture_output=True)
50
- return True
51
- else:
52
- # Linux/Mac: fuser or lsof
53
- result = subprocess.run(
54
- ['fuser', '-k', f'{port}/tcp'],
55
- capture_output=True, text=True
56
- )
57
- if result.returncode == 0:
58
- return True
59
-
60
- # Fallback to lsof
61
- result = subprocess.run(
62
- ['lsof', '-t', f'-i:{port}'],
63
- capture_output=True, text=True
64
- )
65
- if result.stdout:
66
- for pid in result.stdout.strip().split('\n'):
67
- if pid:
68
- subprocess.run(['kill', '-9', pid], capture_output=True)
69
- return True
70
- except Exception:
71
- pass
72
-
73
- return False
74
-
75
-
76
- class WebEditor:
77
- """
78
- Browser-based figure editor using Flask.
79
-
80
- Features:
81
- - Modern responsive UI
82
- - Real-time preview via WebSocket or polling
83
- - Property editors with sliders and color pickers
84
- - Save to .manual.json
85
- - SciTeX style defaults pre-filled
86
- - Auto-finds available port if default is in use
87
- """
88
-
89
- def __init__(
90
- self,
91
- json_path: Path,
92
- metadata: Dict[str, Any],
93
- csv_data: Optional[Any] = None,
94
- png_path: Optional[Path] = None,
95
- manual_overrides: Optional[Dict[str, Any]] = None,
96
- port: int = 5050,
97
- ):
98
- self.json_path = Path(json_path)
99
- self.metadata = metadata
100
- self.csv_data = csv_data
101
- self.png_path = Path(png_path) if png_path else None
102
- self.manual_overrides = manual_overrides or {}
103
- self._requested_port = port
104
- self.port = port # Will be updated in run() if needed
105
-
106
- # Get SciTeX defaults and merge with metadata
107
- from ._defaults import get_scitex_defaults, extract_defaults_from_metadata
108
- self.scitex_defaults = get_scitex_defaults()
109
- self.metadata_defaults = extract_defaults_from_metadata(metadata)
110
-
111
- # Start with defaults, then overlay manual overrides
112
- self.current_overrides = copy.deepcopy(self.scitex_defaults)
113
- self.current_overrides.update(self.metadata_defaults)
114
- self.current_overrides.update(self.manual_overrides)
115
-
116
- # Track initial state to detect modifications
117
- self._initial_overrides = copy.deepcopy(self.current_overrides)
118
- self._user_modified = False # Set to True when user makes changes
119
-
120
- def run(self):
121
- """Launch the web editor."""
122
- try:
123
- from flask import Flask, render_template_string, request, jsonify
124
- except ImportError:
125
- raise ImportError("Flask is required for web editor. Install: pip install flask")
126
-
127
- # Handle port conflicts: try to find available port or kill existing process
128
- import socket
129
- try:
130
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
131
- s.bind(('127.0.0.1', self._requested_port))
132
- self.port = self._requested_port
133
- except OSError:
134
- # Port in use - try to kill existing process first
135
- print(f"Port {self._requested_port} is in use. Attempting to free it...")
136
- if _kill_process_on_port(self._requested_port):
137
- import time
138
- time.sleep(0.5) # Give process time to release port
139
- self.port = self._requested_port
140
- print(f"Successfully freed port {self.port}")
141
- else:
142
- # Find an alternative port
143
- self.port = _find_available_port(self._requested_port + 1)
144
- print(f"Using alternative port: {self.port}")
145
-
146
- app = Flask(__name__)
147
-
148
- # Store reference to self for routes
149
- editor = self
150
-
151
- @app.route('/')
152
- def index():
153
- return render_template_string(HTML_TEMPLATE,
154
- filename=editor.json_path.name,
155
- overrides=json.dumps(editor.current_overrides))
156
-
157
- @app.route('/preview')
158
- def preview():
159
- """Generate figure preview as base64 PNG."""
160
- img_data = editor._render_preview()
161
- return jsonify({'image': img_data})
162
-
163
- @app.route('/update', methods=['POST'])
164
- def update():
165
- """Update overrides and return new preview."""
166
- data = request.json
167
- editor.current_overrides.update(data.get('overrides', {}))
168
- editor._user_modified = True # Mark as modified to regenerate from CSV
169
- img_data = editor._render_preview()
170
- return jsonify({'image': img_data, 'status': 'updated'})
171
-
172
- @app.route('/save', methods=['POST'])
173
- def save():
174
- """Save to .manual.json."""
175
- from ._edit import save_manual_overrides
176
- try:
177
- manual_path = save_manual_overrides(editor.json_path, editor.current_overrides)
178
- return jsonify({'status': 'saved', 'path': str(manual_path)})
179
- except Exception as e:
180
- return jsonify({'status': 'error', 'message': str(e)}), 500
181
-
182
- @app.route('/shutdown', methods=['POST'])
183
- def shutdown():
184
- """Shutdown the server."""
185
- func = request.environ.get('werkzeug.server.shutdown')
186
- if func is None:
187
- raise RuntimeError('Not running with Werkzeug Server')
188
- func()
189
- return jsonify({'status': 'shutdown'})
190
-
191
- # Open browser after short delay
192
- def open_browser():
193
- import time
194
- time.sleep(0.5)
195
- webbrowser.open(f'http://127.0.0.1:{self.port}')
196
-
197
- threading.Thread(target=open_browser, daemon=True).start()
198
-
199
- print(f"Starting SciTeX Editor at http://127.0.0.1:{self.port}")
200
- print("Press Ctrl+C to stop")
201
-
202
- app.run(host='127.0.0.1', port=self.port, debug=False, use_reloader=False)
203
-
204
- def _has_modifications(self) -> bool:
205
- """Check if user has made any modifications to the figure."""
206
- return self._user_modified
207
-
208
- def _render_preview(self) -> str:
209
- """Render figure and return as base64 PNG.
210
-
211
- If original PNG exists and no overrides have been applied, return the original.
212
- Otherwise, regenerate from CSV with applied overrides using exact metadata.
213
- """
214
- # If PNG exists and this is initial load (no modifications), show original
215
- if self.png_path and self.png_path.exists() and not self._has_modifications():
216
- with open(self.png_path, 'rb') as f:
217
- return base64.b64encode(f.read()).decode('utf-8')
218
-
219
- import matplotlib
220
- matplotlib.use('Agg')
221
- import matplotlib.pyplot as plt
222
- from matplotlib.ticker import MaxNLocator
223
-
224
- # mm to pt conversion
225
- mm_to_pt = 2.83465
226
-
227
- # Get values from overrides (which includes metadata defaults)
228
- o = self.current_overrides
229
-
230
- # Dimensions
231
- dpi = o.get('dpi', 300)
232
- fig_size = o.get('fig_size', [3.15, 2.68])
233
-
234
- # Font sizes
235
- axis_fontsize = o.get('axis_fontsize', 7)
236
- tick_fontsize = o.get('tick_fontsize', 7)
237
- title_fontsize = o.get('title_fontsize', 8)
238
- legend_fontsize = o.get('legend_fontsize', 6)
239
-
240
- # Line/axis thickness (convert mm to pt)
241
- linewidth_pt = o.get('linewidth', 0.57) # Already in pt or convert
242
- axis_width_pt = o.get('axis_width', 0.2) * mm_to_pt
243
- tick_length_pt = o.get('tick_length', 0.8) * mm_to_pt
244
- tick_width_pt = o.get('tick_width', 0.2) * mm_to_pt
245
- tick_direction = o.get('tick_direction', 'out')
246
- n_ticks = o.get('n_ticks', 4)
247
-
248
- # Transparent background
249
- transparent = o.get('transparent', True)
250
-
251
- # Create figure with dimensions from overrides
252
- fig, ax = plt.subplots(figsize=fig_size, dpi=dpi)
253
- if transparent:
254
- fig.patch.set_facecolor('none')
255
- ax.patch.set_facecolor('none')
256
- elif o.get('facecolor'):
257
- fig.patch.set_facecolor(o['facecolor'])
258
- ax.patch.set_facecolor(o['facecolor'])
259
-
260
- # Plot from CSV data
261
- if self.csv_data is not None:
262
- self._plot_from_csv(ax, linewidth=linewidth_pt)
263
- else:
264
- ax.text(0.5, 0.5, "No plot data available\n(CSV not found)",
265
- ha='center', va='center', transform=ax.transAxes,
266
- fontsize=axis_fontsize)
267
-
268
- # Apply labels
269
- if o.get('title'):
270
- ax.set_title(o['title'], fontsize=title_fontsize)
271
- if o.get('xlabel'):
272
- ax.set_xlabel(o['xlabel'], fontsize=axis_fontsize)
273
- if o.get('ylabel'):
274
- ax.set_ylabel(o['ylabel'], fontsize=axis_fontsize)
275
-
276
- # Tick styling
277
- ax.tick_params(
278
- axis='both',
279
- labelsize=tick_fontsize,
280
- length=tick_length_pt,
281
- width=tick_width_pt,
282
- direction=tick_direction,
283
- )
284
-
285
- # Number of ticks
286
- ax.xaxis.set_major_locator(MaxNLocator(nbins=n_ticks))
287
- ax.yaxis.set_major_locator(MaxNLocator(nbins=n_ticks))
288
-
289
- # Grid
290
- if o.get('grid'):
291
- ax.grid(True, linewidth=axis_width_pt, alpha=0.3)
292
-
293
- # Axis limits
294
- if o.get('xlim'):
295
- ax.set_xlim(o['xlim'])
296
- if o.get('ylim'):
297
- ax.set_ylim(o['ylim'])
298
-
299
- # Spines visibility
300
- if o.get('hide_top_spine', True):
301
- ax.spines['top'].set_visible(False)
302
- if o.get('hide_right_spine', True):
303
- ax.spines['right'].set_visible(False)
304
-
305
- # Spine line width
306
- for spine in ax.spines.values():
307
- spine.set_linewidth(axis_width_pt)
308
-
309
- # Apply annotations
310
- for annot in o.get('annotations', []):
311
- if annot.get('type') == 'text':
312
- ax.text(
313
- annot.get('x', 0.5),
314
- annot.get('y', 0.5),
315
- annot.get('text', ''),
316
- transform=ax.transAxes,
317
- fontsize=annot.get('fontsize', axis_fontsize),
318
- )
319
-
320
- fig.tight_layout()
321
-
322
- # Convert to base64
323
- buf = io.BytesIO()
324
- fig.savefig(buf, format='png', dpi=dpi, bbox_inches='tight', transparent=transparent)
325
- buf.seek(0)
326
- img_data = base64.b64encode(buf.read()).decode('utf-8')
327
- plt.close(fig)
328
-
329
- return img_data
330
-
331
- def _plot_from_csv(self, ax, linewidth=1.0):
332
- """Reconstruct plot from CSV data using trace info from overrides."""
333
- import pandas as pd
334
-
335
- if not isinstance(self.csv_data, pd.DataFrame):
336
- return
337
-
338
- df = self.csv_data
339
- o = self.current_overrides
340
-
341
- # Get legend settings from overrides
342
- legend_fontsize = o.get('legend_fontsize', 6)
343
- legend_visible = o.get('legend_visible', True)
344
- legend_frameon = o.get('legend_frameon', False)
345
- legend_loc = o.get('legend_loc', 'best')
346
-
347
- # Get traces from overrides (which may have been edited by user)
348
- traces = o.get('traces', [])
349
-
350
- if traces:
351
- # Use trace information to reconstruct plot correctly
352
- for trace in traces:
353
- csv_cols = trace.get('csv_columns', {})
354
- x_col = csv_cols.get('x')
355
- y_col = csv_cols.get('y')
356
-
357
- if x_col in df.columns and y_col in df.columns:
358
- ax.plot(
359
- df[x_col],
360
- df[y_col],
361
- label=trace.get('label', trace.get('id', '')),
362
- color=trace.get('color'),
363
- linestyle=trace.get('linestyle', '-'),
364
- linewidth=trace.get('linewidth', linewidth),
365
- marker=trace.get('marker', None),
366
- markersize=trace.get('markersize', 6),
367
- )
368
-
369
- # Add legend if there are labeled traces
370
- if legend_visible and any(t.get('label') for t in traces):
371
- ax.legend(
372
- fontsize=legend_fontsize,
373
- frameon=legend_frameon,
374
- loc=legend_loc,
375
- )
376
- else:
377
- # Fallback: smart parsing of CSV column names
378
- # Format: ax_00_{id}_plot_x, ax_00_{id}_plot_y
379
- cols = df.columns.tolist()
380
-
381
- # Group columns by trace ID
382
- trace_groups = {}
383
- for col in cols:
384
- if col.endswith('_x'):
385
- trace_id = col[:-2] # Remove '_x'
386
- y_col = trace_id + '_y'
387
- if y_col in cols:
388
- # Extract label from column name (e.g., ax_00_sine_plot -> sine)
389
- parts = trace_id.split('_')
390
- label = parts[2] if len(parts) > 2 else trace_id
391
- trace_groups[trace_id] = {
392
- 'x_col': col,
393
- 'y_col': y_col,
394
- 'label': label,
395
- }
396
-
397
- if trace_groups:
398
- for trace_id, info in trace_groups.items():
399
- ax.plot(
400
- df[info['x_col']],
401
- df[info['y_col']],
402
- label=info['label'],
403
- linewidth=linewidth,
404
- )
405
- if legend_visible:
406
- ax.legend(fontsize=legend_fontsize, frameon=legend_frameon, loc=legend_loc)
407
- elif len(cols) >= 2:
408
- # Last resort: assume first column is x, rest are y
409
- x_col = cols[0]
410
- for y_col in cols[1:]:
411
- try:
412
- ax.plot(df[x_col], df[y_col], label=str(y_col), linewidth=linewidth)
413
- except Exception:
414
- pass
415
- if len(cols) > 2 and legend_visible:
416
- ax.legend(fontsize=legend_fontsize, frameon=legend_frameon, loc=legend_loc)
417
-
418
-
419
- # HTML template for web editor with light/dark mode based on scitex-cloud
420
- HTML_TEMPLATE = '''
421
- <!DOCTYPE html>
422
- <html lang="en" data-theme="dark">
423
- <head>
424
- <meta charset="UTF-8">
425
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
426
- <title>SciTeX Editor - {{ filename }}</title>
427
- <style>
428
- /* =============================================================================
429
- * SciTeX Color System - Based on scitex-cloud/static/shared/css
430
- * ============================================================================= */
431
- :root, [data-theme="light"] {
432
- /* Brand colors (light mode) */
433
- --scitex-01: #1a2a40;
434
- --scitex-02: #34495e;
435
- --scitex-03: #506b7a;
436
- --scitex-04: #6c8ba0;
437
- --scitex-05: #8fa4b0;
438
- --scitex-06: #b5c7d1;
439
- --scitex-07: #d4e1e8;
440
- --white: #fafbfc;
441
- --gray-subtle: #f6f8fa;
442
-
443
- /* Semantic tokens */
444
- --text-primary: var(--scitex-01);
445
- --text-secondary: var(--scitex-02);
446
- --text-muted: var(--scitex-04);
447
- --text-inverse: var(--white);
448
-
449
- --bg-page: #fefefe;
450
- --bg-surface: var(--white);
451
- --bg-muted: var(--gray-subtle);
452
-
453
- --border-default: var(--scitex-05);
454
- --border-muted: var(--scitex-06);
455
-
456
- /* Workspace colors */
457
- --workspace-bg-primary: #f8f9fa;
458
- --workspace-bg-secondary: #f3f4f6;
459
- --workspace-bg-tertiary: #ebedef;
460
- --workspace-bg-elevated: #ffffff;
461
- --workspace-border-subtle: #e0e4e8;
462
- --workspace-border-default: #b5c7d1;
463
-
464
- /* Status */
465
- --status-success: #4a9b7e;
466
- --status-warning: #b8956a;
467
- --status-error: #a67373;
468
-
469
- /* CTA */
470
- --color-cta: #3b82f6;
471
- --color-cta-hover: #2563eb;
472
-
473
- /* Preview background (checkered for transparency) */
474
- --preview-bg: linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
475
- linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
476
- linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
477
- linear-gradient(-45deg, transparent 75%, #e0e0e0 75%);
478
- }
479
-
480
- [data-theme="dark"] {
481
- /* Semantic tokens (dark mode) */
482
- --text-primary: var(--scitex-07);
483
- --text-secondary: var(--scitex-05);
484
- --text-muted: var(--scitex-04);
485
- --text-inverse: var(--scitex-01);
486
-
487
- --bg-page: #0f1419;
488
- --bg-surface: var(--scitex-01);
489
- --bg-muted: var(--scitex-02);
490
-
491
- --border-default: var(--scitex-03);
492
- --border-muted: var(--scitex-02);
493
-
494
- /* Workspace colors */
495
- --workspace-bg-primary: #0d0d0d;
496
- --workspace-bg-secondary: #151515;
497
- --workspace-bg-tertiary: #1a1a1a;
498
- --workspace-bg-elevated: #1f1f1f;
499
- --workspace-border-subtle: #1a1a1a;
500
- --workspace-border-default: #3a3a3a;
501
-
502
- /* Status */
503
- --status-success: #6ba89a;
504
- --status-warning: #d4a87a;
505
- --status-error: #c08888;
506
-
507
- /* Preview background (darker checkered) */
508
- --preview-bg: linear-gradient(45deg, #2a2a2a 25%, transparent 25%),
509
- linear-gradient(-45deg, #2a2a2a 25%, transparent 25%),
510
- linear-gradient(45deg, transparent 75%, #2a2a2a 75%),
511
- linear-gradient(-45deg, transparent 75%, #2a2a2a 75%);
512
- }
513
-
514
- /* =============================================================================
515
- * Base Styles
516
- * ============================================================================= */
517
- * { box-sizing: border-box; margin: 0; padding: 0; }
518
-
519
- body {
520
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
521
- background: var(--workspace-bg-primary);
522
- color: var(--text-primary);
523
- transition: background 0.3s, color 0.3s;
524
- }
525
-
526
- .container { display: flex; height: 100vh; }
527
-
528
- /* =============================================================================
529
- * Preview Panel
530
- * ============================================================================= */
531
- .preview {
532
- flex: 2;
533
- padding: 20px;
534
- display: flex;
535
- align-items: center;
536
- justify-content: center;
537
- background: var(--workspace-bg-secondary);
538
- }
539
-
540
- .preview-wrapper {
541
- background: var(--preview-bg);
542
- background-size: 20px 20px;
543
- background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
544
- border-radius: 8px;
545
- padding: 10px;
546
- box-shadow: 0 4px 20px rgba(0,0,0,0.15);
547
- }
548
-
549
- .preview img {
550
- max-width: 100%;
551
- max-height: calc(100vh - 80px);
552
- display: block;
553
- }
554
-
555
- /* =============================================================================
556
- * Controls Panel
557
- * ============================================================================= */
558
- .controls {
559
- flex: 1;
560
- min-width: 320px;
561
- max-width: 420px;
562
- background: var(--workspace-bg-elevated);
563
- border-left: 1px solid var(--workspace-border-default);
564
- overflow-y: auto;
565
- display: flex;
566
- flex-direction: column;
567
- }
568
-
569
- .controls-header {
570
- padding: 16px 20px;
571
- border-bottom: 1px solid var(--workspace-border-subtle);
572
- display: flex;
573
- justify-content: space-between;
574
- align-items: center;
575
- background: var(--bg-surface);
576
- position: sticky;
577
- top: 0;
578
- z-index: 10;
579
- }
580
-
581
- .controls-header h1 {
582
- font-size: 1.1em;
583
- font-weight: 600;
584
- color: var(--status-success);
585
- }
586
-
587
- .controls-body {
588
- padding: 0 20px 20px;
589
- flex: 1;
590
- }
591
-
592
- .filename {
593
- font-size: 0.8em;
594
- color: var(--text-muted);
595
- margin-top: 4px;
596
- word-break: break-all;
597
- }
598
-
599
- /* Theme toggle */
600
- .theme-toggle {
601
- background: transparent;
602
- border: 1px solid var(--border-muted);
603
- color: var(--text-secondary);
604
- cursor: pointer;
605
- font-size: 16px;
606
- padding: 6px 10px;
607
- border-radius: 6px;
608
- transition: all 0.2s;
609
- display: flex;
610
- align-items: center;
611
- gap: 6px;
612
- }
613
-
614
- .theme-toggle:hover {
615
- background: var(--bg-muted);
616
- border-color: var(--border-default);
617
- }
618
-
619
- /* =============================================================================
620
- * Section Headers
621
- * ============================================================================= */
622
- .section {
623
- margin-top: 16px;
624
- }
625
-
626
- .section-header {
627
- font-size: 0.75em;
628
- font-weight: 600;
629
- text-transform: uppercase;
630
- letter-spacing: 0.5px;
631
- color: var(--text-inverse);
632
- background: var(--status-success);
633
- padding: 8px 12px;
634
- border-radius: 4px;
635
- margin-bottom: 12px;
636
- }
637
-
638
- /* =============================================================================
639
- * Form Fields
640
- * ============================================================================= */
641
- .field { margin-bottom: 12px; }
642
-
643
- .field label {
644
- display: block;
645
- font-size: 0.8em;
646
- font-weight: 500;
647
- margin-bottom: 4px;
648
- color: var(--text-secondary);
649
- }
650
-
651
- .field input[type="text"],
652
- .field input[type="number"],
653
- .field select {
654
- width: 100%;
655
- padding: 8px 10px;
656
- border: 1px solid var(--border-muted);
657
- border-radius: 4px;
658
- background: var(--bg-surface);
659
- color: var(--text-primary);
660
- font-size: 0.85em;
661
- transition: border-color 0.2s;
662
- }
663
-
664
- .field input:focus,
665
- .field select:focus {
666
- outline: none;
667
- border-color: var(--status-success);
668
- }
669
-
670
- .field input[type="color"] {
671
- width: 40px;
672
- height: 32px;
673
- padding: 2px;
674
- border: 1px solid var(--border-muted);
675
- border-radius: 4px;
676
- cursor: pointer;
677
- background: var(--bg-surface);
678
- }
679
-
680
- .field-row {
681
- display: flex;
682
- gap: 10px;
683
- }
684
-
685
- .field-row .field { flex: 1; }
686
-
687
- /* Checkbox styling */
688
- .checkbox-field {
689
- display: flex;
690
- align-items: center;
691
- gap: 8px;
692
- cursor: pointer;
693
- padding: 6px 0;
694
- }
695
-
696
- .checkbox-field input[type="checkbox"] {
697
- width: 16px;
698
- height: 16px;
699
- accent-color: var(--status-success);
700
- }
701
-
702
- .checkbox-field span {
703
- font-size: 0.85em;
704
- color: var(--text-primary);
705
- }
706
-
707
- /* Color field with input */
708
- .color-field {
709
- display: flex;
710
- align-items: center;
711
- gap: 8px;
712
- }
713
-
714
- .color-field input[type="text"] {
715
- flex: 1;
716
- }
717
-
718
- /* =============================================================================
719
- * Traces Section
720
- * ============================================================================= */
721
- .traces-list {
722
- max-height: 200px;
723
- overflow-y: auto;
724
- border: 1px solid var(--border-muted);
725
- border-radius: 4px;
726
- background: var(--bg-muted);
727
- }
728
-
729
- .trace-item {
730
- display: flex;
731
- align-items: center;
732
- gap: 8px;
733
- padding: 8px 10px;
734
- border-bottom: 1px solid var(--border-muted);
735
- font-size: 0.85em;
736
- }
737
-
738
- .trace-item:last-child { border-bottom: none; }
739
-
740
- .trace-color {
741
- width: 24px;
742
- height: 24px;
743
- border-radius: 4px;
744
- border: 1px solid var(--border-default);
745
- cursor: pointer;
746
- }
747
-
748
- .trace-label {
749
- flex: 1;
750
- color: var(--text-primary);
751
- }
752
-
753
- .trace-style select {
754
- padding: 4px 6px;
755
- font-size: 0.8em;
756
- border: 1px solid var(--border-muted);
757
- border-radius: 3px;
758
- background: var(--bg-surface);
759
- color: var(--text-primary);
760
- }
761
-
762
- /* =============================================================================
763
- * Annotations
764
- * ============================================================================= */
765
- .annotations-list {
766
- margin-top: 10px;
767
- max-height: 120px;
768
- overflow-y: auto;
769
- }
770
-
771
- .annotation-item {
772
- display: flex;
773
- justify-content: space-between;
774
- align-items: center;
775
- padding: 6px 10px;
776
- background: var(--bg-muted);
777
- border-radius: 4px;
778
- margin-bottom: 5px;
779
- font-size: 0.85em;
780
- }
781
-
782
- .annotation-item span { color: var(--text-primary); }
783
-
784
- .annotation-item button {
785
- padding: 3px 8px;
786
- font-size: 0.75em;
787
- background: var(--status-error);
788
- border: none;
789
- border-radius: 3px;
790
- color: white;
791
- cursor: pointer;
792
- }
793
-
794
- /* =============================================================================
795
- * Buttons
796
- * ============================================================================= */
797
- .btn {
798
- width: 100%;
799
- padding: 10px 16px;
800
- margin-top: 8px;
801
- border: none;
802
- border-radius: 4px;
803
- cursor: pointer;
804
- font-size: 0.9em;
805
- font-weight: 500;
806
- transition: all 0.2s;
807
- }
808
-
809
- .btn-primary {
810
- background: var(--status-success);
811
- color: white;
812
- }
813
-
814
- .btn-primary:hover {
815
- filter: brightness(1.1);
816
- }
817
-
818
- .btn-secondary {
819
- background: var(--bg-muted);
820
- color: var(--text-primary);
821
- border: 1px solid var(--border-muted);
822
- }
823
-
824
- .btn-secondary:hover {
825
- background: var(--workspace-bg-tertiary);
826
- }
827
-
828
- .btn-cta {
829
- background: var(--color-cta);
830
- color: white;
831
- }
832
-
833
- .btn-cta:hover {
834
- background: var(--color-cta-hover);
835
- }
836
-
837
- /* =============================================================================
838
- * Status Bar
839
- * ============================================================================= */
840
- .status-bar {
841
- margin-top: 16px;
842
- padding: 10px 12px;
843
- border-radius: 4px;
844
- background: var(--bg-muted);
845
- font-size: 0.8em;
846
- color: var(--text-secondary);
847
- border-left: 3px solid var(--status-success);
848
- }
849
-
850
- .status-bar.error {
851
- border-left-color: var(--status-error);
852
- }
853
-
854
- /* =============================================================================
855
- * Collapsible Sections
856
- * ============================================================================= */
857
- .section-toggle {
858
- cursor: pointer;
859
- display: flex;
860
- align-items: center;
861
- gap: 8px;
862
- }
863
-
864
- .section-toggle::before {
865
- content: "\\25BC";
866
- font-size: 0.7em;
867
- transition: transform 0.2s;
868
- }
869
-
870
- .section-toggle.collapsed::before {
871
- transform: rotate(-90deg);
872
- }
873
-
874
- .section-content {
875
- overflow: hidden;
876
- transition: max-height 0.3s ease;
877
- }
878
-
879
- .section-content.collapsed {
880
- max-height: 0 !important;
881
- }
882
-
883
- /* =============================================================================
884
- * Scrollbar Styling
885
- * ============================================================================= */
886
- .controls::-webkit-scrollbar,
887
- .traces-list::-webkit-scrollbar,
888
- .annotations-list::-webkit-scrollbar {
889
- width: 6px;
890
- }
891
-
892
- .controls::-webkit-scrollbar-track,
893
- .traces-list::-webkit-scrollbar-track,
894
- .annotations-list::-webkit-scrollbar-track {
895
- background: var(--bg-muted);
896
- }
897
-
898
- .controls::-webkit-scrollbar-thumb,
899
- .traces-list::-webkit-scrollbar-thumb,
900
- .annotations-list::-webkit-scrollbar-thumb {
901
- background: var(--border-default);
902
- border-radius: 3px;
903
- }
904
-
905
- .controls::-webkit-scrollbar-thumb:hover,
906
- .traces-list::-webkit-scrollbar-thumb:hover,
907
- .annotations-list::-webkit-scrollbar-thumb:hover {
908
- background: var(--text-muted);
909
- }
910
- </style>
911
- </head>
912
- <body>
913
- <div class="container">
914
- <div class="preview">
915
- <div class="preview-wrapper">
916
- <img id="preview-img" src="" alt="Figure Preview">
917
- </div>
918
- </div>
919
- <div class="controls">
920
- <div class="controls-header">
921
- <div>
922
- <h1>SciTeX Editor</h1>
923
- <div class="filename">{{ filename }}</div>
924
- </div>
925
- <button class="theme-toggle" onclick="toggleTheme()" title="Toggle light/dark mode">
926
- <span id="theme-icon">&#9790;</span>
927
- </button>
928
- </div>
929
-
930
- <div class="controls-body">
931
- <!-- Labels Section -->
932
- <div class="section">
933
- <div class="section-header section-toggle" onclick="toggleSection(this)">Labels</div>
934
- <div class="section-content">
935
- <div class="field">
936
- <label>Title</label>
937
- <input type="text" id="title" placeholder="Figure title">
938
- </div>
939
- <div class="field">
940
- <label>X Label</label>
941
- <input type="text" id="xlabel" placeholder="X axis label">
942
- </div>
943
- <div class="field">
944
- <label>Y Label</label>
945
- <input type="text" id="ylabel" placeholder="Y axis label">
946
- </div>
947
- </div>
948
- </div>
949
-
950
- <!-- Axis Limits Section -->
951
- <div class="section">
952
- <div class="section-header section-toggle" onclick="toggleSection(this)">Axis Limits</div>
953
- <div class="section-content">
954
- <div class="field-row">
955
- <div class="field">
956
- <label>X Min</label>
957
- <input type="number" id="xmin" step="any">
958
- </div>
959
- <div class="field">
960
- <label>X Max</label>
961
- <input type="number" id="xmax" step="any">
962
- </div>
963
- </div>
964
- <div class="field-row">
965
- <div class="field">
966
- <label>Y Min</label>
967
- <input type="number" id="ymin" step="any">
968
- </div>
969
- <div class="field">
970
- <label>Y Max</label>
971
- <input type="number" id="ymax" step="any">
972
- </div>
973
- </div>
974
- </div>
975
- </div>
976
-
977
- <!-- Traces Section -->
978
- <div class="section">
979
- <div class="section-header section-toggle" onclick="toggleSection(this)">Traces</div>
980
- <div class="section-content">
981
- <div class="traces-list" id="traces-list">
982
- <!-- Dynamically populated -->
983
- </div>
984
- <div class="field" style="margin-top: 10px;">
985
- <label>Default Line Width (pt)</label>
986
- <input type="number" id="linewidth" value="1.0" min="0.1" max="5" step="0.1">
987
- </div>
988
- </div>
989
- </div>
990
-
991
- <!-- Legend Section -->
992
- <div class="section">
993
- <div class="section-header section-toggle" onclick="toggleSection(this)">Legend</div>
994
- <div class="section-content">
995
- <label class="checkbox-field">
996
- <input type="checkbox" id="legend_visible" checked>
997
- <span>Show Legend</span>
998
- </label>
999
- <div class="field">
1000
- <label>Position</label>
1001
- <select id="legend_loc">
1002
- <option value="best">Best</option>
1003
- <option value="upper right">Upper Right</option>
1004
- <option value="upper left">Upper Left</option>
1005
- <option value="lower right">Lower Right</option>
1006
- <option value="lower left">Lower Left</option>
1007
- <option value="center right">Center Right</option>
1008
- <option value="center left">Center Left</option>
1009
- <option value="upper center">Upper Center</option>
1010
- <option value="lower center">Lower Center</option>
1011
- <option value="center">Center</option>
1012
- </select>
1013
- </div>
1014
- <label class="checkbox-field">
1015
- <input type="checkbox" id="legend_frameon">
1016
- <span>Show Frame</span>
1017
- </label>
1018
- <div class="field">
1019
- <label>Font Size (pt)</label>
1020
- <input type="number" id="legend_fontsize" value="6" min="4" max="16" step="1">
1021
- </div>
1022
- </div>
1023
- </div>
1024
-
1025
- <!-- Ticks Section -->
1026
- <div class="section">
1027
- <div class="section-header section-toggle" onclick="toggleSection(this)">Ticks</div>
1028
- <div class="section-content">
1029
- <div class="field-row">
1030
- <div class="field">
1031
- <label>N Ticks</label>
1032
- <input type="number" id="n_ticks" value="4" min="2" max="10" step="1">
1033
- </div>
1034
- <div class="field">
1035
- <label>Font Size (pt)</label>
1036
- <input type="number" id="tick_fontsize" value="7" min="4" max="16" step="1">
1037
- </div>
1038
- </div>
1039
- <div class="field-row">
1040
- <div class="field">
1041
- <label>Tick Length (mm)</label>
1042
- <input type="number" id="tick_length" value="0.8" min="0.1" max="3" step="0.1">
1043
- </div>
1044
- <div class="field">
1045
- <label>Tick Width (mm)</label>
1046
- <input type="number" id="tick_width" value="0.2" min="0.05" max="1" step="0.05">
1047
- </div>
1048
- </div>
1049
- <div class="field">
1050
- <label>Direction</label>
1051
- <select id="tick_direction">
1052
- <option value="out">Out</option>
1053
- <option value="in">In</option>
1054
- <option value="inout">Both</option>
1055
- </select>
1056
- </div>
1057
- </div>
1058
- </div>
1059
-
1060
- <!-- Style Section -->
1061
- <div class="section">
1062
- <div class="section-header section-toggle" onclick="toggleSection(this)">Style</div>
1063
- <div class="section-content">
1064
- <label class="checkbox-field">
1065
- <input type="checkbox" id="grid">
1066
- <span>Show Grid</span>
1067
- </label>
1068
- <label class="checkbox-field">
1069
- <input type="checkbox" id="hide_top_spine" checked>
1070
- <span>Hide Top Spine</span>
1071
- </label>
1072
- <label class="checkbox-field">
1073
- <input type="checkbox" id="hide_right_spine" checked>
1074
- <span>Hide Right Spine</span>
1075
- </label>
1076
- <div class="field-row">
1077
- <div class="field">
1078
- <label>Axis Width (mm)</label>
1079
- <input type="number" id="axis_width" value="0.2" min="0.05" max="1" step="0.05">
1080
- </div>
1081
- <div class="field">
1082
- <label>Label Size (pt)</label>
1083
- <input type="number" id="axis_fontsize" value="7" min="4" max="16" step="1">
1084
- </div>
1085
- </div>
1086
- <div class="field">
1087
- <label>Background Color</label>
1088
- <div class="color-field">
1089
- <input type="color" id="facecolor" value="#ffffff">
1090
- <input type="text" id="facecolor_text" value="#ffffff" placeholder="#ffffff">
1091
- </div>
1092
- </div>
1093
- <label class="checkbox-field">
1094
- <input type="checkbox" id="transparent" checked>
1095
- <span>Transparent Background</span>
1096
- </label>
1097
- </div>
1098
- </div>
1099
-
1100
- <!-- Dimensions Section -->
1101
- <div class="section">
1102
- <div class="section-header section-toggle" onclick="toggleSection(this)">Dimensions</div>
1103
- <div class="section-content">
1104
- <div class="field-row">
1105
- <div class="field">
1106
- <label>Width (inch)</label>
1107
- <input type="number" id="fig_width" value="3.15" min="1" max="12" step="0.1">
1108
- </div>
1109
- <div class="field">
1110
- <label>Height (inch)</label>
1111
- <input type="number" id="fig_height" value="2.68" min="1" max="12" step="0.1">
1112
- </div>
1113
- </div>
1114
- <div class="field">
1115
- <label>DPI</label>
1116
- <input type="number" id="dpi" value="300" min="72" max="600" step="1">
1117
- </div>
1118
- </div>
1119
- </div>
1120
-
1121
- <!-- Annotations Section -->
1122
- <div class="section">
1123
- <div class="section-header section-toggle" onclick="toggleSection(this)">Annotations</div>
1124
- <div class="section-content">
1125
- <div class="field">
1126
- <label>Text</label>
1127
- <input type="text" id="annot-text" placeholder="Annotation text">
1128
- </div>
1129
- <div class="field-row">
1130
- <div class="field">
1131
- <label>X (0-1)</label>
1132
- <input type="number" id="annot-x" value="0.5" min="0" max="1" step="0.05">
1133
- </div>
1134
- <div class="field">
1135
- <label>Y (0-1)</label>
1136
- <input type="number" id="annot-y" value="0.5" min="0" max="1" step="0.05">
1137
- </div>
1138
- <div class="field">
1139
- <label>Size</label>
1140
- <input type="number" id="annot-size" value="8" min="4" max="24" step="1">
1141
- </div>
1142
- </div>
1143
- <button class="btn btn-secondary" onclick="addAnnotation()">Add Annotation</button>
1144
- <div class="annotations-list" id="annotations-list"></div>
1145
- </div>
1146
- </div>
1147
-
1148
- <!-- Actions Section -->
1149
- <div class="section">
1150
- <div class="section-header">Actions</div>
1151
- <button class="btn btn-cta" onclick="updatePreview()">Update Preview</button>
1152
- <button class="btn btn-primary" onclick="saveManual()">Save to .manual.json</button>
1153
- <button class="btn btn-secondary" onclick="resetOverrides()">Reset to Original</button>
1154
- </div>
1155
-
1156
- <div class="status-bar" id="status">Ready</div>
1157
- </div>
1158
- </div>
1159
- </div>
1160
-
1161
- <script>
1162
- let overrides = {{ overrides|safe }};
1163
- let traces = overrides.traces || [];
1164
-
1165
- // Theme management
1166
- function toggleTheme() {
1167
- const html = document.documentElement;
1168
- const current = html.getAttribute('data-theme');
1169
- const next = current === 'dark' ? 'light' : 'dark';
1170
- html.setAttribute('data-theme', next);
1171
- document.getElementById('theme-icon').innerHTML = next === 'dark' ? '&#9790;' : '&#9788;';
1172
- localStorage.setItem('scitex-editor-theme', next);
1173
- }
1174
-
1175
- // Load saved theme
1176
- const savedTheme = localStorage.getItem('scitex-editor-theme');
1177
- if (savedTheme) {
1178
- document.documentElement.setAttribute('data-theme', savedTheme);
1179
- document.getElementById('theme-icon').innerHTML = savedTheme === 'dark' ? '&#9790;' : '&#9788;';
1180
- }
1181
-
1182
- // Collapsible sections
1183
- function toggleSection(header) {
1184
- header.classList.toggle('collapsed');
1185
- const content = header.nextElementSibling;
1186
- content.classList.toggle('collapsed');
1187
- }
1188
-
1189
- // Initialize fields
1190
- document.addEventListener('DOMContentLoaded', () => {
1191
- // Labels
1192
- if (overrides.title) document.getElementById('title').value = overrides.title;
1193
- if (overrides.xlabel) document.getElementById('xlabel').value = overrides.xlabel;
1194
- if (overrides.ylabel) document.getElementById('ylabel').value = overrides.ylabel;
1195
-
1196
- // Axis limits
1197
- if (overrides.xlim) {
1198
- document.getElementById('xmin').value = overrides.xlim[0];
1199
- document.getElementById('xmax').value = overrides.xlim[1];
1200
- }
1201
- if (overrides.ylim) {
1202
- document.getElementById('ymin').value = overrides.ylim[0];
1203
- document.getElementById('ymax').value = overrides.ylim[1];
1204
- }
1205
-
1206
- // Traces
1207
- document.getElementById('linewidth').value = overrides.linewidth || 1.0;
1208
- updateTracesList();
1209
-
1210
- // Legend
1211
- document.getElementById('legend_visible').checked = overrides.legend_visible !== false;
1212
- document.getElementById('legend_loc').value = overrides.legend_loc || 'best';
1213
- document.getElementById('legend_frameon').checked = overrides.legend_frameon || false;
1214
- document.getElementById('legend_fontsize').value = overrides.legend_fontsize || 6;
1215
-
1216
- // Ticks
1217
- document.getElementById('n_ticks').value = overrides.n_ticks || 4;
1218
- document.getElementById('tick_fontsize').value = overrides.tick_fontsize || 7;
1219
- document.getElementById('tick_length').value = overrides.tick_length || 0.8;
1220
- document.getElementById('tick_width').value = overrides.tick_width || 0.2;
1221
- document.getElementById('tick_direction').value = overrides.tick_direction || 'out';
1222
-
1223
- // Style
1224
- document.getElementById('grid').checked = overrides.grid || false;
1225
- document.getElementById('hide_top_spine').checked = overrides.hide_top_spine !== false;
1226
- document.getElementById('hide_right_spine').checked = overrides.hide_right_spine !== false;
1227
- document.getElementById('axis_width').value = overrides.axis_width || 0.2;
1228
- document.getElementById('axis_fontsize').value = overrides.axis_fontsize || 7;
1229
- document.getElementById('facecolor').value = overrides.facecolor || '#ffffff';
1230
- document.getElementById('facecolor_text').value = overrides.facecolor || '#ffffff';
1231
- document.getElementById('transparent').checked = overrides.transparent !== false;
1232
-
1233
- // Dimensions
1234
- if (overrides.fig_size) {
1235
- document.getElementById('fig_width').value = overrides.fig_size[0];
1236
- document.getElementById('fig_height').value = overrides.fig_size[1];
1237
- }
1238
- document.getElementById('dpi').value = overrides.dpi || 300;
1239
-
1240
- // Sync color inputs
1241
- document.getElementById('facecolor').addEventListener('input', (e) => {
1242
- document.getElementById('facecolor_text').value = e.target.value;
1243
- });
1244
- document.getElementById('facecolor_text').addEventListener('change', (e) => {
1245
- document.getElementById('facecolor').value = e.target.value;
1246
- });
1247
-
1248
- updateAnnotationsList();
1249
- updatePreview();
1250
- });
1251
-
1252
- // Traces list management
1253
- function updateTracesList() {
1254
- const list = document.getElementById('traces-list');
1255
- if (!traces || traces.length === 0) {
1256
- list.innerHTML = '<div style="padding: 10px; color: var(--text-muted); font-size: 0.85em;">No traces found in metadata</div>';
1257
- return;
1258
- }
1259
-
1260
- list.innerHTML = traces.map((t, i) => `
1261
- <div class="trace-item">
1262
- <input type="color" class="trace-color" value="${t.color || '#1f77b4'}"
1263
- onchange="updateTraceColor(${i}, this.value)">
1264
- <span class="trace-label">${t.label || t.id || 'Trace ' + (i+1)}</span>
1265
- <div class="trace-style">
1266
- <select onchange="updateTraceStyle(${i}, this.value)">
1267
- <option value="-" ${t.linestyle === '-' ? 'selected' : ''}>Solid</option>
1268
- <option value="--" ${t.linestyle === '--' ? 'selected' : ''}>Dashed</option>
1269
- <option value=":" ${t.linestyle === ':' ? 'selected' : ''}>Dotted</option>
1270
- <option value="-." ${t.linestyle === '-.' ? 'selected' : ''}>Dash-dot</option>
1271
- </select>
1272
- </div>
1273
- </div>
1274
- `).join('');
1275
- }
1276
-
1277
- function updateTraceColor(idx, color) {
1278
- if (traces[idx]) {
1279
- traces[idx].color = color;
1280
- }
1281
- }
1282
-
1283
- function updateTraceStyle(idx, style) {
1284
- if (traces[idx]) {
1285
- traces[idx].linestyle = style;
1286
- }
1287
- }
1288
-
1289
- function collectOverrides() {
1290
- const o = {};
1291
-
1292
- // Labels
1293
- const title = document.getElementById('title').value;
1294
- const xlabel = document.getElementById('xlabel').value;
1295
- const ylabel = document.getElementById('ylabel').value;
1296
- if (title) o.title = title;
1297
- if (xlabel) o.xlabel = xlabel;
1298
- if (ylabel) o.ylabel = ylabel;
1299
-
1300
- // Axis limits
1301
- const xmin = document.getElementById('xmin').value;
1302
- const xmax = document.getElementById('xmax').value;
1303
- if (xmin !== '' && xmax !== '') o.xlim = [parseFloat(xmin), parseFloat(xmax)];
1304
-
1305
- const ymin = document.getElementById('ymin').value;
1306
- const ymax = document.getElementById('ymax').value;
1307
- if (ymin !== '' && ymax !== '') o.ylim = [parseFloat(ymin), parseFloat(ymax)];
1308
-
1309
- // Traces
1310
- o.linewidth = parseFloat(document.getElementById('linewidth').value) || 1.0;
1311
- o.traces = traces;
1312
-
1313
- // Legend
1314
- o.legend_visible = document.getElementById('legend_visible').checked;
1315
- o.legend_loc = document.getElementById('legend_loc').value;
1316
- o.legend_frameon = document.getElementById('legend_frameon').checked;
1317
- o.legend_fontsize = parseInt(document.getElementById('legend_fontsize').value) || 6;
1318
-
1319
- // Ticks
1320
- o.n_ticks = parseInt(document.getElementById('n_ticks').value) || 4;
1321
- o.tick_fontsize = parseInt(document.getElementById('tick_fontsize').value) || 7;
1322
- o.tick_length = parseFloat(document.getElementById('tick_length').value) || 0.8;
1323
- o.tick_width = parseFloat(document.getElementById('tick_width').value) || 0.2;
1324
- o.tick_direction = document.getElementById('tick_direction').value;
1325
-
1326
- // Style
1327
- o.grid = document.getElementById('grid').checked;
1328
- o.hide_top_spine = document.getElementById('hide_top_spine').checked;
1329
- o.hide_right_spine = document.getElementById('hide_right_spine').checked;
1330
- o.axis_width = parseFloat(document.getElementById('axis_width').value) || 0.2;
1331
- o.axis_fontsize = parseInt(document.getElementById('axis_fontsize').value) || 7;
1332
- o.facecolor = document.getElementById('facecolor').value;
1333
- o.transparent = document.getElementById('transparent').checked;
1334
-
1335
- // Dimensions
1336
- o.fig_size = [
1337
- parseFloat(document.getElementById('fig_width').value) || 3.15,
1338
- parseFloat(document.getElementById('fig_height').value) || 2.68
1339
- ];
1340
- o.dpi = parseInt(document.getElementById('dpi').value) || 300;
1341
-
1342
- // Annotations
1343
- o.annotations = overrides.annotations || [];
1344
-
1345
- return o;
1346
- }
1347
-
1348
- async function updatePreview() {
1349
- setStatus('Updating...', false);
1350
- overrides = collectOverrides();
1351
- try {
1352
- const resp = await fetch('/update', {
1353
- method: 'POST',
1354
- headers: {'Content-Type': 'application/json'},
1355
- body: JSON.stringify({overrides})
1356
- });
1357
- const data = await resp.json();
1358
- document.getElementById('preview-img').src = 'data:image/png;base64,' + data.image;
1359
- setStatus('Preview updated', false);
1360
- } catch (e) {
1361
- setStatus('Error: ' + e.message, true);
1362
- }
1363
- }
1364
-
1365
- async function saveManual() {
1366
- setStatus('Saving...', false);
1367
- try {
1368
- const resp = await fetch('/save', {
1369
- method: 'POST',
1370
- headers: {'Content-Type': 'application/json'}
1371
- });
1372
- const data = await resp.json();
1373
- if (data.status === 'saved') {
1374
- setStatus('Saved: ' + data.path.split('/').pop(), false);
1375
- } else {
1376
- setStatus('Error: ' + data.message, true);
1377
- }
1378
- } catch (e) {
1379
- setStatus('Error: ' + e.message, true);
1380
- }
1381
- }
1382
-
1383
- function resetOverrides() {
1384
- if (confirm('Reset all changes to original values?')) {
1385
- location.reload();
1386
- }
1387
- }
1388
-
1389
- function addAnnotation() {
1390
- const text = document.getElementById('annot-text').value;
1391
- if (!text) return;
1392
- const x = parseFloat(document.getElementById('annot-x').value) || 0.5;
1393
- const y = parseFloat(document.getElementById('annot-y').value) || 0.5;
1394
- const size = parseInt(document.getElementById('annot-size').value) || 8;
1395
- if (!overrides.annotations) overrides.annotations = [];
1396
- overrides.annotations.push({type: 'text', text, x, y, fontsize: size});
1397
- document.getElementById('annot-text').value = '';
1398
- updateAnnotationsList();
1399
- updatePreview();
1400
- }
1401
-
1402
- function removeAnnotation(idx) {
1403
- overrides.annotations.splice(idx, 1);
1404
- updateAnnotationsList();
1405
- updatePreview();
1406
- }
1407
-
1408
- function updateAnnotationsList() {
1409
- const list = document.getElementById('annotations-list');
1410
- const annotations = overrides.annotations || [];
1411
- if (annotations.length === 0) {
1412
- list.innerHTML = '';
1413
- return;
1414
- }
1415
- list.innerHTML = annotations.map((a, i) =>
1416
- `<div class="annotation-item">
1417
- <span>${a.text.substring(0, 25)}${a.text.length > 25 ? '...' : ''} (${a.x.toFixed(2)}, ${a.y.toFixed(2)})</span>
1418
- <button onclick="removeAnnotation(${i})">Remove</button>
1419
- </div>`
1420
- ).join('');
1421
- }
1422
-
1423
- function setStatus(msg, isError = false) {
1424
- const el = document.getElementById('status');
1425
- el.textContent = msg;
1426
- el.classList.toggle('error', isError);
1427
- }
1428
-
1429
- // Auto-update on Enter key in input fields
1430
- document.querySelectorAll('input[type="text"], input[type="number"]').forEach(el => {
1431
- el.addEventListener('keypress', (e) => {
1432
- if (e.key === 'Enter') updatePreview();
1433
- });
1434
- });
1435
- </script>
1436
- </body>
1437
- </html>
1438
- '''
1439
-
1440
- # EOF