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.

Files changed (73) hide show
  1. {figpack-0.2.2/figpack.egg-info → figpack-0.2.4}/PKG-INFO +1 -1
  2. {figpack-0.2.2 → figpack-0.2.4}/figpack/__init__.py +1 -1
  3. {figpack-0.2.2 → figpack-0.2.4}/figpack/core/_server_manager.py +9 -3
  4. {figpack-0.2.2 → figpack-0.2.4}/figpack/core/_show_view.py +23 -5
  5. {figpack-0.2.2 → figpack-0.2.4}/figpack/core/_upload_bundle.py +21 -10
  6. {figpack-0.2.2 → figpack-0.2.4}/figpack/core/config.py +2 -0
  7. {figpack-0.2.2 → figpack-0.2.4}/figpack/core/figpack_view.py +37 -3
  8. 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
  9. {figpack-0.2.2 → figpack-0.2.4}/figpack/figpack-gui-dist/index.html +1 -1
  10. figpack-0.2.4/figpack/spike_sorting/views/RasterPlot.py +77 -0
  11. figpack-0.2.4/figpack/spike_sorting/views/RasterPlotItem.py +28 -0
  12. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/__init__.py +4 -0
  13. {figpack-0.2.2 → figpack-0.2.4/figpack.egg-info}/PKG-INFO +1 -1
  14. {figpack-0.2.2 → figpack-0.2.4}/figpack.egg-info/SOURCES.txt +3 -1
  15. {figpack-0.2.2 → figpack-0.2.4}/pyproject.toml +3 -3
  16. figpack-0.2.4/tests/test_figpack_view.py +51 -0
  17. figpack-0.2.2/tests/test_figpack_view.py +0 -106
  18. {figpack-0.2.2 → figpack-0.2.4}/LICENSE +0 -0
  19. {figpack-0.2.2 → figpack-0.2.4}/MANIFEST.in +0 -0
  20. {figpack-0.2.2 → figpack-0.2.4}/README.md +0 -0
  21. {figpack-0.2.2 → figpack-0.2.4}/figpack/cli.py +0 -0
  22. {figpack-0.2.2 → figpack-0.2.4}/figpack/core/__init__.py +0 -0
  23. {figpack-0.2.2 → figpack-0.2.4}/figpack/core/_bundle_utils.py +0 -0
  24. {figpack-0.2.2 → figpack-0.2.4}/figpack/figpack-gui-dist/assets/index-Cmae55E4.css +0 -0
  25. {figpack-0.2.2 → figpack-0.2.4}/figpack/figpack-gui-dist/assets/neurosift-logo-CLsuwLMO.png +0 -0
  26. {figpack-0.2.2 → figpack-0.2.4}/figpack/franklab/__init__.py +0 -0
  27. {figpack-0.2.2 → figpack-0.2.4}/figpack/franklab/views/TrackAnimation.py +0 -0
  28. {figpack-0.2.2 → figpack-0.2.4}/figpack/franklab/views/__init__.py +0 -0
  29. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/__init__.py +0 -0
  30. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/AutocorrelogramItem.py +0 -0
  31. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/Autocorrelograms.py +0 -0
  32. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/AverageWaveforms.py +0 -0
  33. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/CrossCorrelogramItem.py +0 -0
  34. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/CrossCorrelograms.py +0 -0
  35. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/SpikeAmplitudes.py +0 -0
  36. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/SpikeAmplitudesItem.py +0 -0
  37. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/UnitSimilarityScore.py +0 -0
  38. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/UnitsTable.py +0 -0
  39. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/UnitsTableColumn.py +0 -0
  40. {figpack-0.2.2 → figpack-0.2.4}/figpack/spike_sorting/views/UnitsTableRow.py +0 -0
  41. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/Box.py +0 -0
  42. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/Image.py +0 -0
  43. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/LayoutItem.py +0 -0
  44. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/Markdown.py +0 -0
  45. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/MatplotlibFigure.py +0 -0
  46. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/MultiChannelTimeseries.py +0 -0
  47. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/PlotlyFigure.py +0 -0
  48. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/Splitter.py +0 -0
  49. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/TabLayout.py +0 -0
  50. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/TabLayoutItem.py +0 -0
  51. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/TimeseriesGraph.py +0 -0
  52. {figpack-0.2.2 → figpack-0.2.4}/figpack/views/__init__.py +0 -0
  53. {figpack-0.2.2 → figpack-0.2.4}/figpack.egg-info/dependency_links.txt +0 -0
  54. {figpack-0.2.2 → figpack-0.2.4}/figpack.egg-info/entry_points.txt +0 -0
  55. {figpack-0.2.2 → figpack-0.2.4}/figpack.egg-info/requires.txt +0 -0
  56. {figpack-0.2.2 → figpack-0.2.4}/figpack.egg-info/top_level.txt +0 -0
  57. {figpack-0.2.2 → figpack-0.2.4}/setup.cfg +0 -0
  58. {figpack-0.2.2 → figpack-0.2.4}/tests/test_average_waveforms.py +0 -0
  59. {figpack-0.2.2 → figpack-0.2.4}/tests/test_box.py +0 -0
  60. {figpack-0.2.2 → figpack-0.2.4}/tests/test_cli.py +0 -0
  61. {figpack-0.2.2 → figpack-0.2.4}/tests/test_core.py +0 -0
  62. {figpack-0.2.2 → figpack-0.2.4}/tests/test_image.py +0 -0
  63. {figpack-0.2.2 → figpack-0.2.4}/tests/test_markdown.py +0 -0
  64. {figpack-0.2.2 → figpack-0.2.4}/tests/test_matplotlib_figure.py +0 -0
  65. {figpack-0.2.2 → figpack-0.2.4}/tests/test_multichannel_timeseries.py +0 -0
  66. {figpack-0.2.2 → figpack-0.2.4}/tests/test_plotly_figure.py +0 -0
  67. {figpack-0.2.2 → figpack-0.2.4}/tests/test_show_view.py +0 -0
  68. {figpack-0.2.2 → figpack-0.2.4}/tests/test_spike_sorting_correlograms.py +0 -0
  69. {figpack-0.2.2 → figpack-0.2.4}/tests/test_splitter.py +0 -0
  70. {figpack-0.2.2 → figpack-0.2.4}/tests/test_tablayout.py +0 -0
  71. {figpack-0.2.2 → figpack-0.2.4}/tests/test_timeseries_graph.py +0 -0
  72. {figpack-0.2.2 → figpack-0.2.4}/tests/test_track_animation.py +0 -0
  73. {figpack-0.2.2 → figpack-0.2.4}/tests/test_units_table.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: figpack
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: A Python package for creating shareable, interactive visualizations in the browser
5
5
  Author-email: Jeremy Magland <jmagland@flatironinstitute.org>
6
6
  License: Apache-2.0
@@ -2,4 +2,4 @@
2
2
  figpack - A Python package for creating shareable, interactive visualizations in the browser
3
3
  """
4
4
 
5
- __version__ = "0.2.2"
5
+ __version__ = "0.2.4"
@@ -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(self) -> pathlib.Path:
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
- figure_id = str(uuid.uuid4())[:8] # Short unique ID
145
- figure_dir = temp_dir / f"figure_{figure_id}"
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 environment variable
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
- print("Starting upload...")
103
- figure_url = _upload_bundle(tmpdir, api_key, title=title)
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(tmpdir: str, api_key: str, title: str = None) -> str:
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 at: {figure_url}")
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
 
@@ -3,3 +3,5 @@ import os
3
3
  FIGPACK_API_BASE_URL = os.getenv(
4
4
  "FIGPACK_API_BASE_URL", "https://figpack-api.vercel.app"
5
5
  )
6
+
7
+ FIGPACK_BUCKET = os.getenv("FIGPACK_BUCKET", "figpack-figures")
@@ -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 = False,
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 _show_view
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: