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
@@ -1,677 +0,0 @@
1
- # Django Integration Guide for scitex.vis
2
-
3
- **Integration with `/vis/sigma/` Django Application**
4
-
5
- ---
6
-
7
- ## Overview
8
-
9
- This guide explains how to integrate `scitex.vis` with the Django-based `/vis/sigma/` application for web-based figure editing and management.
10
-
11
- ## Architecture
12
-
13
- ```
14
- ┌─────────────────────────────────────────────┐
15
- │ Frontend (React/Vue) │
16
- │ - Tree view of figure structure │
17
- │ - Canvas for visual editing │
18
- │ - Form controls for plot parameters │
19
- └────────────┬────────────────────────────────┘
20
- │ HTTP/REST API
21
- ┌────────────▼────────────────────────────────┐
22
- │ Django Backend (/vis/sigma/) │
23
- │ - Figure CRUD endpoints │
24
- │ - Export endpoints │
25
- │ - Project management │
26
- └────────────┬────────────────────────────────┘
27
- │ Python API
28
- ┌────────────▼────────────────────────────────┐
29
- │ scitex.vis │
30
- │ - JSON validation │
31
- │ - Figure rendering │
32
- │ - Export to PNG/PDF/SVG │
33
- └─────────────────────────────────────────────┘
34
- ```
35
-
36
- ---
37
-
38
- ## Required Django API Endpoints
39
-
40
- ### 1. Figure Management
41
-
42
- #### List Figures in Project
43
-
44
- ```python
45
- # GET /api/vis/figures/
46
- def list_figures(request):
47
- """List all figures in the project."""
48
- import scitex as stx
49
-
50
- project_dir = get_project_dir(request) # Your implementation
51
-
52
- figure_ids = stx.vis.io.list_figures_in_project(project_dir)
53
-
54
- return JsonResponse({
55
- 'figures': [
56
- {
57
- 'id': fig_id,
58
- 'path': f'{project_dir}/scitex/vis/figs/{fig_id}.json'
59
- }
60
- for fig_id in figure_ids
61
- ]
62
- })
63
- ```
64
-
65
- #### Get Figure JSON
66
-
67
- ```python
68
- # GET /api/vis/figures/{figure_id}/
69
- def get_figure(request, figure_id):
70
- """Get figure JSON by ID."""
71
- import scitex as stx
72
-
73
- project_dir = get_project_dir(request)
74
-
75
- try:
76
- fig_json = stx.vis.load_figure_json_from_project(
77
- project_dir=project_dir,
78
- figure_id=figure_id
79
- )
80
- return JsonResponse(fig_json)
81
-
82
- except FileNotFoundError:
83
- return JsonResponse(
84
- {'error': f'Figure {figure_id} not found'},
85
- status=404
86
- )
87
- ```
88
-
89
- #### Create/Update Figure
90
-
91
- ```python
92
- # POST /api/vis/figures/{figure_id}/
93
- def save_figure(request, figure_id):
94
- """Save or update figure JSON."""
95
- import scitex as stx
96
- from scitex.vis.backend import validate_figure_json
97
-
98
- project_dir = get_project_dir(request)
99
- fig_json = json.loads(request.body)
100
-
101
- # Validate before saving
102
- try:
103
- validate_figure_json(fig_json)
104
- except ValueError as e:
105
- return JsonResponse(
106
- {'error': f'Invalid figure JSON: {e}'},
107
- status=400
108
- )
109
-
110
- # Save to project
111
- path = stx.vis.save_figure_json_to_project(
112
- project_dir=project_dir,
113
- figure_id=figure_id,
114
- fig_json=fig_json
115
- )
116
-
117
- return JsonResponse({
118
- 'success': True,
119
- 'figure_id': figure_id,
120
- 'path': str(path)
121
- })
122
- ```
123
-
124
- #### Delete Figure
125
-
126
- ```python
127
- # DELETE /api/vis/figures/{figure_id}/
128
- def delete_figure(request, figure_id):
129
- """Delete figure JSON."""
130
- project_dir = get_project_dir(request)
131
- json_path = Path(project_dir) / 'scitex' / 'vis' / 'figs' / f'{figure_id}.json'
132
-
133
- if json_path.exists():
134
- json_path.unlink()
135
- return JsonResponse({'success': True})
136
- else:
137
- return JsonResponse(
138
- {'error': f'Figure {figure_id} not found'},
139
- status=404
140
- )
141
- ```
142
-
143
- ### 2. Export Endpoints
144
-
145
- #### Export Figure to Image
146
-
147
- ```python
148
- # GET /api/vis/figures/{figure_id}/export?format=png&dpi=300
149
- def export_figure(request, figure_id):
150
- """Export figure to image format."""
151
- import scitex as stx
152
-
153
- project_dir = get_project_dir(request)
154
-
155
- # Get parameters
156
- fmt = request.GET.get('format', 'png') # png, pdf, svg
157
- dpi = int(request.GET.get('dpi', 300))
158
- auto_crop = request.GET.get('auto_crop', 'false').lower() == 'true'
159
-
160
- # Load figure JSON
161
- try:
162
- fig_json = stx.vis.load_figure_json_from_project(
163
- project_dir=project_dir,
164
- figure_id=figure_id
165
- )
166
- except FileNotFoundError:
167
- return JsonResponse(
168
- {'error': f'Figure {figure_id} not found'},
169
- status=404
170
- )
171
-
172
- # Export to temporary file
173
- import tempfile
174
- with tempfile.NamedTemporaryFile(
175
- suffix=f'.{fmt}',
176
- delete=False
177
- ) as tmp:
178
- tmp_path = tmp.name
179
-
180
- try:
181
- stx.vis.export_figure(
182
- fig_json=fig_json,
183
- output_path=tmp_path,
184
- fmt=fmt,
185
- dpi=dpi,
186
- auto_crop=auto_crop
187
- )
188
-
189
- # Return file
190
- with open(tmp_path, 'rb') as f:
191
- response = HttpResponse(
192
- f.read(),
193
- content_type=f'image/{fmt}' if fmt in ['png', 'svg'] else 'application/pdf'
194
- )
195
- response['Content-Disposition'] = f'attachment; filename="{figure_id}.{fmt}"'
196
- return response
197
-
198
- finally:
199
- # Clean up temp file
200
- if os.path.exists(tmp_path):
201
- os.unlink(tmp_path)
202
- ```
203
-
204
- #### Export Multiple Formats
205
-
206
- ```python
207
- # POST /api/vis/figures/{figure_id}/export-multi/
208
- def export_multiple_formats(request, figure_id):
209
- """Export figure to multiple formats simultaneously."""
210
- import scitex as stx
211
-
212
- project_dir = get_project_dir(request)
213
- formats = request.POST.getlist('formats', ['png', 'pdf', 'svg'])
214
-
215
- # Load figure
216
- fig_json = stx.vis.load_figure_json_from_project(
217
- project_dir=project_dir,
218
- figure_id=figure_id
219
- )
220
-
221
- # Export to project export directory
222
- export_dir = Path(project_dir) / 'scitex' / 'vis' / 'export'
223
-
224
- paths = stx.vis.backend.export_multiple_formats(
225
- fig_json=fig_json,
226
- output_dir=export_dir,
227
- base_name=figure_id,
228
- formats=formats,
229
- dpi=300,
230
- auto_crop=True
231
- )
232
-
233
- return JsonResponse({
234
- 'success': True,
235
- 'files': {fmt: str(path) for fmt, path in paths.items()}
236
- })
237
- ```
238
-
239
- ### 3. Template Endpoints
240
-
241
- #### List Available Templates
242
-
243
- ```python
244
- # GET /api/vis/templates/
245
- def list_templates(request):
246
- """List available figure templates."""
247
- import scitex as stx
248
-
249
- templates = stx.vis.list_templates()
250
-
251
- template_info = []
252
- for name in templates:
253
- template = stx.vis.get_template(name)
254
- template_info.append({
255
- 'name': name,
256
- 'width_mm': template['width_mm'],
257
- 'height_mm': template['height_mm'],
258
- 'description': template.get('metadata', {}).get('template', name)
259
- })
260
-
261
- return JsonResponse({'templates': template_info})
262
- ```
263
-
264
- #### Get Template
265
-
266
- ```python
267
- # GET /api/vis/templates/{template_name}/
268
- def get_template(request, template_name):
269
- """Get a specific template."""
270
- import scitex as stx
271
-
272
- try:
273
- # Get optional parameters
274
- height_mm = request.GET.get('height_mm')
275
- nrows = request.GET.get('nrows')
276
- ncols = request.GET.get('ncols')
277
-
278
- kwargs = {}
279
- if height_mm:
280
- kwargs['height_mm'] = float(height_mm)
281
- if nrows:
282
- kwargs['nrows'] = int(nrows)
283
- if ncols:
284
- kwargs['ncols'] = int(ncols)
285
-
286
- template = stx.vis.get_template(template_name, **kwargs)
287
- return JsonResponse(template)
288
-
289
- except ValueError as e:
290
- return JsonResponse(
291
- {'error': f'Unknown template: {template_name}'},
292
- status=404
293
- )
294
- ```
295
-
296
- ### 4. Validation Endpoint
297
-
298
- ```python
299
- # POST /api/vis/validate/
300
- def validate_figure_json(request):
301
- """Validate figure JSON without saving."""
302
- from scitex.vis.backend import validate_figure_json
303
-
304
- fig_json = json.loads(request.body)
305
-
306
- try:
307
- validate_figure_json(fig_json)
308
- return JsonResponse({
309
- 'valid': True,
310
- 'message': 'Figure JSON is valid'
311
- })
312
-
313
- except ValueError as e:
314
- return JsonResponse({
315
- 'valid': False,
316
- 'error': str(e)
317
- }, status=400)
318
- ```
319
-
320
- ---
321
-
322
- ## URL Configuration
323
-
324
- ```python
325
- # urls.py
326
- from django.urls import path
327
- from . import views
328
-
329
- urlpatterns = [
330
- # Figure management
331
- path('api/vis/figures/', views.list_figures, name='list_figures'),
332
- path('api/vis/figures/<str:figure_id>/', views.get_figure, name='get_figure'),
333
- path('api/vis/figures/<str:figure_id>/', views.save_figure, name='save_figure'),
334
- path('api/vis/figures/<str:figure_id>/', views.delete_figure, name='delete_figure'),
335
-
336
- # Export
337
- path('api/vis/figures/<str:figure_id>/export/', views.export_figure, name='export_figure'),
338
- path('api/vis/figures/<str:figure_id>/export-multi/', views.export_multiple_formats, name='export_multiple'),
339
-
340
- # Templates
341
- path('api/vis/templates/', views.list_templates, name='list_templates'),
342
- path('api/vis/templates/<str:template_name>/', views.get_template, name='get_template'),
343
-
344
- # Validation
345
- path('api/vis/validate/', views.validate_figure_json, name='validate'),
346
- ]
347
- ```
348
-
349
- ---
350
-
351
- ## Frontend Integration
352
-
353
- ### Loading CSV Data for Plots
354
-
355
- When the frontend needs to create a plot, it should:
356
-
357
- 1. **Upload CSV** to Django backend
358
- 2. **Parse CSV** to extract x, y data
359
- 3. **Convert to JSON arrays** for plot data
360
- 4. **Send to scitex.vis** as part of figure JSON
361
-
362
- ```python
363
- # POST /api/vis/data/upload/
364
- def upload_plot_data(request):
365
- """Upload CSV and convert to plot data."""
366
- import pandas as pd
367
- import io
368
-
369
- csv_file = request.FILES['file']
370
- x_column = request.POST.get('x_column', 0)
371
- y_column = request.POST.get('y_column', 1)
372
-
373
- # Read CSV
374
- df = pd.read_csv(io.BytesIO(csv_file.read()))
375
-
376
- # Extract columns
377
- x_data = df.iloc[:, x_column].tolist()
378
- y_data = df.iloc[:, y_column].tolist()
379
-
380
- return JsonResponse({
381
- 'data': {
382
- 'x': x_data,
383
- 'y': y_data
384
- },
385
- 'columns': df.columns.tolist()
386
- })
387
- ```
388
-
389
- ### Frontend Workflow
390
-
391
- 1. **Select Template**
392
- ```javascript
393
- const template = await fetch('/api/vis/templates/nature_single/').then(r => r.json());
394
- ```
395
-
396
- 2. **Upload Data**
397
- ```javascript
398
- const formData = new FormData();
399
- formData.append('file', csvFile);
400
- formData.append('x_column', 0);
401
- formData.append('y_column', 1);
402
-
403
- const plotData = await fetch('/api/vis/data/upload/', {
404
- method: 'POST',
405
- body: formData
406
- }).then(r => r.json());
407
- ```
408
-
409
- 3. **Build Figure JSON**
410
- ```javascript
411
- const figJson = {
412
- ...template,
413
- axes: [{
414
- row: 0,
415
- col: 0,
416
- xlabel: "Time (s)",
417
- ylabel: "Amplitude",
418
- plots: [{
419
- plot_type: "line",
420
- data: plotData.data,
421
- color: "blue",
422
- linewidth: 2
423
- }]
424
- }]
425
- };
426
- ```
427
-
428
- 4. **Save Figure**
429
- ```javascript
430
- await fetch('/api/vis/figures/fig-001/', {
431
- method: 'POST',
432
- headers: {'Content-Type': 'application/json'},
433
- body: JSON.stringify(figJson)
434
- });
435
- ```
436
-
437
- 5. **Export**
438
- ```javascript
439
- const imageBlob = await fetch(
440
- '/api/vis/figures/fig-001/export?format=png&dpi=300'
441
- ).then(r => r.blob());
442
- ```
443
-
444
- ---
445
-
446
- ## Project Directory Structure
447
-
448
- ```
449
- project/
450
- ├── scitex/
451
- │ └── vis/
452
- │ ├── figs/ # Figure JSON specifications
453
- │ │ ├── fig-001.json
454
- │ │ ├── fig-002.json
455
- │ │ └── ...
456
- │ ├── export/ # Exported images
457
- │ │ ├── fig-001.png
458
- │ │ ├── fig-001.pdf
459
- │ │ └── ...
460
- │ └── data/ # Optional: CSV data files
461
- │ ├── dataset-01.csv
462
- │ └── ...
463
- └── ...
464
- ```
465
-
466
- ---
467
-
468
- ## Security Considerations
469
-
470
- ### 1. Input Validation
471
-
472
- Always validate figure JSON before processing:
473
-
474
- ```python
475
- from scitex.vis.backend import validate_figure_json
476
-
477
- try:
478
- validate_figure_json(fig_json)
479
- except ValueError as e:
480
- return JsonResponse({'error': str(e)}, status=400)
481
- ```
482
-
483
- ### 2. File Path Sanitization
484
-
485
- Prevent directory traversal attacks:
486
-
487
- ```python
488
- import os
489
- from pathlib import Path
490
-
491
- def get_safe_figure_path(project_dir, figure_id):
492
- """Get sanitized figure path."""
493
- # Remove any path components
494
- safe_id = Path(figure_id).name
495
-
496
- # Ensure .json extension
497
- if not safe_id.endswith('.json'):
498
- safe_id = f'{safe_id}.json'
499
-
500
- path = Path(project_dir) / 'scitex' / 'vis' / 'figs' / safe_id
501
-
502
- # Ensure path is within project directory
503
- if not str(path.resolve()).startswith(str(Path(project_dir).resolve())):
504
- raise ValueError('Invalid figure ID')
505
-
506
- return path
507
- ```
508
-
509
- ### 3. Rate Limiting
510
-
511
- Apply rate limiting to export endpoints:
512
-
513
- ```python
514
- from django.views.decorators.cache import cache_page
515
- from django.views.decorators.ratelimit import ratelimit
516
-
517
- @ratelimit(key='user', rate='10/m', method='GET')
518
- def export_figure(request, figure_id):
519
- # ... export logic
520
- ```
521
-
522
- ---
523
-
524
- ## Performance Optimization
525
-
526
- ### 1. Caching
527
-
528
- Cache rendered figures:
529
-
530
- ```python
531
- from django.core.cache import cache
532
-
533
- def export_figure(request, figure_id):
534
- cache_key = f'figure_{figure_id}_{fmt}_{dpi}'
535
-
536
- cached = cache.get(cache_key)
537
- if cached:
538
- return cached
539
-
540
- # Render and cache
541
- response = render_and_export(fig_json, fmt, dpi)
542
- cache.set(cache_key, response, timeout=3600) # 1 hour
543
- return response
544
- ```
545
-
546
- ### 2. Background Tasks
547
-
548
- Use Celery for slow export operations:
549
-
550
- ```python
551
- from celery import shared_task
552
-
553
- @shared_task
554
- def export_figure_task(project_dir, figure_id, fmt, dpi):
555
- """Background task for figure export."""
556
- import scitex as stx
557
-
558
- fig_json = stx.vis.load_figure_json_from_project(project_dir, figure_id)
559
- output_path = Path(project_dir) / 'scitex' / 'vis' / 'export' / f'{figure_id}.{fmt}'
560
-
561
- stx.vis.export_figure(fig_json, output_path, fmt=fmt, dpi=dpi)
562
-
563
- return str(output_path)
564
-
565
- # In view:
566
- def export_figure(request, figure_id):
567
- task = export_figure_task.delay(project_dir, figure_id, fmt, dpi)
568
- return JsonResponse({'task_id': task.id})
569
- ```
570
-
571
- ---
572
-
573
- ## Testing
574
-
575
- ### Unit Tests
576
-
577
- ```python
578
- from django.test import TestCase
579
- import scitex as stx
580
-
581
- class VisFigureTests(TestCase):
582
- def test_save_and_load_figure(self):
583
- """Test figure save/load cycle."""
584
- fig_json = stx.vis.get_template('square')
585
-
586
- # Save
587
- path = stx.vis.save_figure_json_to_project(
588
- '/tmp/test_project',
589
- 'test-fig',
590
- fig_json
591
- )
592
-
593
- # Load
594
- loaded = stx.vis.load_figure_json_from_project(
595
- '/tmp/test_project',
596
- 'test-fig'
597
- )
598
-
599
- self.assertEqual(fig_json['width_mm'], loaded['width_mm'])
600
-
601
- def test_export_figure(self):
602
- """Test figure export."""
603
- fig_json = stx.vis.get_template('square')
604
- fig_json['axes'] = [{
605
- 'plots': [{
606
- 'plot_type': 'line',
607
- 'data': {'x': [0, 1, 2], 'y': [0, 1, 4]}
608
- }]
609
- }]
610
-
611
- output = '/tmp/test.png'
612
- stx.vis.export_figure(fig_json, output, dpi=150)
613
-
614
- self.assertTrue(Path(output).exists())
615
- ```
616
-
617
- ### Integration Tests
618
-
619
- ```python
620
- class VisAPITests(TestCase):
621
- def test_list_figures(self):
622
- """Test figure listing endpoint."""
623
- response = self.client.get('/api/vis/figures/')
624
- self.assertEqual(response.status_code, 200)
625
- self.assertIn('figures', response.json())
626
-
627
- def test_save_figure(self):
628
- """Test figure save endpoint."""
629
- fig_json = stx.vis.get_template('square')
630
-
631
- response = self.client.post(
632
- '/api/vis/figures/test-fig/',
633
- data=json.dumps(fig_json),
634
- content_type='application/json'
635
- )
636
-
637
- self.assertEqual(response.status_code, 200)
638
- self.assertTrue(response.json()['success'])
639
- ```
640
-
641
- ---
642
-
643
- ## Troubleshooting
644
-
645
- ### Common Issues
646
-
647
- 1. **Figure not rendering**
648
- - Check if scitex.plt is properly installed
649
- - Verify matplotlib backend is available
650
- - Check for missing data in plot configurations
651
-
652
- 2. **Validation errors**
653
- - Use `/api/vis/validate/` endpoint to check JSON
654
- - Review error messages for specific field issues
655
- - Ensure all required fields are present
656
-
657
- 3. **Export failures**
658
- - Check disk space for export directory
659
- - Verify write permissions
660
- - Check matplotlib/scitex.plt configuration
661
-
662
- ---
663
-
664
- ## Next Steps
665
-
666
- 1. **Implement Django views** using the examples above
667
- 2. **Create URL patterns** for all endpoints
668
- 3. **Build frontend components** for:
669
- - Figure tree viewer
670
- - Visual canvas editor
671
- - Form controls for plot parameters
672
- 4. **Test integration** with sample figures
673
- 5. **Deploy** to production environment
674
-
675
- ---
676
-
677
- **Integration complete! Ready to connect `/vis/sigma/` Django app with `scitex.vis`** 🚀