openms-insight 0.1.1__py3-none-any.whl → 0.1.3__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.
- openms_insight/__init__.py +11 -7
- openms_insight/components/__init__.py +2 -2
- openms_insight/components/heatmap.py +192 -102
- openms_insight/components/lineplot.py +377 -82
- openms_insight/components/sequenceview.py +677 -213
- openms_insight/components/table.py +86 -58
- openms_insight/core/__init__.py +2 -2
- openms_insight/core/base.py +113 -49
- openms_insight/core/registry.py +6 -5
- openms_insight/core/state.py +33 -31
- openms_insight/core/subprocess_preprocess.py +1 -3
- openms_insight/js-component/dist/assets/index.css +1 -1
- openms_insight/js-component/dist/assets/index.js +113 -113
- openms_insight/preprocessing/__init__.py +5 -6
- openms_insight/preprocessing/compression.py +68 -66
- openms_insight/preprocessing/filtering.py +119 -9
- openms_insight/rendering/__init__.py +1 -1
- openms_insight/rendering/bridge.py +192 -42
- {openms_insight-0.1.1.dist-info → openms_insight-0.1.3.dist-info}/METADATA +163 -20
- openms_insight-0.1.3.dist-info/RECORD +28 -0
- openms_insight-0.1.1.dist-info/RECORD +0 -28
- {openms_insight-0.1.1.dist-info → openms_insight-0.1.3.dist-info}/WHEEL +0 -0
- {openms_insight-0.1.1.dist-info → openms_insight-0.1.3.dist-info}/licenses/LICENSE +0 -0
openms_insight/core/state.py
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"""State management for cross-component selection synchronization."""
|
|
2
2
|
|
|
3
3
|
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
4
5
|
import numpy as np
|
|
5
6
|
|
|
6
7
|
# Module-level default state manager
|
|
7
|
-
_default_state_manager: Optional[
|
|
8
|
+
_default_state_manager: Optional["StateManager"] = None
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
def get_default_state_manager() ->
|
|
11
|
+
def get_default_state_manager() -> "StateManager":
|
|
11
12
|
"""
|
|
12
13
|
Get or create the default shared StateManager.
|
|
13
14
|
|
|
@@ -58,27 +59,28 @@ class StateManager:
|
|
|
58
59
|
|
|
59
60
|
if self._session_key not in st.session_state:
|
|
60
61
|
st.session_state[self._session_key] = {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
"counter": 0,
|
|
63
|
+
"id": float(np.random.random()),
|
|
64
|
+
"selections": {},
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
@property
|
|
67
68
|
def _state(self) -> Dict[str, Any]:
|
|
68
69
|
"""Get the internal state dict from session_state."""
|
|
69
70
|
import streamlit as st
|
|
71
|
+
|
|
70
72
|
self._ensure_session_state()
|
|
71
73
|
return st.session_state[self._session_key]
|
|
72
74
|
|
|
73
75
|
@property
|
|
74
76
|
def session_id(self) -> float:
|
|
75
77
|
"""Get the unique session ID."""
|
|
76
|
-
return self._state[
|
|
78
|
+
return self._state["id"]
|
|
77
79
|
|
|
78
80
|
@property
|
|
79
81
|
def counter(self) -> int:
|
|
80
82
|
"""Get the current state counter."""
|
|
81
|
-
return self._state[
|
|
83
|
+
return self._state["counter"]
|
|
82
84
|
|
|
83
85
|
def get_selection(self, identifier: str) -> Any:
|
|
84
86
|
"""
|
|
@@ -90,7 +92,7 @@ class StateManager:
|
|
|
90
92
|
Returns:
|
|
91
93
|
The current selection value, or None if not set
|
|
92
94
|
"""
|
|
93
|
-
return self._state[
|
|
95
|
+
return self._state["selections"].get(identifier)
|
|
94
96
|
|
|
95
97
|
def set_selection(self, identifier: str, value: Any) -> bool:
|
|
96
98
|
"""
|
|
@@ -103,12 +105,12 @@ class StateManager:
|
|
|
103
105
|
Returns:
|
|
104
106
|
True if the value changed, False otherwise
|
|
105
107
|
"""
|
|
106
|
-
current = self._state[
|
|
108
|
+
current = self._state["selections"].get(identifier)
|
|
107
109
|
if current == value:
|
|
108
110
|
return False
|
|
109
111
|
|
|
110
|
-
self._state[
|
|
111
|
-
self._state[
|
|
112
|
+
self._state["selections"][identifier] = value
|
|
113
|
+
self._state["counter"] += 1
|
|
112
114
|
return True
|
|
113
115
|
|
|
114
116
|
def clear_selection(self, identifier: str) -> bool:
|
|
@@ -121,9 +123,9 @@ class StateManager:
|
|
|
121
123
|
Returns:
|
|
122
124
|
True if a selection was cleared, False if it wasn't set
|
|
123
125
|
"""
|
|
124
|
-
if identifier in self._state[
|
|
125
|
-
del self._state[
|
|
126
|
-
self._state[
|
|
126
|
+
if identifier in self._state["selections"]:
|
|
127
|
+
del self._state["selections"][identifier]
|
|
128
|
+
self._state["counter"] += 1
|
|
127
129
|
return True
|
|
128
130
|
return False
|
|
129
131
|
|
|
@@ -134,7 +136,7 @@ class StateManager:
|
|
|
134
136
|
Returns:
|
|
135
137
|
Dict mapping identifiers to their selected values
|
|
136
138
|
"""
|
|
137
|
-
return self._state[
|
|
139
|
+
return self._state["selections"].copy()
|
|
138
140
|
|
|
139
141
|
def get_state_for_vue(self) -> Dict[str, Any]:
|
|
140
142
|
"""
|
|
@@ -144,10 +146,10 @@ class StateManager:
|
|
|
144
146
|
Dict with counter, id, and all selections as top-level keys
|
|
145
147
|
"""
|
|
146
148
|
state = {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
+
"counter": self._state["counter"],
|
|
150
|
+
"id": self._state["id"],
|
|
149
151
|
}
|
|
150
|
-
state.update(self._state[
|
|
152
|
+
state.update(self._state["selections"])
|
|
151
153
|
return state
|
|
152
154
|
|
|
153
155
|
def update_from_vue(self, vue_state: Dict[str, Any]) -> bool:
|
|
@@ -167,45 +169,45 @@ class StateManager:
|
|
|
167
169
|
return False
|
|
168
170
|
|
|
169
171
|
# Verify same session (prevents cross-tab interference)
|
|
170
|
-
if vue_state.get(
|
|
172
|
+
if vue_state.get("id") != self._state["id"]:
|
|
171
173
|
return False
|
|
172
174
|
|
|
173
175
|
# Extract metadata
|
|
174
|
-
vue_counter = vue_state.pop(
|
|
175
|
-
vue_state.pop(
|
|
176
|
+
vue_counter = vue_state.pop("counter", 0)
|
|
177
|
+
vue_state.pop("id", None)
|
|
176
178
|
|
|
177
179
|
# Filter out internal keys (starting with _)
|
|
178
|
-
vue_state = {k: v for k, v in vue_state.items() if not k.startswith(
|
|
180
|
+
vue_state = {k: v for k, v in vue_state.items() if not k.startswith("_")}
|
|
179
181
|
|
|
180
182
|
modified = False
|
|
181
183
|
|
|
182
184
|
# Always accept previously undefined keys (but skip None/undefined values)
|
|
183
185
|
for key, value in vue_state.items():
|
|
184
|
-
if key not in self._state[
|
|
186
|
+
if key not in self._state["selections"]:
|
|
185
187
|
# Only add if value is not None (undefined in Vue = no selection)
|
|
186
188
|
if value is not None:
|
|
187
|
-
self._state[
|
|
189
|
+
self._state["selections"][key] = value
|
|
188
190
|
modified = True
|
|
189
191
|
|
|
190
192
|
# Only accept conflicting updates if Vue has newer state
|
|
191
|
-
if vue_counter >= self._state[
|
|
193
|
+
if vue_counter >= self._state["counter"]:
|
|
192
194
|
for key, value in vue_state.items():
|
|
193
|
-
if key in self._state[
|
|
194
|
-
if self._state[
|
|
195
|
-
self._state[
|
|
195
|
+
if key in self._state["selections"]:
|
|
196
|
+
if self._state["selections"][key] != value:
|
|
197
|
+
self._state["selections"][key] = value
|
|
196
198
|
modified = True
|
|
197
199
|
|
|
198
200
|
if modified:
|
|
199
201
|
# Set counter to be at least vue_counter + 1 to reject future stale updates
|
|
200
202
|
# from other Vue components that haven't received the latest state yet
|
|
201
|
-
self._state[
|
|
203
|
+
self._state["counter"] = max(self._state["counter"] + 1, vue_counter + 1)
|
|
202
204
|
|
|
203
205
|
return modified
|
|
204
206
|
|
|
205
207
|
def clear(self) -> None:
|
|
206
208
|
"""Clear all selections and reset counter."""
|
|
207
|
-
self._state[
|
|
208
|
-
self._state[
|
|
209
|
+
self._state["selections"] = {}
|
|
210
|
+
self._state["counter"] = 0
|
|
209
211
|
|
|
210
212
|
def __repr__(self) -> str:
|
|
211
213
|
return (
|