scitex 2.4.2__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 (64) hide show
  1. scitex/__version__.py +1 -1
  2. scitex/browser/__init__.py +53 -0
  3. scitex/browser/debugging/__init__.py +56 -0
  4. scitex/browser/debugging/_failure_capture.py +372 -0
  5. scitex/browser/debugging/_sync_session.py +259 -0
  6. scitex/browser/debugging/_test_monitor.py +284 -0
  7. scitex/browser/debugging/_visual_cursor.py +432 -0
  8. scitex/io/_load.py +5 -0
  9. scitex/io/_load_modules/_canvas.py +171 -0
  10. scitex/io/_save.py +8 -0
  11. scitex/io/_save_modules/_canvas.py +356 -0
  12. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +77 -22
  13. scitex/plt/docs/FIGURE_ARCHITECTURE.md +257 -0
  14. scitex/plt/utils/__init__.py +10 -0
  15. scitex/plt/utils/_collect_figure_metadata.py +14 -12
  16. scitex/plt/utils/_csv_column_naming.py +237 -0
  17. scitex/scholar/citation_graph/database.py +9 -2
  18. scitex/scholar/config/ScholarConfig.py +23 -3
  19. scitex/scholar/config/default.yaml +55 -0
  20. scitex/scholar/core/Paper.py +102 -0
  21. scitex/scholar/core/__init__.py +44 -0
  22. scitex/scholar/core/journal_normalizer.py +524 -0
  23. scitex/scholar/core/oa_cache.py +285 -0
  24. scitex/scholar/core/open_access.py +457 -0
  25. scitex/scholar/pdf_download/ScholarPDFDownloader.py +137 -0
  26. scitex/scholar/pdf_download/strategies/__init__.py +6 -0
  27. scitex/scholar/pdf_download/strategies/open_access_download.py +186 -0
  28. scitex/scholar/pipelines/ScholarPipelineSearchParallel.py +18 -3
  29. scitex/scholar/pipelines/ScholarPipelineSearchSingle.py +15 -2
  30. scitex/session/_decorator.py +13 -1
  31. scitex/vis/README.md +246 -615
  32. scitex/vis/__init__.py +138 -78
  33. scitex/vis/canvas.py +423 -0
  34. scitex/vis/docs/CANVAS_ARCHITECTURE.md +307 -0
  35. scitex/vis/editor/__init__.py +1 -1
  36. scitex/vis/editor/_dearpygui_editor.py +1830 -0
  37. scitex/vis/editor/_defaults.py +40 -1
  38. scitex/vis/editor/_edit.py +54 -18
  39. scitex/vis/editor/_flask_editor.py +37 -0
  40. scitex/vis/editor/_qt_editor.py +865 -0
  41. scitex/vis/editor/flask_editor/__init__.py +21 -0
  42. scitex/vis/editor/flask_editor/bbox.py +216 -0
  43. scitex/vis/editor/flask_editor/core.py +152 -0
  44. scitex/vis/editor/flask_editor/plotter.py +130 -0
  45. scitex/vis/editor/flask_editor/renderer.py +184 -0
  46. scitex/vis/editor/flask_editor/templates/__init__.py +33 -0
  47. scitex/vis/editor/flask_editor/templates/html.py +295 -0
  48. scitex/vis/editor/flask_editor/templates/scripts.py +614 -0
  49. scitex/vis/editor/flask_editor/templates/styles.py +549 -0
  50. scitex/vis/editor/flask_editor/utils.py +81 -0
  51. scitex/vis/io/__init__.py +84 -21
  52. scitex/vis/io/canvas.py +226 -0
  53. scitex/vis/io/data.py +204 -0
  54. scitex/vis/io/directory.py +202 -0
  55. scitex/vis/io/export.py +460 -0
  56. scitex/vis/io/panel.py +424 -0
  57. {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/METADATA +9 -2
  58. {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/RECORD +61 -32
  59. scitex/vis/DJANGO_INTEGRATION.md +0 -677
  60. scitex/vis/editor/_web_editor.py +0 -1440
  61. scitex/vis/tmp.txt +0 -239
  62. {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/WHEEL +0 -0
  63. {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/entry_points.txt +0 -0
  64. {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/licenses/LICENSE +0 -0
scitex/vis/io/__init__.py CHANGED
@@ -1,36 +1,99 @@
1
1
  #!/usr/bin/env python3
2
2
  # -*- coding: utf-8 -*-
3
+ # Timestamp: 2025-12-08
3
4
  # File: ./src/scitex/vis/io/__init__.py
4
5
  """
5
- I/O functions for figure JSON specifications.
6
+ I/O operations for scitex.vis canvas management.
6
7
 
7
- Provides convenient wrappers around scitex.io for loading and
8
- saving figure JSONs with validation and project structure support.
8
+ Canvas Architecture:
9
+ project/scitex/vis/canvases/{canvas_name}.canvas/
10
+ ├── canvas.json # Layout, panels, composition settings
11
+ ├── panels/ # Panel directories
12
+ │ ├── panel_a/ # scitex type (PNG + JSON + CSV)
13
+ │ └── panel_b/ # image type (PNG/JPG/SVG only)
14
+ └── exports/ # Composed outputs (PNG, PDF, SVG)
15
+
16
+ The .canvas extension makes directories self-documenting, portable, and detectable.
17
+
18
+ Schema Version: 2.0.0
9
19
  """
10
20
 
11
- from .load import (
12
- load_figure_json,
13
- load_figure_json_from_project,
14
- load_figure_model,
15
- list_figures_in_project,
21
+ from .directory import (
22
+ SCHEMA_VERSION,
23
+ CANVAS_EXTENSION,
24
+ ensure_canvas_directory,
25
+ get_canvas_directory_path,
26
+ list_canvas_directories,
27
+ delete_canvas_directory,
28
+ canvas_directory_exists,
29
+ )
30
+
31
+ from .canvas import (
32
+ save_canvas_json,
33
+ load_canvas_json,
34
+ update_canvas_json,
35
+ get_canvas_schema_version,
36
+ )
37
+
38
+ from .panel import (
39
+ add_panel_from_scitex,
40
+ add_panel_from_image,
41
+ remove_panel,
42
+ update_panel,
43
+ list_panels,
44
+ get_panel,
45
+ reorder_panels,
46
+ )
47
+
48
+ from .data import (
49
+ HashMismatchError,
50
+ compute_file_hash,
51
+ verify_data_hash,
52
+ verify_all_data_hashes,
53
+ update_data_hash,
54
+ list_data_files,
16
55
  )
17
56
 
18
- from .save import (
19
- save_figure_json,
20
- save_figure_json_to_project,
21
- save_figure_model,
57
+ from .export import (
58
+ export_canvas_to_file,
59
+ export_canvas_to_multiple_formats,
60
+ list_canvas_exports,
22
61
  )
23
62
 
24
63
  __all__ = [
25
- # Load
26
- "load_figure_json",
27
- "load_figure_json_from_project",
28
- "load_figure_model",
29
- "list_figures_in_project",
30
- # Save
31
- "save_figure_json",
32
- "save_figure_json_to_project",
33
- "save_figure_model",
64
+ # Schema
65
+ "SCHEMA_VERSION",
66
+ "CANVAS_EXTENSION",
67
+ # Directory operations
68
+ "ensure_canvas_directory",
69
+ "get_canvas_directory_path",
70
+ "list_canvas_directories",
71
+ "delete_canvas_directory",
72
+ "canvas_directory_exists",
73
+ # Canvas operations
74
+ "save_canvas_json",
75
+ "load_canvas_json",
76
+ "update_canvas_json",
77
+ "get_canvas_schema_version",
78
+ # Panel operations
79
+ "add_panel_from_scitex",
80
+ "add_panel_from_image",
81
+ "remove_panel",
82
+ "update_panel",
83
+ "list_panels",
84
+ "get_panel",
85
+ "reorder_panels",
86
+ # Data operations
87
+ "HashMismatchError",
88
+ "compute_file_hash",
89
+ "verify_data_hash",
90
+ "verify_all_data_hashes",
91
+ "update_data_hash",
92
+ "list_data_files",
93
+ # Export operations
94
+ "export_canvas_to_file",
95
+ "export_canvas_to_multiple_formats",
96
+ "list_canvas_exports",
34
97
  ]
35
98
 
36
99
  # EOF
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: 2025-12-08
4
+ # File: ./src/scitex/vis/io/canvas.py
5
+ """
6
+ Canvas JSON operations for scitex.vis.
7
+
8
+ Handles saving, loading, and updating canvas.json files.
9
+ """
10
+
11
+ from pathlib import Path
12
+ from typing import Dict, Any, Union, Optional
13
+ from datetime import datetime
14
+ import json
15
+
16
+ from .directory import get_canvas_directory_path, SCHEMA_VERSION
17
+
18
+
19
+ def _get_empty_canvas_template(canvas_name: str) -> Dict[str, Any]:
20
+ """
21
+ Get empty canvas.json template.
22
+
23
+ Parameters
24
+ ----------
25
+ canvas_name : str
26
+ Name of the canvas
27
+
28
+ Returns
29
+ -------
30
+ Dict[str, Any]
31
+ Empty canvas template with schema version
32
+ """
33
+ now = datetime.utcnow().isoformat() + "Z"
34
+
35
+ return {
36
+ "schema_version": SCHEMA_VERSION,
37
+ "canvas_name": canvas_name,
38
+ "size": {
39
+ "width_mm": 180,
40
+ "height_mm": 240,
41
+ },
42
+ "background": {
43
+ "color": "#ffffff",
44
+ "grid": False,
45
+ },
46
+ "panels": [],
47
+ "annotations": [],
48
+ "title": {
49
+ "text": "",
50
+ "position": {"x_mm": 90, "y_mm": 5},
51
+ "fontsize": 14,
52
+ },
53
+ "data_files": [],
54
+ "metadata": {
55
+ "created_at": now,
56
+ "updated_at": now,
57
+ "author": "",
58
+ "description": "",
59
+ },
60
+ "manual_overrides": {},
61
+ }
62
+
63
+
64
+ def save_canvas_json(
65
+ project_dir: Union[str, Path],
66
+ canvas_name: str,
67
+ canvas_json: Dict[str, Any],
68
+ ) -> Path:
69
+ """
70
+ Save canvas specification to canvas.json.
71
+
72
+ Parameters
73
+ ----------
74
+ project_dir : str or Path
75
+ Project root directory
76
+ canvas_name : str
77
+ Descriptive canvas name
78
+ canvas_json : Dict[str, Any]
79
+ Canvas specification dictionary
80
+
81
+ Returns
82
+ -------
83
+ Path
84
+ Path to saved canvas.json file
85
+ """
86
+ canvas_dir = get_canvas_directory_path(project_dir, canvas_name)
87
+ json_path = canvas_dir / "canvas.json"
88
+
89
+ # Ensure directory exists
90
+ canvas_dir.mkdir(parents=True, exist_ok=True)
91
+
92
+ # Update metadata timestamp
93
+ if "metadata" not in canvas_json:
94
+ canvas_json["metadata"] = {}
95
+ canvas_json["metadata"]["updated_at"] = datetime.utcnow().isoformat() + "Z"
96
+
97
+ # Ensure schema version is set
98
+ if "schema_version" not in canvas_json:
99
+ canvas_json["schema_version"] = SCHEMA_VERSION
100
+
101
+ with open(json_path, "w") as f:
102
+ json.dump(canvas_json, f, indent=2)
103
+
104
+ return json_path
105
+
106
+
107
+ def load_canvas_json(
108
+ project_dir: Union[str, Path],
109
+ canvas_name: str,
110
+ verify_data_hashes: bool = True,
111
+ ) -> Dict[str, Any]:
112
+ """
113
+ Load canvas specification from canvas.json.
114
+
115
+ Parameters
116
+ ----------
117
+ project_dir : str or Path
118
+ Project root directory
119
+ canvas_name : str
120
+ Descriptive canvas name
121
+ verify_data_hashes : bool, optional
122
+ If True, verify all data file hashes (default: True)
123
+
124
+ Returns
125
+ -------
126
+ Dict[str, Any]
127
+ Canvas specification dictionary
128
+
129
+ Raises
130
+ ------
131
+ FileNotFoundError
132
+ If canvas.json does not exist
133
+ HashMismatchError
134
+ If verify_data_hashes=True and any data file hash doesn't match
135
+ """
136
+ canvas_dir = get_canvas_directory_path(project_dir, canvas_name)
137
+ json_path = canvas_dir / "canvas.json"
138
+
139
+ if not json_path.exists():
140
+ raise FileNotFoundError(f"Canvas not found: {json_path}")
141
+
142
+ with open(json_path, "r") as f:
143
+ canvas_json = json.load(f)
144
+
145
+ # Verify data hashes if requested
146
+ if verify_data_hashes and canvas_json.get("data_files"):
147
+ from .data import verify_all_data_hashes
148
+ hash_results = verify_all_data_hashes(project_dir, canvas_name)
149
+ invalid_files = [f for f, valid in hash_results.items() if not valid]
150
+ if invalid_files:
151
+ from .data import HashMismatchError
152
+ raise HashMismatchError(
153
+ f"Data file hash mismatch for: {', '.join(invalid_files)}"
154
+ )
155
+
156
+ return canvas_json
157
+
158
+
159
+ def update_canvas_json(
160
+ project_dir: Union[str, Path],
161
+ canvas_name: str,
162
+ updates: Dict[str, Any],
163
+ ) -> Path:
164
+ """
165
+ Partial update of canvas.json (merge with existing).
166
+
167
+ Parameters
168
+ ----------
169
+ project_dir : str or Path
170
+ Project root directory
171
+ canvas_name : str
172
+ Descriptive canvas name
173
+ updates : Dict[str, Any]
174
+ Dictionary of updates to merge
175
+
176
+ Returns
177
+ -------
178
+ Path
179
+ Path to updated canvas.json file
180
+ """
181
+ # Load existing
182
+ canvas_json = load_canvas_json(project_dir, canvas_name, verify_data_hashes=False)
183
+
184
+ # Deep merge updates
185
+ _deep_merge(canvas_json, updates)
186
+
187
+ # Save back
188
+ return save_canvas_json(project_dir, canvas_name, canvas_json)
189
+
190
+
191
+ def _deep_merge(base: Dict, updates: Dict) -> None:
192
+ """Deep merge updates into base dictionary (in-place)."""
193
+ for key, value in updates.items():
194
+ if key in base and isinstance(base[key], dict) and isinstance(value, dict):
195
+ _deep_merge(base[key], value)
196
+ else:
197
+ base[key] = value
198
+
199
+
200
+ def get_canvas_schema_version(
201
+ project_dir: Union[str, Path],
202
+ canvas_name: str,
203
+ ) -> Optional[str]:
204
+ """
205
+ Get schema version of a canvas.
206
+
207
+ Parameters
208
+ ----------
209
+ project_dir : str or Path
210
+ Project root directory
211
+ canvas_name : str
212
+ Descriptive canvas name
213
+
214
+ Returns
215
+ -------
216
+ Optional[str]
217
+ Schema version string or None if not found
218
+ """
219
+ try:
220
+ canvas_json = load_canvas_json(project_dir, canvas_name, verify_data_hashes=False)
221
+ return canvas_json.get("schema_version")
222
+ except FileNotFoundError:
223
+ return None
224
+
225
+
226
+ # EOF
scitex/vis/io/data.py ADDED
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: 2025-12-08
4
+ # File: ./src/scitex/vis/io/data.py
5
+ """
6
+ Data operations for scitex.vis.
7
+
8
+ Handles SHA256 hash computation and verification for data integrity.
9
+ """
10
+
11
+ from pathlib import Path
12
+ from typing import Dict, Union
13
+ import hashlib
14
+
15
+
16
+ class HashMismatchError(Exception):
17
+ """Raised when a data file hash doesn't match expected value."""
18
+ pass
19
+
20
+
21
+ def compute_file_hash(filepath: Union[str, Path]) -> str:
22
+ """
23
+ Compute SHA256 hash of a file.
24
+
25
+ Parameters
26
+ ----------
27
+ filepath : str or Path
28
+ Path to file
29
+
30
+ Returns
31
+ -------
32
+ str
33
+ Hash string in format "sha256:{hex_digest}"
34
+ """
35
+ filepath = Path(filepath)
36
+
37
+ if not filepath.exists():
38
+ raise FileNotFoundError(f"File not found: {filepath}")
39
+
40
+ sha256_hash = hashlib.sha256()
41
+
42
+ with open(filepath, "rb") as f:
43
+ # Read in chunks for large files
44
+ for chunk in iter(lambda: f.read(8192), b""):
45
+ sha256_hash.update(chunk)
46
+
47
+ return f"sha256:{sha256_hash.hexdigest()}"
48
+
49
+
50
+ def verify_data_hash(
51
+ filepath: Union[str, Path],
52
+ expected_hash: str,
53
+ ) -> bool:
54
+ """
55
+ Verify file hash against expected value.
56
+
57
+ Parameters
58
+ ----------
59
+ filepath : str or Path
60
+ Path to file
61
+ expected_hash : str
62
+ Expected hash string (format: "sha256:{hex_digest}")
63
+
64
+ Returns
65
+ -------
66
+ bool
67
+ True if hash matches, False otherwise
68
+ """
69
+ try:
70
+ actual_hash = compute_file_hash(filepath)
71
+ return actual_hash == expected_hash
72
+ except FileNotFoundError:
73
+ return False
74
+
75
+
76
+ def verify_all_data_hashes(
77
+ project_dir: Union[str, Path],
78
+ canvas_name: str,
79
+ ) -> Dict[str, bool]:
80
+ """
81
+ Verify hashes of all data files in a canvas.
82
+
83
+ Parameters
84
+ ----------
85
+ project_dir : str or Path
86
+ Project root directory
87
+ canvas_name : str
88
+ Canvas name
89
+
90
+ Returns
91
+ -------
92
+ Dict[str, bool]
93
+ Dictionary mapping file paths to validation results
94
+ """
95
+ from .directory import get_canvas_directory_path
96
+ from .canvas import load_canvas_json
97
+
98
+ canvas_dir = get_canvas_directory_path(project_dir, canvas_name)
99
+
100
+ # Load canvas.json without hash verification to avoid recursion
101
+ json_path = canvas_dir / "canvas.json"
102
+ if not json_path.exists():
103
+ return {}
104
+
105
+ import json
106
+ with open(json_path, "r") as f:
107
+ canvas_json = json.load(f)
108
+
109
+ results = {}
110
+ for data_file in canvas_json.get("data_files", []):
111
+ rel_path = data_file.get("path", "")
112
+ expected_hash = data_file.get("hash", "")
113
+
114
+ if rel_path and expected_hash:
115
+ filepath = canvas_dir / rel_path
116
+ results[rel_path] = verify_data_hash(filepath, expected_hash)
117
+
118
+ return results
119
+
120
+
121
+ def update_data_hash(
122
+ project_dir: Union[str, Path],
123
+ canvas_name: str,
124
+ rel_path: str,
125
+ ) -> str:
126
+ """
127
+ Update hash for a data file in canvas.json.
128
+
129
+ Parameters
130
+ ----------
131
+ project_dir : str or Path
132
+ Project root directory
133
+ canvas_name : str
134
+ Canvas name
135
+ rel_path : str
136
+ Relative path to data file within canvas directory
137
+
138
+ Returns
139
+ -------
140
+ str
141
+ New hash value
142
+ """
143
+ from .directory import get_canvas_directory_path
144
+ from .canvas import load_canvas_json, save_canvas_json
145
+
146
+ canvas_dir = get_canvas_directory_path(project_dir, canvas_name)
147
+ filepath = canvas_dir / rel_path
148
+
149
+ new_hash = compute_file_hash(filepath)
150
+
151
+ canvas_json = load_canvas_json(project_dir, canvas_name, verify_data_hashes=False)
152
+
153
+ # Update or add data file entry
154
+ found = False
155
+ for data_file in canvas_json.get("data_files", []):
156
+ if data_file.get("path") == rel_path:
157
+ data_file["hash"] = new_hash
158
+ found = True
159
+ break
160
+
161
+ if not found:
162
+ if "data_files" not in canvas_json:
163
+ canvas_json["data_files"] = []
164
+ canvas_json["data_files"].append({
165
+ "path": rel_path,
166
+ "hash": new_hash,
167
+ })
168
+
169
+ save_canvas_json(project_dir, canvas_name, canvas_json)
170
+
171
+ return new_hash
172
+
173
+
174
+ def list_data_files(
175
+ project_dir: Union[str, Path],
176
+ canvas_name: str,
177
+ ) -> Dict[str, str]:
178
+ """
179
+ List all data files and their hashes.
180
+
181
+ Parameters
182
+ ----------
183
+ project_dir : str or Path
184
+ Project root directory
185
+ canvas_name : str
186
+ Canvas name
187
+
188
+ Returns
189
+ -------
190
+ Dict[str, str]
191
+ Dictionary mapping file paths to hash values
192
+ """
193
+ from .canvas import load_canvas_json
194
+
195
+ canvas_json = load_canvas_json(project_dir, canvas_name, verify_data_hashes=False)
196
+
197
+ return {
198
+ d.get("path", ""): d.get("hash", "")
199
+ for d in canvas_json.get("data_files", [])
200
+ if d.get("path")
201
+ }
202
+
203
+
204
+ # EOF