scitex 2.15.1__py3-none-any.whl → 2.15.2__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 (107) hide show
  1. scitex/__init__.py +68 -61
  2. scitex/_mcp_tools/introspect.py +42 -23
  3. scitex/_mcp_tools/template.py +24 -0
  4. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +30 -1550
  5. scitex/ai/classification/timeseries/_sliding_window_core.py +467 -0
  6. scitex/ai/classification/timeseries/_sliding_window_plotting.py +369 -0
  7. scitex/audio/__init__.py +2 -2
  8. scitex/audio/_tts.py +18 -10
  9. scitex/audio/engines/base.py +17 -10
  10. scitex/audio/engines/elevenlabs_engine.py +1 -1
  11. scitex/canvas/editor/flask_editor/_core/__init__.py +27 -0
  12. scitex/canvas/editor/flask_editor/_core/_bbox_extraction.py +200 -0
  13. scitex/canvas/editor/flask_editor/_core/_editor.py +173 -0
  14. scitex/canvas/editor/flask_editor/_core/_export_helpers.py +353 -0
  15. scitex/canvas/editor/flask_editor/_core/_routes_basic.py +190 -0
  16. scitex/canvas/editor/flask_editor/_core/_routes_export.py +332 -0
  17. scitex/canvas/editor/flask_editor/_core/_routes_panels.py +252 -0
  18. scitex/canvas/editor/flask_editor/_core/_routes_save.py +218 -0
  19. scitex/canvas/editor/flask_editor/_core.py +25 -1684
  20. scitex/cli/introspect.py +112 -74
  21. scitex/cli/main.py +2 -0
  22. scitex/cli/plt.py +357 -0
  23. scitex/cli/repro.py +15 -8
  24. scitex/cli/resource.py +15 -8
  25. scitex/cli/scholar/__init__.py +15 -8
  26. scitex/cli/social.py +6 -6
  27. scitex/cli/stats.py +15 -8
  28. scitex/cli/template.py +129 -12
  29. scitex/cli/tex.py +15 -8
  30. scitex/cli/writer.py +15 -8
  31. scitex/cloud/__init__.py +41 -2
  32. scitex/config/_env_registry.py +84 -19
  33. scitex/context/__init__.py +22 -0
  34. scitex/dev/__init__.py +20 -1
  35. scitex/gen/__init__.py +50 -14
  36. scitex/gen/_list_packages.py +4 -4
  37. scitex/introspect/__init__.py +16 -9
  38. scitex/introspect/_core.py +7 -8
  39. scitex/{gen/_inspect_module.py → introspect/_list_api.py} +43 -54
  40. scitex/introspect/_mcp/__init__.py +10 -6
  41. scitex/introspect/_mcp/handlers.py +37 -12
  42. scitex/introspect/_members.py +7 -3
  43. scitex/introspect/_signature.py +3 -3
  44. scitex/introspect/_source.py +2 -2
  45. scitex/io/_save.py +1 -2
  46. scitex/logging/_formatters.py +19 -9
  47. scitex/mcp_server.py +1 -1
  48. scitex/os/__init__.py +4 -0
  49. scitex/{gen → os}/_check_host.py +4 -5
  50. scitex/plt/__init__.py +11 -14
  51. scitex/session/__init__.py +26 -7
  52. scitex/session/_decorator.py +1 -1
  53. scitex/sh/__init__.py +7 -4
  54. scitex/social/__init__.py +10 -8
  55. scitex/stats/_mcp/_handlers/__init__.py +31 -0
  56. scitex/stats/_mcp/_handlers/_corrections.py +113 -0
  57. scitex/stats/_mcp/_handlers/_descriptive.py +78 -0
  58. scitex/stats/_mcp/_handlers/_effect_size.py +106 -0
  59. scitex/stats/_mcp/_handlers/_format.py +94 -0
  60. scitex/stats/_mcp/_handlers/_normality.py +110 -0
  61. scitex/stats/_mcp/_handlers/_posthoc.py +224 -0
  62. scitex/stats/_mcp/_handlers/_power.py +247 -0
  63. scitex/stats/_mcp/_handlers/_recommend.py +102 -0
  64. scitex/stats/_mcp/_handlers/_run_test.py +279 -0
  65. scitex/stats/_mcp/_handlers/_stars.py +48 -0
  66. scitex/stats/_mcp/handlers.py +19 -1171
  67. scitex/stats/auto/_stat_style.py +175 -0
  68. scitex/stats/auto/_style_definitions.py +411 -0
  69. scitex/stats/auto/_styles.py +22 -620
  70. scitex/stats/descriptive/__init__.py +11 -8
  71. scitex/stats/descriptive/_ci.py +39 -0
  72. scitex/stats/power/_power.py +15 -4
  73. scitex/str/__init__.py +2 -1
  74. scitex/str/_title_case.py +63 -0
  75. scitex/template/__init__.py +25 -10
  76. scitex/template/_code_templates.py +147 -0
  77. scitex/template/_mcp/handlers.py +81 -0
  78. scitex/template/_mcp/tool_schemas.py +55 -0
  79. scitex/template/_templates/__init__.py +51 -0
  80. scitex/template/_templates/audio.py +233 -0
  81. scitex/template/_templates/canvas.py +312 -0
  82. scitex/template/_templates/capture.py +268 -0
  83. scitex/template/_templates/config.py +43 -0
  84. scitex/template/_templates/diagram.py +294 -0
  85. scitex/template/_templates/io.py +107 -0
  86. scitex/template/_templates/module.py +53 -0
  87. scitex/template/_templates/plt.py +202 -0
  88. scitex/template/_templates/scholar.py +267 -0
  89. scitex/template/_templates/session.py +130 -0
  90. scitex/template/_templates/session_minimal.py +43 -0
  91. scitex/template/_templates/session_plot.py +67 -0
  92. scitex/template/_templates/session_stats.py +77 -0
  93. scitex/template/_templates/stats.py +323 -0
  94. scitex/template/_templates/writer.py +296 -0
  95. scitex/ui/_backends/_email.py +10 -2
  96. scitex/ui/_backends/_webhook.py +5 -1
  97. scitex/web/_search_pubmed.py +10 -6
  98. {scitex-2.15.1.dist-info → scitex-2.15.2.dist-info}/METADATA +1 -1
  99. {scitex-2.15.1.dist-info → scitex-2.15.2.dist-info}/RECORD +105 -64
  100. scitex/gen/_ci.py +0 -12
  101. scitex/gen/_title_case.py +0 -89
  102. /scitex/{gen → context}/_detect_environment.py +0 -0
  103. /scitex/{gen → context}/_get_notebook_path.py +0 -0
  104. /scitex/{gen/_shell.py → sh/_shell_legacy.py} +0 -0
  105. {scitex-2.15.1.dist-info → scitex-2.15.2.dist-info}/WHEEL +0 -0
  106. {scitex-2.15.1.dist-info → scitex-2.15.2.dist-info}/entry_points.txt +0 -0
  107. {scitex-2.15.1.dist-info → scitex-2.15.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-01-24 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/canvas/editor/flask_editor/_core/_routes_save.py
4
+
5
+ """Save-related Flask routes for the editor."""
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from .._core import WebEditor
11
+
12
+ __all__ = [
13
+ "create_save_route",
14
+ "create_save_layout_route",
15
+ "create_save_element_position_route",
16
+ ]
17
+
18
+
19
+ def create_save_route(app, editor: "WebEditor"):
20
+ """Create the save route."""
21
+ from flask import jsonify
22
+
23
+ from ..edit import save_manual_overrides
24
+
25
+ @app.route("/save", methods=["POST"])
26
+ def save():
27
+ try:
28
+ manual_path = save_manual_overrides(
29
+ editor.json_path, editor.current_overrides
30
+ )
31
+ return jsonify({"status": "saved", "path": str(manual_path)})
32
+ except Exception as e:
33
+ return jsonify({"status": "error", "message": str(e)}), 500
34
+
35
+ return save
36
+
37
+
38
+ def create_save_layout_route(app, editor: "WebEditor"):
39
+ """Create the save_layout route."""
40
+ from flask import jsonify, request
41
+
42
+ from ._export_helpers import export_composed_figure
43
+
44
+ @app.route("/save_layout", methods=["POST"])
45
+ def save_layout():
46
+ try:
47
+ data = request.get_json()
48
+ layout = data.get("layout", {})
49
+
50
+ if not layout:
51
+ return jsonify({"success": False, "error": "No layout data provided"})
52
+
53
+ if not editor.panel_info:
54
+ return jsonify(
55
+ {
56
+ "success": False,
57
+ "error": "No panel info available (not a figure bundle)",
58
+ }
59
+ )
60
+
61
+ bundle_path = editor.panel_info.get("bundle_path")
62
+ if not bundle_path:
63
+ return jsonify({"success": False, "error": "Bundle path not available"})
64
+
65
+ from scitex.canvas.io import ZipBundle
66
+
67
+ bundle = ZipBundle(bundle_path)
68
+
69
+ # Read existing layout or create new one
70
+ try:
71
+ existing_layout = bundle.read_json("layout.json")
72
+ except:
73
+ existing_layout = {}
74
+
75
+ # Update layout with new positions
76
+ for panel_name, pos in layout.items():
77
+ if panel_name not in existing_layout:
78
+ existing_layout[panel_name] = {}
79
+ if "position" not in existing_layout[panel_name]:
80
+ existing_layout[panel_name]["position"] = {}
81
+ if "size" not in existing_layout[panel_name]:
82
+ existing_layout[panel_name]["size"] = {}
83
+
84
+ existing_layout[panel_name]["position"]["x_mm"] = pos.get("x_mm", 0)
85
+ existing_layout[panel_name]["position"]["y_mm"] = pos.get("y_mm", 0)
86
+
87
+ if "width_mm" in pos:
88
+ existing_layout[panel_name]["size"]["width_mm"] = pos["width_mm"]
89
+ if "height_mm" in pos:
90
+ existing_layout[panel_name]["size"]["height_mm"] = pos["height_mm"]
91
+
92
+ bundle.write_json("layout.json", existing_layout)
93
+ editor.panel_info["layout"] = existing_layout
94
+
95
+ # Auto-export composed figure
96
+ export_result = export_composed_figure(editor, formats=["png", "svg"])
97
+
98
+ return jsonify(
99
+ {
100
+ "success": True,
101
+ "layout": existing_layout,
102
+ "exported": export_result.get("exported", {}),
103
+ }
104
+ )
105
+
106
+ except Exception as e:
107
+ import traceback
108
+
109
+ return jsonify(
110
+ {
111
+ "success": False,
112
+ "error": str(e),
113
+ "traceback": traceback.format_exc(),
114
+ }
115
+ )
116
+
117
+ return save_layout
118
+
119
+
120
+ def create_save_element_position_route(app, editor: "WebEditor"):
121
+ """Create the save_element_position route.
122
+
123
+ ONLY legends and panel letters can be repositioned to maintain
124
+ scientific rigor. Data elements are never moved.
125
+ """
126
+ from flask import jsonify, request
127
+
128
+ @app.route("/save_element_position", methods=["POST"])
129
+ def save_element_position():
130
+ try:
131
+ data = request.get_json()
132
+ element = data.get("element", "")
133
+ panel = data.get("panel", "")
134
+ element_type = data.get("element_type", "")
135
+ position = data.get("position", {})
136
+ snap_name = data.get("snap_name")
137
+
138
+ # Validate element type (whitelist for scientific rigor)
139
+ ALLOWED_TYPES = ["legend", "panel_letter"]
140
+ if element_type not in ALLOWED_TYPES:
141
+ return jsonify(
142
+ {
143
+ "success": False,
144
+ "error": f"Element type '{element_type}' cannot be repositioned (scientific rigor)",
145
+ }
146
+ )
147
+
148
+ if not editor.panel_info:
149
+ return jsonify({"success": False, "error": "No panel info available"})
150
+
151
+ bundle_path = editor.panel_info.get("bundle_path")
152
+ if not bundle_path:
153
+ return jsonify({"success": False, "error": "Bundle path not available"})
154
+
155
+ from scitex.canvas.io import ZipBundle
156
+
157
+ bundle = ZipBundle(bundle_path)
158
+
159
+ try:
160
+ style = bundle.read_json("style.json")
161
+ except:
162
+ style = {}
163
+
164
+ if "elements" not in style:
165
+ style["elements"] = {}
166
+ if panel not in style["elements"]:
167
+ style["elements"][panel] = {}
168
+
169
+ style["elements"][panel][element] = {
170
+ "type": element_type,
171
+ "position": position,
172
+ "snap_name": snap_name,
173
+ }
174
+
175
+ # For legends, update legend_location for matplotlib compatibility
176
+ if element_type == "legend" and snap_name:
177
+ loc_map = {
178
+ "upper left": "upper left",
179
+ "upper center": "upper center",
180
+ "upper right": "upper right",
181
+ "center left": "center left",
182
+ "center": "center",
183
+ "center right": "center right",
184
+ "lower left": "lower left",
185
+ "lower center": "lower center",
186
+ "lower right": "lower right",
187
+ }
188
+ if snap_name in loc_map:
189
+ if "legend" not in style:
190
+ style["legend"] = {}
191
+ style["legend"]["location"] = loc_map[snap_name]
192
+
193
+ bundle.write_json("style.json", style)
194
+
195
+ return jsonify(
196
+ {
197
+ "success": True,
198
+ "element": element,
199
+ "position": position,
200
+ "snap_name": snap_name,
201
+ }
202
+ )
203
+
204
+ except Exception as e:
205
+ import traceback
206
+
207
+ return jsonify(
208
+ {
209
+ "success": False,
210
+ "error": str(e),
211
+ "traceback": traceback.format_exc(),
212
+ }
213
+ )
214
+
215
+ return save_element_position
216
+
217
+
218
+ # EOF