openms-insight 0.1.8__py3-none-any.whl → 0.1.9__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.
@@ -130,10 +130,27 @@ def clear_component_annotations() -> None:
130
130
  st.session_state[_COMPONENT_ANNOTATIONS_KEY].clear()
131
131
 
132
132
 
133
+ def _compute_annotation_hash(component: "BaseComponent") -> Optional[str]:
134
+ """
135
+ Compute hash of component's dynamic annotations, if any.
136
+
137
+ Args:
138
+ component: The component to check for annotations
139
+
140
+ Returns:
141
+ Short hash string if annotations exist, None otherwise
142
+ """
143
+ annotations = getattr(component, "_dynamic_annotations", None)
144
+ if annotations is None:
145
+ return None
146
+ # Hash the sorted keys (sufficient for change detection)
147
+ return hashlib.md5(str(sorted(annotations.keys())).encode()).hexdigest()[:8]
148
+
149
+
133
150
  def _get_cached_vue_data(
134
151
  component_id: str,
135
152
  filter_state_hashable: Tuple[Tuple[str, Any], ...],
136
- ) -> Optional[Tuple[Dict[str, Any], str]]:
153
+ ) -> Optional[Tuple[Dict[str, Any], str, Optional[str]]]:
137
154
  """
138
155
  Get cached Vue data for component if filter state matches.
139
156
 
@@ -145,13 +162,19 @@ def _get_cached_vue_data(
145
162
  filter_state_hashable: Current filter state (for cache validation)
146
163
 
147
164
  Returns:
148
- Tuple of (vue_data, data_hash) if cache hit, None otherwise
165
+ Tuple of (vue_data, data_hash, annotation_hash) if cache hit, None otherwise
149
166
  """
150
167
  cache = _get_component_cache()
151
168
  if component_id in cache:
152
- cached_state, vue_data, data_hash = cache[component_id]
169
+ entry = cache[component_id]
170
+ # Support both old (3-tuple) and new (4-tuple) format
171
+ if len(entry) == 4:
172
+ cached_state, vue_data, data_hash, ann_hash = entry
173
+ else:
174
+ cached_state, vue_data, data_hash = entry
175
+ ann_hash = None
153
176
  if cached_state == filter_state_hashable:
154
- return (vue_data, data_hash)
177
+ return (vue_data, data_hash, ann_hash)
155
178
  return None
156
179
 
157
180
 
@@ -160,6 +183,7 @@ def _set_cached_vue_data(
160
183
  filter_state_hashable: Tuple[Tuple[str, Any], ...],
161
184
  vue_data: Dict[str, Any],
162
185
  data_hash: str,
186
+ ann_hash: Optional[str] = None,
163
187
  ) -> None:
164
188
  """
165
189
  Cache Vue data for component, replacing any previous entry.
@@ -171,9 +195,10 @@ def _set_cached_vue_data(
171
195
  filter_state_hashable: Current filter state
172
196
  vue_data: Data to cache
173
197
  data_hash: Hash of the data
198
+ ann_hash: Hash of dynamic annotations (if any)
174
199
  """
175
200
  cache = _get_component_cache()
176
- cache[component_id] = (filter_state_hashable, vue_data, data_hash)
201
+ cache[component_id] = (filter_state_hashable, vue_data, data_hash, ann_hash)
177
202
 
178
203
 
179
204
  def _prepare_vue_data_cached(
@@ -217,7 +242,7 @@ def _prepare_vue_data_cached(
217
242
  )
218
243
 
219
244
  if cached is not None:
220
- cached_data, cached_hash = cached
245
+ cached_data, cached_hash, _ = cached # Ignore cached annotation hash here
221
246
 
222
247
  if has_dynamic_annotations:
223
248
  # Cache hit but need to re-apply annotations (they may have changed)
@@ -240,6 +265,9 @@ def _prepare_vue_data_cached(
240
265
  # Cache miss - compute data
241
266
  vue_data = component._prepare_vue_data(state_dict)
242
267
 
268
+ # Compute annotation hash for cache storage
269
+ ann_hash = _compute_annotation_hash(component)
270
+
243
271
  if has_dynamic_annotations:
244
272
  # Store BASE data (without dynamic annotation columns) in cache
245
273
  if hasattr(component, "_strip_dynamic_columns"):
@@ -248,7 +276,7 @@ def _prepare_vue_data_cached(
248
276
  # Fallback: store without _plotConfig (may have stale column refs)
249
277
  base_data = {k: v for k, v in vue_data.items() if k != "_plotConfig"}
250
278
  base_hash = _hash_data(base_data)
251
- _set_cached_vue_data(component_id, filter_state_hashable, base_data, base_hash)
279
+ _set_cached_vue_data(component_id, filter_state_hashable, base_data, base_hash, ann_hash)
252
280
 
253
281
  # Return full data with annotations
254
282
  data_hash = _hash_data(vue_data)
@@ -256,7 +284,7 @@ def _prepare_vue_data_cached(
256
284
  else:
257
285
  # Store complete data in cache
258
286
  data_hash = _hash_data(vue_data)
259
- _set_cached_vue_data(component_id, filter_state_hashable, vue_data, data_hash)
287
+ _set_cached_vue_data(component_id, filter_state_hashable, vue_data, data_hash, ann_hash)
260
288
  return vue_data, data_hash
261
289
 
262
290
 
@@ -377,16 +405,28 @@ def render_component(
377
405
  # Check if cached data is VALID for current state
378
406
  # KEY FIX: Only send data when cache matches current state
379
407
  # - Before: Always sent cached data, even if stale (page 38 when Vue wants page 1)
380
- # - Now: Only send if cache matches current state
408
+ # - Now: Only send if cache matches current state AND annotation state matches
381
409
  cache_valid = False
410
+ current_ann_hash = _compute_annotation_hash(component)
411
+
382
412
  if cached_entry is not None:
383
- cached_state, cached_data, cached_hash = cached_entry
384
- cache_valid = (cached_state == current_filter_state)
413
+ # Support both old (3-tuple) and new (4-tuple) cache format
414
+ if len(cached_entry) == 4:
415
+ cached_state, cached_data, cached_hash, cached_ann_hash = cached_entry
416
+ else:
417
+ cached_state, cached_data, cached_hash = cached_entry
418
+ cached_ann_hash = None
419
+
420
+ # Cache valid only if BOTH filter state AND annotation state match
421
+ filter_state_matches = (cached_state == current_filter_state)
422
+ ann_state_matches = (cached_ann_hash == current_ann_hash)
423
+ cache_valid = filter_state_matches and ann_state_matches
424
+
385
425
  if _DEBUG_STATE_SYNC:
386
426
  _logger.warning(
387
427
  f"[Bridge:{component._cache_id}] Phase1: cache_valid={cache_valid}, "
388
- f"cached_state={cached_state[:2] if cached_state else None}..., "
389
- f"current_state={current_filter_state[:2] if current_filter_state else None}..."
428
+ f"filter_match={filter_state_matches}, ann_match={ann_state_matches}, "
429
+ f"cached_ann={cached_ann_hash}, current_ann={current_ann_hash}"
390
430
  )
391
431
 
392
432
  # Build payload - only send data if cache is valid for current state
@@ -538,8 +578,8 @@ def render_component(
538
578
  else:
539
579
  converted_data[data_key] = value
540
580
 
541
- # Store in cache for next render
542
- cache[component_id] = (filter_state_hashable, converted_data, data_hash)
581
+ # Store in cache for next render (include annotation hash for validity check)
582
+ cache[component_id] = (filter_state_hashable, converted_data, data_hash, current_ann_hash)
543
583
 
544
584
  # If cache was invalid at Phase 1, we didn't send data to Vue (dataChanged=False).
545
585
  # Trigger a rerun so the newly cached data gets sent on the next render.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openms-insight
3
- Version: 0.1.8
3
+ Version: 0.1.9
4
4
  Summary: Interactive visualization components for mass spectrometry data in Streamlit
5
5
  Project-URL: Homepage, https://github.com/t0mdavid-m/OpenMS-Insight
6
6
  Project-URL: Documentation, https://github.com/t0mdavid-m/OpenMS-Insight#readme
@@ -16,7 +16,7 @@ openms_insight/preprocessing/compression.py,sha256=T4YbX9PUlfTfPit_kpuLZn8hYpqLY
16
16
  openms_insight/preprocessing/filtering.py,sha256=fkmaIXfR5hfjyWfaMYqaeybMHaZjvUZYaKCqvxPOWMQ,14152
17
17
  openms_insight/preprocessing/scatter.py,sha256=2ifCNTUKHEW9UVpv4z9c5GaLnz5zw9o1119IenzAe9s,4703
18
18
  openms_insight/rendering/__init__.py,sha256=ApHvKeh87yY4GTIEai-tCeIXpNbwOXWlmcmIwMMRZYc,198
19
- openms_insight/rendering/bridge.py,sha256=3FiMlNL3hBHue28-0RWP9pt7GaNeczZbQwZl4cupXlc,23935
19
+ openms_insight/rendering/bridge.py,sha256=a_6lq3jR9tDFxdSxMjeJhyV_Tgw7ALcm-UFEvl_TE_M,25615
20
20
  openms_insight/js-component/dist/index.html,sha256=LSJ3B_YmGUrCCdZ1UaZO2p6Wqsih6nTH62Z_0uZxpD8,430
21
21
  openms_insight/js-component/dist/assets/index.css,sha256=wFvo7FbG132LL7uw0slXfrL_oSQ8u2RKL1DW9hD9-kk,884264
22
22
  openms_insight/js-component/dist/assets/index.js,sha256=aqGc3g7XLTRr7ptEgoA3XDu5oMS47yxxjUBXgansIo0,6091480
@@ -24,7 +24,7 @@ openms_insight/js-component/dist/assets/materialdesignicons-webfont.eot,sha256=C
24
24
  openms_insight/js-component/dist/assets/materialdesignicons-webfont.ttf,sha256=YeirpaTpgf4iz3yOi82-oAR251xiw38Bv37jM2HWhCg,1307660
25
25
  openms_insight/js-component/dist/assets/materialdesignicons-webfont.woff,sha256=pZKKDVwvYk5G-Y2bFcL2AEU3f3xZTdeKF1kTLqO0Y-s,587984
26
26
  openms_insight/js-component/dist/assets/materialdesignicons-webfont.woff2,sha256=Zi_vqPL4qVwYWI0hd0eJwQfGTnccvmWmmvRikcQxGvw,403216
27
- openms_insight-0.1.8.dist-info/METADATA,sha256=l5aHKdSHf5z046vRxq8xJLDN7UywXFfLEcLTxfEEn4Q,16787
28
- openms_insight-0.1.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
29
- openms_insight-0.1.8.dist-info/licenses/LICENSE,sha256=INFF4rOMmpah7Oi14hLqu7NTOsx56KRRNChAAUcfh2E,1823
30
- openms_insight-0.1.8.dist-info/RECORD,,
27
+ openms_insight-0.1.9.dist-info/METADATA,sha256=--VBuRN6Co39E_BZYFk6OwYkOyWEE9uOq_OBK21PdWE,16787
28
+ openms_insight-0.1.9.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
29
+ openms_insight-0.1.9.dist-info/licenses/LICENSE,sha256=INFF4rOMmpah7Oi14hLqu7NTOsx56KRRNChAAUcfh2E,1823
30
+ openms_insight-0.1.9.dist-info/RECORD,,