scitex 2.4.3__py3-none-any.whl → 2.5.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 (45) hide show
  1. scitex/__version__.py +1 -1
  2. scitex/io/_load.py +5 -0
  3. scitex/io/_load_modules/_canvas.py +171 -0
  4. scitex/io/_save.py +8 -0
  5. scitex/io/_save_modules/_canvas.py +356 -0
  6. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +77 -22
  7. scitex/plt/docs/FIGURE_ARCHITECTURE.md +257 -0
  8. scitex/plt/utils/__init__.py +10 -0
  9. scitex/plt/utils/_collect_figure_metadata.py +14 -12
  10. scitex/plt/utils/_csv_column_naming.py +237 -0
  11. scitex/session/_decorator.py +13 -1
  12. scitex/vis/README.md +246 -615
  13. scitex/vis/__init__.py +138 -78
  14. scitex/vis/canvas.py +423 -0
  15. scitex/vis/docs/CANVAS_ARCHITECTURE.md +307 -0
  16. scitex/vis/editor/__init__.py +1 -1
  17. scitex/vis/editor/_dearpygui_editor.py +1830 -0
  18. scitex/vis/editor/_defaults.py +40 -1
  19. scitex/vis/editor/_edit.py +54 -18
  20. scitex/vis/editor/_flask_editor.py +37 -0
  21. scitex/vis/editor/_qt_editor.py +865 -0
  22. scitex/vis/editor/flask_editor/__init__.py +21 -0
  23. scitex/vis/editor/flask_editor/bbox.py +216 -0
  24. scitex/vis/editor/flask_editor/core.py +152 -0
  25. scitex/vis/editor/flask_editor/plotter.py +130 -0
  26. scitex/vis/editor/flask_editor/renderer.py +184 -0
  27. scitex/vis/editor/flask_editor/templates/__init__.py +33 -0
  28. scitex/vis/editor/flask_editor/templates/html.py +295 -0
  29. scitex/vis/editor/flask_editor/templates/scripts.py +614 -0
  30. scitex/vis/editor/flask_editor/templates/styles.py +549 -0
  31. scitex/vis/editor/flask_editor/utils.py +81 -0
  32. scitex/vis/io/__init__.py +84 -21
  33. scitex/vis/io/canvas.py +226 -0
  34. scitex/vis/io/data.py +204 -0
  35. scitex/vis/io/directory.py +202 -0
  36. scitex/vis/io/export.py +460 -0
  37. scitex/vis/io/panel.py +424 -0
  38. {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/METADATA +9 -2
  39. {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/RECORD +42 -21
  40. scitex/vis/DJANGO_INTEGRATION.md +0 -677
  41. scitex/vis/editor/_web_editor.py +0 -1440
  42. scitex/vis/tmp.txt +0 -239
  43. {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/WHEEL +0 -0
  44. {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/entry_points.txt +0 -0
  45. {scitex-2.4.3.dist-info → scitex-2.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: 2025-12-08
4
+ # File: ./src/scitex/plt/utils/_csv_column_naming.py
5
+
6
+ """
7
+ Single source of truth for CSV column naming in scitex.
8
+
9
+ This module ensures consistent column naming between:
10
+ - CSV export (_export_as_csv)
11
+ - JSON metadata (_collect_figure_metadata)
12
+ - GUI editors (reading CSV data back)
13
+
14
+ Column naming convention:
15
+ ax_{row}{col}_{trace_id}_{data_type}
16
+
17
+ Where:
18
+ - row, col: axes position in grid (e.g., "00" for single axes)
19
+ - trace_id: unique identifier for the trace (from label, id kwarg, or index)
20
+ - data_type: type of data (e.g., "plot_x", "plot_y", "hist_bins", etc.)
21
+ """
22
+
23
+ __all__ = [
24
+ 'get_csv_column_name',
25
+ 'get_csv_column_prefix',
26
+ 'parse_csv_column_name',
27
+ 'sanitize_trace_id',
28
+ ]
29
+
30
+
31
+ def sanitize_trace_id(trace_id: str) -> str:
32
+ """Sanitize trace ID for use in CSV column names.
33
+
34
+ Removes or replaces characters that could cause issues in column names.
35
+
36
+ Parameters
37
+ ----------
38
+ trace_id : str
39
+ Raw trace identifier (label, id kwarg, or generated)
40
+
41
+ Returns
42
+ -------
43
+ str
44
+ Sanitized trace ID safe for CSV column names
45
+ """
46
+ if not trace_id:
47
+ return "unnamed"
48
+
49
+ # Replace problematic characters
50
+ sanitized = str(trace_id)
51
+ # Keep alphanumeric, underscore, hyphen; replace others with underscore
52
+ result = []
53
+ for char in sanitized:
54
+ if char.isalnum() or char in ('_', '-'):
55
+ result.append(char)
56
+ elif char in (' ', '(', ')', '[', ']', '{', '}', '/', '\\', '.'):
57
+ result.append('_')
58
+ # Skip other characters
59
+
60
+ sanitized = ''.join(result)
61
+
62
+ # Remove consecutive underscores
63
+ while '__' in sanitized:
64
+ sanitized = sanitized.replace('__', '_')
65
+
66
+ # Remove leading/trailing underscores
67
+ sanitized = sanitized.strip('_')
68
+
69
+ return sanitized if sanitized else "unnamed"
70
+
71
+
72
+ def get_csv_column_prefix(ax_row: int = 0, ax_col: int = 0, trace_id: str = None, trace_index: int = None) -> str:
73
+ """Get CSV column prefix for a trace.
74
+
75
+ Parameters
76
+ ----------
77
+ ax_row : int
78
+ Row position of axes in grid (default: 0)
79
+ ax_col : int
80
+ Column position of axes in grid (default: 0)
81
+ trace_id : str, optional
82
+ Trace identifier (from label or id kwarg). If None, uses trace_index.
83
+ trace_index : int, optional
84
+ Index of trace when no trace_id is provided (default: 0)
85
+
86
+ Returns
87
+ -------
88
+ str
89
+ Column prefix like "ax_00_sin_x_" or "ax_01_plot_0_"
90
+ """
91
+ ax_pos = f"{ax_row}{ax_col}"
92
+
93
+ if trace_id:
94
+ safe_id = sanitize_trace_id(trace_id)
95
+ elif trace_index is not None:
96
+ safe_id = f"plot_{trace_index}"
97
+ else:
98
+ safe_id = "plot_0"
99
+
100
+ return f"ax_{ax_pos}_{safe_id}_"
101
+
102
+
103
+ def get_csv_column_name(
104
+ data_type: str,
105
+ ax_row: int = 0,
106
+ ax_col: int = 0,
107
+ trace_id: str = None,
108
+ trace_index: int = None,
109
+ ) -> str:
110
+ """Get full CSV column name for a data field.
111
+
112
+ Parameters
113
+ ----------
114
+ data_type : str
115
+ Type of data (e.g., "plot_x", "plot_y", "hist_bins", "bar_heights")
116
+ ax_row : int
117
+ Row position of axes in grid (default: 0)
118
+ ax_col : int
119
+ Column position of axes in grid (default: 0)
120
+ trace_id : str, optional
121
+ Trace identifier (from label or id kwarg)
122
+ trace_index : int, optional
123
+ Index of trace when no trace_id is provided
124
+
125
+ Returns
126
+ -------
127
+ str
128
+ Full column name like "ax_00_sin_x_plot_x" or "ax_01_plot_0_plot_y"
129
+
130
+ Examples
131
+ --------
132
+ >>> get_csv_column_name("plot_x", trace_id="sin(x)")
133
+ 'ax_00_sin_x_plot_x'
134
+ >>> get_csv_column_name("plot_y", ax_row=1, ax_col=2, trace_index=0)
135
+ 'ax_12_plot_0_plot_y'
136
+ """
137
+ prefix = get_csv_column_prefix(ax_row, ax_col, trace_id, trace_index)
138
+ return f"{prefix}{data_type}"
139
+
140
+
141
+ def parse_csv_column_name(column_name: str) -> dict:
142
+ """Parse CSV column name to extract components.
143
+
144
+ Parameters
145
+ ----------
146
+ column_name : str
147
+ Full column name (e.g., "ax_00_sin_x_plot_x")
148
+
149
+ Returns
150
+ -------
151
+ dict
152
+ Dictionary with keys:
153
+ - ax_row: int
154
+ - ax_col: int
155
+ - trace_id: str
156
+ - data_type: str
157
+ - valid: bool (True if parsing succeeded)
158
+
159
+ Examples
160
+ --------
161
+ >>> parse_csv_column_name("ax_00_sin_x_plot_x")
162
+ {'ax_row': 0, 'ax_col': 0, 'trace_id': 'sin_x', 'data_type': 'plot_x', 'valid': True}
163
+ """
164
+ result = {
165
+ 'ax_row': 0,
166
+ 'ax_col': 0,
167
+ 'trace_id': '',
168
+ 'data_type': '',
169
+ 'valid': False,
170
+ }
171
+
172
+ if not column_name or not column_name.startswith('ax_'):
173
+ return result
174
+
175
+ parts = column_name.split('_')
176
+ if len(parts) < 4:
177
+ return result
178
+
179
+ try:
180
+ # Parse ax position (e.g., "00" from "ax_00_...")
181
+ ax_pos = parts[1]
182
+ if len(ax_pos) >= 2:
183
+ result['ax_row'] = int(ax_pos[0])
184
+ result['ax_col'] = int(ax_pos[1])
185
+
186
+ # Last two parts are typically data_type (e.g., "plot_x", "hist_bins")
187
+ # Everything in between is the trace_id
188
+ data_type_parts = parts[-2:] # e.g., ["plot", "x"]
189
+ result['data_type'] = '_'.join(data_type_parts)
190
+
191
+ # Trace ID is everything between ax_pos and data_type
192
+ trace_parts = parts[2:-2]
193
+ result['trace_id'] = '_'.join(trace_parts) if trace_parts else 'plot_0'
194
+
195
+ result['valid'] = True
196
+
197
+ except (ValueError, IndexError):
198
+ pass
199
+
200
+ return result
201
+
202
+
203
+ def get_trace_columns_from_df(df, trace_id: str = None, trace_index: int = None, ax_row: int = 0, ax_col: int = 0) -> dict:
204
+ """Find CSV columns for a specific trace in a DataFrame.
205
+
206
+ Parameters
207
+ ----------
208
+ df : pandas.DataFrame
209
+ DataFrame with CSV data
210
+ trace_id : str, optional
211
+ Trace identifier to search for
212
+ trace_index : int, optional
213
+ Trace index to search for (if trace_id not provided)
214
+ ax_row : int
215
+ Row position of axes
216
+ ax_col : int
217
+ Column position of axes
218
+
219
+ Returns
220
+ -------
221
+ dict
222
+ Dictionary mapping data types to column names, e.g.:
223
+ {'plot_x': 'ax_00_sin_x_plot_x', 'plot_y': 'ax_00_sin_x_plot_y'}
224
+ """
225
+ result = {}
226
+ prefix = get_csv_column_prefix(ax_row, ax_col, trace_id, trace_index)
227
+
228
+ for col in df.columns:
229
+ if col.startswith(prefix):
230
+ # Extract data_type from column name
231
+ data_type = col[len(prefix):]
232
+ result[data_type] = col
233
+
234
+ return result
235
+
236
+
237
+ # EOF
@@ -495,6 +495,7 @@ def _add_argument(
495
495
  type_hints: Type hints dictionary
496
496
  short_form: Optional short form (e.g., 'a' for -a)
497
497
  """
498
+ from typing import get_origin, get_args, Literal
498
499
 
499
500
  # Get type
500
501
  param_type = type_hints.get(param_name, param.annotation)
@@ -513,6 +514,13 @@ def _add_argument(
513
514
  if short_form:
514
515
  arg_names.insert(0, f"-{short_form}")
515
516
 
517
+ # Check for Literal type (choices)
518
+ choices = None
519
+ origin = get_origin(param_type)
520
+ if origin is Literal:
521
+ choices = list(get_args(param_type))
522
+ param_type = type(choices[0]) if choices else str
523
+
516
524
  # Handle different types
517
525
  if param_type == bool:
518
526
  # Boolean flags
@@ -524,11 +532,15 @@ def _add_argument(
524
532
  )
525
533
  else:
526
534
  # Regular arguments
535
+ choices_str = f", choices: {choices}" if choices else ""
527
536
  kwargs = {
528
537
  'type': param_type,
529
- 'help': f"(default: {default})" if has_default else "(required)",
538
+ 'help': f"(default: {default}{choices_str})" if has_default else f"(required{choices_str})",
530
539
  }
531
540
 
541
+ if choices:
542
+ kwargs['choices'] = choices
543
+
532
544
  if has_default:
533
545
  kwargs['default'] = default
534
546
  else: