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/panel.py ADDED
@@ -0,0 +1,424 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: 2025-12-08
4
+ # File: ./src/scitex/vis/io/panel.py
5
+ """
6
+ Panel operations for scitex.vis.
7
+
8
+ Handles adding, removing, updating, and listing panels within a canvas.
9
+ Panels can be either 'scitex' type (full stx.plt output) or 'image' type (static image).
10
+ """
11
+
12
+ from pathlib import Path
13
+ from typing import Dict, Any, Union, List, Optional
14
+ import shutil
15
+
16
+ from .directory import get_canvas_directory_path
17
+ from .canvas import load_canvas_json, save_canvas_json
18
+ from .data import compute_file_hash
19
+
20
+
21
+ def _symlink_or_copy(source: Path, dest: Path, bundle: bool = False) -> None:
22
+ """Create relative symlink or copy file based on bundle flag."""
23
+ import os
24
+
25
+ if dest.exists() or dest.is_symlink():
26
+ dest.unlink()
27
+
28
+ if bundle:
29
+ shutil.copy2(source, dest)
30
+ else:
31
+ try:
32
+ # Use relative symlink for portability
33
+ rel_path = os.path.relpath(source.resolve(), dest.parent.resolve())
34
+ dest.symlink_to(rel_path)
35
+ except (OSError, ValueError):
36
+ # Fallback to copy if symlink fails (e.g., Windows without admin)
37
+ shutil.copy2(source, dest)
38
+
39
+
40
+ def _get_default_panel_properties() -> Dict[str, Any]:
41
+ """Get default panel properties."""
42
+ return {
43
+ "position": {"x_mm": 0, "y_mm": 0},
44
+ "size": {"width_mm": 50, "height_mm": 50},
45
+ "z_index": 0,
46
+ "rotation_deg": 0,
47
+ "clip": {
48
+ "enabled": False,
49
+ "x_mm": 0,
50
+ "y_mm": 0,
51
+ "width_mm": None,
52
+ "height_mm": None,
53
+ },
54
+ "opacity": 1.0,
55
+ "flip_h": False,
56
+ "flip_v": False,
57
+ "visible": True,
58
+ "label": {
59
+ "text": "",
60
+ "position": "top-left",
61
+ "fontsize": 12,
62
+ "fontweight": "bold",
63
+ },
64
+ "border": {
65
+ "visible": False,
66
+ "color": "#000000",
67
+ "width_mm": 0.2,
68
+ },
69
+ }
70
+
71
+
72
+ def add_panel_from_scitex(
73
+ project_dir: Union[str, Path],
74
+ canvas_name: str,
75
+ panel_name: str,
76
+ source_png: Union[str, Path],
77
+ source_json: Optional[Union[str, Path]] = None,
78
+ source_csv: Optional[Union[str, Path]] = None,
79
+ panel_properties: Optional[Dict[str, Any]] = None,
80
+ bundle: bool = False,
81
+ ) -> Path:
82
+ """
83
+ Add a panel from stx.plt output (scitex type).
84
+
85
+ Parameters
86
+ ----------
87
+ project_dir : str or Path
88
+ Project root directory
89
+ canvas_name : str
90
+ Canvas name
91
+ panel_name : str
92
+ Name for the new panel
93
+ source_png : str or Path
94
+ Path to source PNG file
95
+ source_json : str or Path, optional
96
+ Path to source JSON file (auto-detected if not provided)
97
+ source_csv : str or Path, optional
98
+ Path to source CSV file (auto-detected if not provided)
99
+ panel_properties : Dict, optional
100
+ Panel properties (position, size, etc.)
101
+ bundle : bool, optional
102
+ If True, copy files. If False (default), use symlinks.
103
+
104
+ Returns
105
+ -------
106
+ Path
107
+ Path to panel directory
108
+ """
109
+ source_png = Path(source_png)
110
+ base_name = source_png.stem
111
+
112
+ # Auto-detect json and csv if not provided
113
+ if source_json is None:
114
+ source_json = source_png.parent / f"{base_name}.json"
115
+ if source_csv is None:
116
+ source_csv = source_png.parent / f"{base_name}.csv"
117
+
118
+ source_json = Path(source_json)
119
+ source_csv = Path(source_csv)
120
+
121
+ # Create panel directory
122
+ canvas_dir = get_canvas_directory_path(project_dir, canvas_name)
123
+ panel_dir = canvas_dir / "panels" / panel_name
124
+ panel_dir.mkdir(parents=True, exist_ok=True)
125
+
126
+ # Use symlinks (default) or copy files based on bundle flag
127
+ _symlink_or_copy(source_png.resolve(), panel_dir / "panel.png", bundle=bundle)
128
+ if source_json.exists():
129
+ _symlink_or_copy(source_json.resolve(), panel_dir / "panel.json", bundle=bundle)
130
+ if source_csv.exists():
131
+ _symlink_or_copy(source_csv.resolve(), panel_dir / "panel.csv", bundle=bundle)
132
+
133
+ # Build panel entry
134
+ panel_entry = _get_default_panel_properties()
135
+ panel_entry["name"] = panel_name
136
+ panel_entry["type"] = "scitex"
137
+
138
+ if panel_properties:
139
+ _deep_merge(panel_entry, panel_properties)
140
+
141
+ # Update canvas.json
142
+ canvas_json = load_canvas_json(project_dir, canvas_name, verify_data_hashes=False)
143
+
144
+ # Remove existing panel with same name
145
+ canvas_json["panels"] = [p for p in canvas_json["panels"] if p.get("name") != panel_name]
146
+
147
+ # Add new panel
148
+ canvas_json["panels"].append(panel_entry)
149
+
150
+ # Add data file reference with hash
151
+ if source_csv.exists():
152
+ csv_path = f"panels/{panel_name}/panel.csv"
153
+ csv_hash = compute_file_hash(panel_dir / "panel.csv")
154
+ # Remove existing reference
155
+ canvas_json["data_files"] = [
156
+ d for d in canvas_json.get("data_files", [])
157
+ if d.get("path") != csv_path
158
+ ]
159
+ canvas_json["data_files"].append({
160
+ "path": csv_path,
161
+ "hash": csv_hash,
162
+ })
163
+
164
+ save_canvas_json(project_dir, canvas_name, canvas_json)
165
+
166
+ return panel_dir
167
+
168
+
169
+ def add_panel_from_image(
170
+ project_dir: Union[str, Path],
171
+ canvas_name: str,
172
+ panel_name: str,
173
+ source_image: Union[str, Path],
174
+ panel_properties: Optional[Dict[str, Any]] = None,
175
+ bundle: bool = False,
176
+ ) -> Path:
177
+ """
178
+ Add a panel from an image file (image type).
179
+
180
+ Parameters
181
+ ----------
182
+ project_dir : str or Path
183
+ Project root directory
184
+ canvas_name : str
185
+ Canvas name
186
+ panel_name : str
187
+ Name for the new panel
188
+ source_image : str or Path
189
+ Path to source image file (PNG, JPG, SVG)
190
+ panel_properties : Dict, optional
191
+ Panel properties (position, size, etc.)
192
+ bundle : bool, optional
193
+ If True, copy files. If False (default), use symlinks.
194
+
195
+ Returns
196
+ -------
197
+ Path
198
+ Path to panel directory
199
+ """
200
+ source_image = Path(source_image)
201
+ suffix = source_image.suffix.lower()
202
+
203
+ # Create panel directory
204
+ canvas_dir = get_canvas_directory_path(project_dir, canvas_name)
205
+ panel_dir = canvas_dir / "panels" / panel_name
206
+ panel_dir.mkdir(parents=True, exist_ok=True)
207
+
208
+ # Use symlink (default) or copy based on bundle flag
209
+ dest_name = f"panel{suffix}"
210
+ _symlink_or_copy(source_image.resolve(), panel_dir / dest_name, bundle=bundle)
211
+
212
+ # Build panel entry
213
+ panel_entry = _get_default_panel_properties()
214
+ panel_entry["name"] = panel_name
215
+ panel_entry["type"] = "image"
216
+ panel_entry["source"] = dest_name
217
+
218
+ if panel_properties:
219
+ _deep_merge(panel_entry, panel_properties)
220
+
221
+ # Update canvas.json
222
+ canvas_json = load_canvas_json(project_dir, canvas_name, verify_data_hashes=False)
223
+
224
+ # Remove existing panel with same name
225
+ canvas_json["panels"] = [p for p in canvas_json["panels"] if p.get("name") != panel_name]
226
+
227
+ # Add new panel
228
+ canvas_json["panels"].append(panel_entry)
229
+
230
+ save_canvas_json(project_dir, canvas_name, canvas_json)
231
+
232
+ return panel_dir
233
+
234
+
235
+ def remove_panel(
236
+ project_dir: Union[str, Path],
237
+ canvas_name: str,
238
+ panel_name: str,
239
+ ) -> bool:
240
+ """
241
+ Remove a panel from canvas.
242
+
243
+ Parameters
244
+ ----------
245
+ project_dir : str or Path
246
+ Project root directory
247
+ canvas_name : str
248
+ Canvas name
249
+ panel_name : str
250
+ Name of panel to remove
251
+
252
+ Returns
253
+ -------
254
+ bool
255
+ True if removed, False if panel didn't exist
256
+ """
257
+ canvas_dir = get_canvas_directory_path(project_dir, canvas_name)
258
+ panel_dir = canvas_dir / "panels" / panel_name
259
+
260
+ # Remove from canvas.json
261
+ canvas_json = load_canvas_json(project_dir, canvas_name, verify_data_hashes=False)
262
+
263
+ original_count = len(canvas_json["panels"])
264
+ canvas_json["panels"] = [p for p in canvas_json["panels"] if p.get("name") != panel_name]
265
+
266
+ # Remove data file references for this panel
267
+ canvas_json["data_files"] = [
268
+ d for d in canvas_json.get("data_files", [])
269
+ if not d.get("path", "").startswith(f"panels/{panel_name}/")
270
+ ]
271
+
272
+ if len(canvas_json["panels"]) < original_count:
273
+ save_canvas_json(project_dir, canvas_name, canvas_json)
274
+
275
+ # Remove panel directory
276
+ if panel_dir.exists():
277
+ shutil.rmtree(panel_dir)
278
+
279
+ return True
280
+
281
+ return False
282
+
283
+
284
+ def update_panel(
285
+ project_dir: Union[str, Path],
286
+ canvas_name: str,
287
+ panel_name: str,
288
+ updates: Dict[str, Any],
289
+ ) -> Dict[str, Any]:
290
+ """
291
+ Update panel properties.
292
+
293
+ Parameters
294
+ ----------
295
+ project_dir : str or Path
296
+ Project root directory
297
+ canvas_name : str
298
+ Canvas name
299
+ panel_name : str
300
+ Name of panel to update
301
+ updates : Dict[str, Any]
302
+ Properties to update
303
+
304
+ Returns
305
+ -------
306
+ Dict[str, Any]
307
+ Updated panel entry
308
+
309
+ Raises
310
+ ------
311
+ ValueError
312
+ If panel not found
313
+ """
314
+ canvas_json = load_canvas_json(project_dir, canvas_name, verify_data_hashes=False)
315
+
316
+ # Find panel
317
+ panel_entry = None
318
+ for panel in canvas_json["panels"]:
319
+ if panel.get("name") == panel_name:
320
+ panel_entry = panel
321
+ break
322
+
323
+ if panel_entry is None:
324
+ raise ValueError(f"Panel not found: {panel_name}")
325
+
326
+ # Apply updates
327
+ _deep_merge(panel_entry, updates)
328
+
329
+ save_canvas_json(project_dir, canvas_name, canvas_json)
330
+
331
+ return panel_entry
332
+
333
+
334
+ def list_panels(
335
+ project_dir: Union[str, Path],
336
+ canvas_name: str,
337
+ ) -> List[Dict[str, Any]]:
338
+ """
339
+ List all panels in a canvas.
340
+
341
+ Parameters
342
+ ----------
343
+ project_dir : str or Path
344
+ Project root directory
345
+ canvas_name : str
346
+ Canvas name
347
+
348
+ Returns
349
+ -------
350
+ List[Dict[str, Any]]
351
+ List of panel entries
352
+ """
353
+ canvas_json = load_canvas_json(project_dir, canvas_name, verify_data_hashes=False)
354
+ return canvas_json.get("panels", [])
355
+
356
+
357
+ def get_panel(
358
+ project_dir: Union[str, Path],
359
+ canvas_name: str,
360
+ panel_name: str,
361
+ ) -> Optional[Dict[str, Any]]:
362
+ """
363
+ Get a specific panel by name.
364
+
365
+ Parameters
366
+ ----------
367
+ project_dir : str or Path
368
+ Project root directory
369
+ canvas_name : str
370
+ Canvas name
371
+ panel_name : str
372
+ Name of panel
373
+
374
+ Returns
375
+ -------
376
+ Optional[Dict[str, Any]]
377
+ Panel entry or None if not found
378
+ """
379
+ panels = list_panels(project_dir, canvas_name)
380
+ for panel in panels:
381
+ if panel.get("name") == panel_name:
382
+ return panel
383
+ return None
384
+
385
+
386
+ def reorder_panels(
387
+ project_dir: Union[str, Path],
388
+ canvas_name: str,
389
+ panel_order: List[str],
390
+ ) -> None:
391
+ """
392
+ Reorder panels by z_index based on provided order.
393
+
394
+ Parameters
395
+ ----------
396
+ project_dir : str or Path
397
+ Project root directory
398
+ canvas_name : str
399
+ Canvas name
400
+ panel_order : List[str]
401
+ List of panel names in desired z-order (first = bottom)
402
+ """
403
+ canvas_json = load_canvas_json(project_dir, canvas_name, verify_data_hashes=False)
404
+
405
+ # Update z_index based on order
406
+ for idx, panel_name in enumerate(panel_order):
407
+ for panel in canvas_json["panels"]:
408
+ if panel.get("name") == panel_name:
409
+ panel["z_index"] = idx
410
+ break
411
+
412
+ save_canvas_json(project_dir, canvas_name, canvas_json)
413
+
414
+
415
+ def _deep_merge(base: Dict, updates: Dict) -> None:
416
+ """Deep merge updates into base dictionary (in-place)."""
417
+ for key, value in updates.items():
418
+ if key in base and isinstance(base[key], dict) and isinstance(value, dict):
419
+ _deep_merge(base[key], value)
420
+ else:
421
+ base[key] = value
422
+
423
+
424
+ # EOF
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scitex
3
- Version: 2.4.2
3
+ Version: 2.5.0
4
4
  Summary: A comprehensive Python library for scientific computing and data analysis
5
5
  Project-URL: Homepage, https://github.com/ywatanabe1989/scitex-code
6
6
  Project-URL: Documentation, https://scitex.readthedocs.io
@@ -101,6 +101,7 @@ Requires-Dist: bitsandbytes; extra == 'all'
101
101
  Requires-Dist: bs4; extra == 'all'
102
102
  Requires-Dist: catboost; extra == 'all'
103
103
  Requires-Dist: crawl4ai; extra == 'all'
104
+ Requires-Dist: dearpygui; extra == 'all'
104
105
  Requires-Dist: einops; extra == 'all'
105
106
  Requires-Dist: fairscale; extra == 'all'
106
107
  Requires-Dist: fastapi; extra == 'all'
@@ -133,6 +134,7 @@ Requires-Dist: pyedflib; extra == 'all'
133
134
  Requires-Dist: pymed; extra == 'all'
134
135
  Requires-Dist: pymupdf; extra == 'all'
135
136
  Requires-Dist: pypdf2; extra == 'all'
137
+ Requires-Dist: pyqt6; extra == 'all'
136
138
  Requires-Dist: pytesseract; extra == 'all'
137
139
  Requires-Dist: python-docx; extra == 'all'
138
140
  Requires-Dist: pytorch-optimizer; extra == 'all'
@@ -206,6 +208,10 @@ Requires-Dist: torchaudio; extra == 'dl'
206
208
  Requires-Dist: torchsummary; extra == 'dl'
207
209
  Requires-Dist: torchvision; extra == 'dl'
208
210
  Requires-Dist: transformers; extra == 'dl'
211
+ Provides-Extra: gui
212
+ Requires-Dist: dearpygui; extra == 'gui'
213
+ Requires-Dist: flask; extra == 'gui'
214
+ Requires-Dist: pyqt6; extra == 'gui'
209
215
  Provides-Extra: jupyter
210
216
  Requires-Dist: ipdb; extra == 'jupyter'
211
217
  Requires-Dist: ipykernel; extra == 'jupyter'
@@ -296,7 +302,7 @@ Part of the fully open-source SciTeX project: https://scitex.ai
296
302
 
297
303
  ``` bash
298
304
  pip install scitex # ~600 MB, Core + utilities
299
- pip install scitex[dl,ml,jupyter,neuro,web,scholar,writer,dev] # ~2-5 GB, Complete toolkit
305
+ pip install scitex[dl,ml,jupyter,neuro,web,gui,scholar,writer,dev] # ~2-5 GB, Complete toolkit
300
306
  ```
301
307
 
302
308
  ### Alial
@@ -352,6 +358,7 @@ plt.close(fig)
352
358
  | **jupyter** | JupyterLab, papermill | ~100 MB |
353
359
  | **neuro** | MNE, obspy (EEG/MEG analysis) | ~200 MB |
354
360
  | **web** | FastAPI, Flask, Streamlit | ~50 MB |
361
+ | **gui** | Flask, DearPyGui, PyQt6 (multi-backend figure editors) | ~100 MB |
355
362
  | **scholar** | Selenium, PDF tools, paper management | ~150 MB |
356
363
  | **writer** | LaTeX compilation tools | ~10 MB |
357
364
  | **dev** | Testing, linting (dev only) | ~100 MB |