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.
- scitex/__version__.py +1 -1
- scitex/browser/__init__.py +53 -0
- scitex/browser/debugging/__init__.py +56 -0
- scitex/browser/debugging/_failure_capture.py +372 -0
- scitex/browser/debugging/_sync_session.py +259 -0
- scitex/browser/debugging/_test_monitor.py +284 -0
- scitex/browser/debugging/_visual_cursor.py +432 -0
- scitex/io/_load.py +5 -0
- scitex/io/_load_modules/_canvas.py +171 -0
- scitex/io/_save.py +8 -0
- scitex/io/_save_modules/_canvas.py +356 -0
- scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +77 -22
- scitex/plt/docs/FIGURE_ARCHITECTURE.md +257 -0
- scitex/plt/utils/__init__.py +10 -0
- scitex/plt/utils/_collect_figure_metadata.py +14 -12
- scitex/plt/utils/_csv_column_naming.py +237 -0
- scitex/scholar/citation_graph/database.py +9 -2
- scitex/scholar/config/ScholarConfig.py +23 -3
- scitex/scholar/config/default.yaml +55 -0
- scitex/scholar/core/Paper.py +102 -0
- scitex/scholar/core/__init__.py +44 -0
- scitex/scholar/core/journal_normalizer.py +524 -0
- scitex/scholar/core/oa_cache.py +285 -0
- scitex/scholar/core/open_access.py +457 -0
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +137 -0
- scitex/scholar/pdf_download/strategies/__init__.py +6 -0
- scitex/scholar/pdf_download/strategies/open_access_download.py +186 -0
- scitex/scholar/pipelines/ScholarPipelineSearchParallel.py +18 -3
- scitex/scholar/pipelines/ScholarPipelineSearchSingle.py +15 -2
- scitex/session/_decorator.py +13 -1
- scitex/vis/README.md +246 -615
- scitex/vis/__init__.py +138 -78
- scitex/vis/canvas.py +423 -0
- scitex/vis/docs/CANVAS_ARCHITECTURE.md +307 -0
- scitex/vis/editor/__init__.py +1 -1
- scitex/vis/editor/_dearpygui_editor.py +1830 -0
- scitex/vis/editor/_defaults.py +40 -1
- scitex/vis/editor/_edit.py +54 -18
- scitex/vis/editor/_flask_editor.py +37 -0
- scitex/vis/editor/_qt_editor.py +865 -0
- scitex/vis/editor/flask_editor/__init__.py +21 -0
- scitex/vis/editor/flask_editor/bbox.py +216 -0
- scitex/vis/editor/flask_editor/core.py +152 -0
- scitex/vis/editor/flask_editor/plotter.py +130 -0
- scitex/vis/editor/flask_editor/renderer.py +184 -0
- scitex/vis/editor/flask_editor/templates/__init__.py +33 -0
- scitex/vis/editor/flask_editor/templates/html.py +295 -0
- scitex/vis/editor/flask_editor/templates/scripts.py +614 -0
- scitex/vis/editor/flask_editor/templates/styles.py +549 -0
- scitex/vis/editor/flask_editor/utils.py +81 -0
- scitex/vis/io/__init__.py +84 -21
- scitex/vis/io/canvas.py +226 -0
- scitex/vis/io/data.py +204 -0
- scitex/vis/io/directory.py +202 -0
- scitex/vis/io/export.py +460 -0
- scitex/vis/io/panel.py +424 -0
- {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/METADATA +9 -2
- {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/RECORD +61 -32
- scitex/vis/DJANGO_INTEGRATION.md +0 -677
- scitex/vis/editor/_web_editor.py +0 -1440
- scitex/vis/tmp.txt +0 -239
- {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/WHEEL +0 -0
- {scitex-2.4.2.dist-info → scitex-2.5.0.dist-info}/entry_points.txt +0 -0
- {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
|
|
6
|
+
I/O operations for scitex.vis canvas management.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
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 .
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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 .
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
#
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
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
|
scitex/vis/io/canvas.py
ADDED
|
@@ -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
|