figpack 0.2.2__tar.gz → 0.2.4__tar.gz
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.
Potentially problematic release.
This version of figpack might be problematic. Click here for more details.
- {figpack-0.2.2/figpack.egg-info → figpack-0.2.4}/PKG-INFO +1 -1
- {figpack-0.2.2 → figpack-0.2.4}/figpack/__init__.py +1 -1
- {figpack-0.2.2 → figpack-0.2.4}/figpack/core/_server_manager.py +9 -3
- {figpack-0.2.2 → figpack-0.2.4}/figpack/core/_show_view.py +23 -5
- {figpack-0.2.2 → figpack-0.2.4}/figpack/core/_upload_bundle.py +21 -10
- {figpack-0.2.2 → figpack-0.2.4}/figpack/core/config.py +2 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/core/figpack_view.py +37 -3
- figpack-0.2.2/figpack/figpack-gui-dist/assets/index-DUR9Dmwh.js → figpack-0.2.4/figpack/figpack-gui-dist/assets/index-CuFseOGX.js +67 -67
- {figpack-0.2.2 → figpack-0.2.4}/figpack/figpack-gui-dist/index.html +1 -1
- figpack-0.2.4/figpack/spike_sorting/views/RasterPlot.py +77 -0
- figpack-0.2.4/figpack/spike_sorting/views/RasterPlotItem.py +28 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/__init__.py +4 -0
- {figpack-0.2.2 → figpack-0.2.4/figpack.egg-info}/PKG-INFO +1 -1
- {figpack-0.2.2 → figpack-0.2.4}/figpack.egg-info/SOURCES.txt +3 -1
- {figpack-0.2.2 → figpack-0.2.4}/pyproject.toml +3 -3
- figpack-0.2.4/tests/test_figpack_view.py +51 -0
- figpack-0.2.2/tests/test_figpack_view.py +0 -106
- {figpack-0.2.2 → figpack-0.2.4}/LICENSE +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/MANIFEST.in +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/README.md +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/cli.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/core/__init__.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/core/_bundle_utils.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/figpack-gui-dist/assets/index-Cmae55E4.css +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/figpack-gui-dist/assets/neurosift-logo-CLsuwLMO.png +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/franklab/__init__.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/franklab/views/TrackAnimation.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/franklab/views/__init__.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/__init__.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/AutocorrelogramItem.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/Autocorrelograms.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/AverageWaveforms.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/CrossCorrelogramItem.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/CrossCorrelograms.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/SpikeAmplitudes.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/SpikeAmplitudesItem.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/UnitSimilarityScore.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/UnitsTable.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/UnitsTableColumn.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/UnitsTableRow.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/Box.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/Image.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/LayoutItem.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/Markdown.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/MatplotlibFigure.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/MultiChannelTimeseries.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/PlotlyFigure.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/Splitter.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/TabLayout.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/TabLayoutItem.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/TimeseriesGraph.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack/views/__init__.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack.egg-info/dependency_links.txt +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack.egg-info/entry_points.txt +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack.egg-info/requires.txt +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/figpack.egg-info/top_level.txt +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/setup.cfg +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_average_waveforms.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_box.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_cli.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_core.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_image.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_markdown.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_matplotlib_figure.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_multichannel_timeseries.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_plotly_figure.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_show_view.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_spike_sorting_correlograms.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_splitter.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_tablayout.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_timeseries_graph.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_track_animation.py +0 -0
- {figpack-0.2.2 → figpack-0.2.4}/tests/test_units_table.py +0 -0
|
@@ -138,11 +138,17 @@ class ProcessServerManager:
|
|
|
138
138
|
self._create_process_info_file()
|
|
139
139
|
return self._temp_dir
|
|
140
140
|
|
|
141
|
-
def create_figure_subdir(
|
|
141
|
+
def create_figure_subdir(
|
|
142
|
+
self, *, _local_figure_name: Optional[str] = None
|
|
143
|
+
) -> pathlib.Path:
|
|
142
144
|
"""Create a unique subdirectory for a figure within the process temp dir."""
|
|
143
145
|
temp_dir = self.get_temp_dir()
|
|
144
|
-
|
|
145
|
-
|
|
146
|
+
local_figure_name = (
|
|
147
|
+
"figure_" + str(uuid.uuid4())[:8]
|
|
148
|
+
if _local_figure_name is None
|
|
149
|
+
else _local_figure_name
|
|
150
|
+
)
|
|
151
|
+
figure_dir = temp_dir / f"{local_figure_name}"
|
|
146
152
|
figure_dir.mkdir(exist_ok=True)
|
|
147
153
|
return figure_dir
|
|
148
154
|
|
|
@@ -38,6 +38,19 @@ def _is_in_notebook() -> bool:
|
|
|
38
38
|
return False
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
def _is_in_colab():
|
|
42
|
+
try:
|
|
43
|
+
import google.colab # type: ignore
|
|
44
|
+
|
|
45
|
+
return True
|
|
46
|
+
except ImportError:
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _is_in_jupyterhub():
|
|
51
|
+
return "JUPYTERHUB_USER" in os.environ
|
|
52
|
+
|
|
53
|
+
|
|
41
54
|
def _display_inline_iframe(url: str, height: int) -> None:
|
|
42
55
|
"""
|
|
43
56
|
Display an iframe inline in a Jupyter notebook.
|
|
@@ -75,10 +88,12 @@ def _show_view(
|
|
|
75
88
|
port: Union[int, None] = None,
|
|
76
89
|
allow_origin: Union[str, None] = None,
|
|
77
90
|
upload: bool = False,
|
|
91
|
+
ephemeral: bool = False,
|
|
78
92
|
title: Union[str, None] = None,
|
|
79
93
|
description: Union[str, None] = None,
|
|
80
94
|
inline: Union[bool, None] = None,
|
|
81
95
|
inline_height: int = 600,
|
|
96
|
+
_local_figure_name: Union[str, None] = None,
|
|
82
97
|
):
|
|
83
98
|
# Determine if we should use inline display
|
|
84
99
|
use_inline = inline
|
|
@@ -91,16 +106,17 @@ def _show_view(
|
|
|
91
106
|
with tempfile.TemporaryDirectory(prefix="figpack_upload_") as tmpdir:
|
|
92
107
|
prepare_figure_bundle(view, tmpdir, title=title, description=description)
|
|
93
108
|
|
|
94
|
-
# Check for required
|
|
109
|
+
# Check for API key - required for regular uploads, optional for ephemeral
|
|
95
110
|
api_key = os.environ.get("FIGPACK_API_KEY")
|
|
96
|
-
if not api_key:
|
|
111
|
+
if not ephemeral and not api_key:
|
|
97
112
|
raise EnvironmentError(
|
|
98
113
|
"FIGPACK_API_KEY environment variable must be set to upload views."
|
|
99
114
|
)
|
|
100
115
|
|
|
101
116
|
# Upload the bundle
|
|
102
|
-
|
|
103
|
-
|
|
117
|
+
figure_url = _upload_bundle(
|
|
118
|
+
tmpdir, api_key, title=title, ephemeral=ephemeral
|
|
119
|
+
)
|
|
104
120
|
|
|
105
121
|
if use_inline:
|
|
106
122
|
# For uploaded figures, display the remote URL inline and continue
|
|
@@ -121,7 +137,9 @@ def _show_view(
|
|
|
121
137
|
server_manager = ProcessServerManager.get_instance()
|
|
122
138
|
|
|
123
139
|
# Create figure subdirectory in process temp directory
|
|
124
|
-
figure_dir = server_manager.create_figure_subdir(
|
|
140
|
+
figure_dir = server_manager.create_figure_subdir(
|
|
141
|
+
_local_figure_name=_local_figure_name
|
|
142
|
+
)
|
|
125
143
|
|
|
126
144
|
# Prepare the figure bundle in the subdirectory
|
|
127
145
|
prepare_figure_bundle(
|
|
@@ -11,7 +11,7 @@ import requests
|
|
|
11
11
|
|
|
12
12
|
from .. import __version__
|
|
13
13
|
|
|
14
|
-
from .config import FIGPACK_API_BASE_URL
|
|
14
|
+
from .config import FIGPACK_API_BASE_URL, FIGPACK_BUCKET
|
|
15
15
|
|
|
16
16
|
thisdir = pathlib.Path(__file__).parent.resolve()
|
|
17
17
|
|
|
@@ -133,33 +133,46 @@ def _create_or_get_figure(
|
|
|
133
133
|
total_files: int = None,
|
|
134
134
|
total_size: int = None,
|
|
135
135
|
title: str = None,
|
|
136
|
+
ephemeral: bool = False,
|
|
136
137
|
) -> dict:
|
|
137
138
|
"""
|
|
138
139
|
Create a new figure or get existing figure information
|
|
139
140
|
|
|
140
141
|
Args:
|
|
141
142
|
figure_hash: The hash of the figure
|
|
142
|
-
api_key: The API key for authentication
|
|
143
|
+
api_key: The API key for authentication (required for non-ephemeral)
|
|
143
144
|
total_files: Optional total number of files
|
|
144
145
|
total_size: Optional total size of files
|
|
145
146
|
title: Optional title for the figure
|
|
147
|
+
ephemeral: Whether to create an ephemeral figure
|
|
146
148
|
|
|
147
149
|
Returns:
|
|
148
150
|
dict: Figure information from the API
|
|
149
151
|
"""
|
|
152
|
+
# Validate API key requirement
|
|
153
|
+
if not ephemeral and api_key is None:
|
|
154
|
+
raise ValueError("API key is required for non-ephemeral figures")
|
|
155
|
+
|
|
150
156
|
payload = {
|
|
151
157
|
"figureHash": figure_hash,
|
|
152
|
-
"apiKey": api_key,
|
|
153
158
|
"figpackVersion": __version__,
|
|
159
|
+
"bucket": FIGPACK_BUCKET,
|
|
154
160
|
}
|
|
155
161
|
|
|
162
|
+
# API key is optional for ephemeral figures
|
|
163
|
+
if api_key is not None:
|
|
164
|
+
payload["apiKey"] = api_key
|
|
165
|
+
|
|
156
166
|
if total_files is not None:
|
|
157
167
|
payload["totalFiles"] = total_files
|
|
158
168
|
if total_size is not None:
|
|
159
169
|
payload["totalSize"] = total_size
|
|
160
170
|
if title is not None:
|
|
161
171
|
payload["title"] = title
|
|
172
|
+
if ephemeral:
|
|
173
|
+
payload["ephemeral"] = True
|
|
162
174
|
|
|
175
|
+
# Use the same endpoint for both regular and ephemeral figures
|
|
163
176
|
response = requests.post(f"{FIGPACK_API_BASE_URL}/api/figures/create", json=payload)
|
|
164
177
|
|
|
165
178
|
if not response.ok:
|
|
@@ -212,16 +225,16 @@ def _finalize_figure(figure_url: str, api_key: str) -> dict:
|
|
|
212
225
|
return response_data
|
|
213
226
|
|
|
214
227
|
|
|
215
|
-
def _upload_bundle(
|
|
228
|
+
def _upload_bundle(
|
|
229
|
+
tmpdir: str, api_key: str, title: str = None, ephemeral: bool = False
|
|
230
|
+
) -> str:
|
|
216
231
|
"""
|
|
217
232
|
Upload the prepared bundle to the cloud using the new database-driven approach
|
|
218
233
|
"""
|
|
219
234
|
tmpdir_path = pathlib.Path(tmpdir)
|
|
220
235
|
|
|
221
236
|
# Compute deterministic figure ID based on file contents
|
|
222
|
-
print("Computing deterministic figure ID...")
|
|
223
237
|
figure_hash = _compute_deterministic_figure_hash(tmpdir_path)
|
|
224
|
-
print(f"Figure hash: {figure_hash}")
|
|
225
238
|
|
|
226
239
|
# Collect all files to upload
|
|
227
240
|
all_files = []
|
|
@@ -239,17 +252,15 @@ def _upload_bundle(tmpdir: str, api_key: str, title: str = None) -> str:
|
|
|
239
252
|
|
|
240
253
|
# Find available figure ID and create/get figure in database with metadata
|
|
241
254
|
result = _create_or_get_figure(
|
|
242
|
-
figure_hash, api_key, total_files, total_size, title=title
|
|
255
|
+
figure_hash, api_key, total_files, total_size, title=title, ephemeral=ephemeral
|
|
243
256
|
)
|
|
244
257
|
figure_info = result.get("figure", {})
|
|
245
258
|
figure_url = figure_info.get("figureUrl")
|
|
246
259
|
|
|
247
260
|
if figure_info["status"] == "completed":
|
|
248
|
-
print(f"Figure already exists
|
|
261
|
+
print(f"Figure already exists. No upload needed.")
|
|
249
262
|
return figure_url
|
|
250
263
|
|
|
251
|
-
print(f"Using figure URL: {figure_url}")
|
|
252
|
-
|
|
253
264
|
files_to_upload = all_files
|
|
254
265
|
total_files_to_upload = len(files_to_upload)
|
|
255
266
|
|
|
@@ -18,7 +18,8 @@ class FigpackView:
|
|
|
18
18
|
port: Union[int, None] = None,
|
|
19
19
|
open_in_browser: bool = False,
|
|
20
20
|
allow_origin: Union[str, None] = None,
|
|
21
|
-
upload: bool =
|
|
21
|
+
upload: Union[bool, None] = None,
|
|
22
|
+
ephemeral: Union[bool, None] = None,
|
|
22
23
|
_dev: bool = False,
|
|
23
24
|
title: Union[str, None] = None,
|
|
24
25
|
description: Union[str, None] = None,
|
|
@@ -33,13 +34,37 @@ class FigpackView:
|
|
|
33
34
|
open_in_browser: Whether to open in browser automatically
|
|
34
35
|
allow_origin: CORS allow origin header
|
|
35
36
|
upload: Whether to upload the figure
|
|
37
|
+
ephemeral: Whether to upload as ephemeral figure (None=auto-detect, True=force ephemeral, False=force regular)
|
|
36
38
|
_dev: Development mode flag
|
|
37
39
|
title: Title for the browser tab and figure
|
|
38
40
|
description: Description text (markdown supported) for the figure
|
|
39
41
|
inline: Whether to display inline in notebook (None=auto-detect, True=force inline, False=force browser)
|
|
40
42
|
inline_height: Height in pixels for inline iframe display (default: 600)
|
|
41
43
|
"""
|
|
42
|
-
from ._show_view import
|
|
44
|
+
from ._show_view import (
|
|
45
|
+
_show_view,
|
|
46
|
+
_is_in_notebook,
|
|
47
|
+
_is_in_colab,
|
|
48
|
+
_is_in_jupyterhub,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if ephemeral is None and upload is None:
|
|
52
|
+
# If we haven't specified both, then let's check if we're in a notebook in a non-local environment
|
|
53
|
+
if _is_in_notebook():
|
|
54
|
+
if _is_in_colab():
|
|
55
|
+
# if we are in a notebook and in colab, we should show as uploaded ephemeral
|
|
56
|
+
print("Detected Google Colab notebook environment.")
|
|
57
|
+
upload = True
|
|
58
|
+
ephemeral = True
|
|
59
|
+
if _is_in_jupyterhub():
|
|
60
|
+
# if we are in a notebook and in jupyterhub, we should show as uploaded ephemeral
|
|
61
|
+
print("Detected JupyterHub notebook environment.")
|
|
62
|
+
upload = True
|
|
63
|
+
ephemeral = True
|
|
64
|
+
|
|
65
|
+
# Validate ephemeral parameter
|
|
66
|
+
if ephemeral and not upload:
|
|
67
|
+
raise ValueError("ephemeral=True requires upload=True to be set")
|
|
43
68
|
|
|
44
69
|
if _dev:
|
|
45
70
|
if port is None:
|
|
@@ -50,8 +75,15 @@ class FigpackView:
|
|
|
50
75
|
if upload:
|
|
51
76
|
raise ValueError("Cannot upload when _dev is True.")
|
|
52
77
|
|
|
78
|
+
# make a random figure name
|
|
79
|
+
import random
|
|
80
|
+
import string
|
|
81
|
+
|
|
82
|
+
_local_figure_name = "fig_" + "".join(
|
|
83
|
+
random.choices(string.ascii_lowercase + string.digits, k=8)
|
|
84
|
+
)
|
|
53
85
|
print(
|
|
54
|
-
f"For development, run figpack-gui in dev mode and use http://localhost:5173?data=http://localhost:{port}/data.zarr"
|
|
86
|
+
f"For development, run figpack-gui in dev mode and use http://localhost:5173?data=http://localhost:{port}/{_local_figure_name}/data.zarr"
|
|
55
87
|
)
|
|
56
88
|
open_in_browser = False
|
|
57
89
|
|
|
@@ -61,10 +93,12 @@ class FigpackView:
|
|
|
61
93
|
open_in_browser=open_in_browser,
|
|
62
94
|
allow_origin=allow_origin,
|
|
63
95
|
upload=upload,
|
|
96
|
+
ephemeral=ephemeral,
|
|
64
97
|
title=title,
|
|
65
98
|
description=description,
|
|
66
99
|
inline=inline,
|
|
67
100
|
inline_height=inline_height,
|
|
101
|
+
_local_figure_name=_local_figure_name if _dev else None,
|
|
68
102
|
)
|
|
69
103
|
|
|
70
104
|
def _write_to_zarr_group(self, group: zarr.Group) -> None:
|