Spaces:
Build error
Build error
Ashkan Taghipour (The University of Western Australia) Claude Opus 4.6 commited on
Commit ·
16c6db2
1
Parent(s): f01b448
Fix Protein World performance: replace 55k-item dropdown with textbox
Browse filesThe gene dropdown with 55,512 choices caused the browser to hang.
Replaced with:
- A textbox where users type a gene ID and press Enter or click Load
- A small "pick from backpack" dropdown showing only pinned genes
- A hidden dropdown for Gene Card "Show Protein" button integration
All pin callbacks now also sync the backpack picker choices.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- app.py +58 -14
- ui/quest4.py +23 -5
app.py
CHANGED
|
@@ -227,12 +227,14 @@ with demo:
|
|
| 227 |
def on_q2_pin(gene_id, state):
|
| 228 |
bp_text, state = on_pin_gene(gene_id, state)
|
| 229 |
q4_bp = _backpack_text(state)
|
| 230 |
-
|
|
|
|
| 231 |
|
| 232 |
C["q2_pin_btn"].click(
|
| 233 |
fn=on_q2_pin,
|
| 234 |
inputs=[C["q2_selected_gene_text"], C["state"]],
|
| 235 |
-
outputs=[C["q2_backpack_display"], C["q4_backpack_display"],
|
|
|
|
| 236 |
)
|
| 237 |
|
| 238 |
# Table row click → select gene
|
|
@@ -260,41 +262,81 @@ with demo:
|
|
| 260 |
)
|
| 261 |
|
| 262 |
# -- Quest 4 --
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
C["q4_gene_dropdown"].change(
|
| 264 |
-
fn=lambda gene_id: get_protein_stats_html(gene_id, DATA),
|
| 265 |
inputs=[C["q4_gene_dropdown"]],
|
| 266 |
-
outputs=[C["q4_protein_stats_html"]],
|
| 267 |
)
|
| 268 |
|
| 269 |
def on_q4_pin(gene_id, state):
|
| 270 |
-
"""Pin a gene from Quest 4
|
| 271 |
-
|
|
|
|
| 272 |
radar = build_backpack_comparison(state, DATA)
|
| 273 |
heatmap = build_composition_heatmap(state, DATA)
|
| 274 |
q4_bp = _backpack_text(state)
|
| 275 |
-
|
|
|
|
|
|
|
| 276 |
|
| 277 |
C["q4_pin_btn"].click(
|
| 278 |
fn=on_q4_pin,
|
| 279 |
-
inputs=[C["
|
| 280 |
outputs=[
|
| 281 |
C["q2_backpack_display"],
|
| 282 |
C["q4_backpack_display"],
|
| 283 |
C["q4_comparison_bar_plot"],
|
| 284 |
C["q4_composition_heatmap"],
|
|
|
|
| 285 |
C["state"],
|
| 286 |
],
|
| 287 |
)
|
| 288 |
|
| 289 |
-
|
| 290 |
-
|
|
|
|
| 291 |
build_backpack_comparison(state, DATA),
|
| 292 |
build_composition_heatmap(state, DATA),
|
| 293 |
_backpack_text(state),
|
| 294 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
inputs=[C["state"]],
|
| 296 |
outputs=[C["q4_comparison_bar_plot"], C["q4_composition_heatmap"],
|
| 297 |
-
C["q4_backpack_display"]],
|
| 298 |
)
|
| 299 |
|
| 300 |
# -- Gene Card --
|
|
@@ -319,12 +361,14 @@ with demo:
|
|
| 319 |
def on_gc_pin(state):
|
| 320 |
bp_text, state = on_pin_gene(state.selected_gene if state else "", state)
|
| 321 |
q4_bp = _backpack_text(state)
|
| 322 |
-
|
|
|
|
| 323 |
|
| 324 |
C["gc_pin_card_btn"].click(
|
| 325 |
fn=on_gc_pin,
|
| 326 |
inputs=[C["state"]],
|
| 327 |
-
outputs=[C["q2_backpack_display"], C["q4_backpack_display"],
|
|
|
|
| 328 |
)
|
| 329 |
|
| 330 |
C["gc_download_gene_btn"].click(
|
|
|
|
| 227 |
def on_q2_pin(gene_id, state):
|
| 228 |
bp_text, state = on_pin_gene(gene_id, state)
|
| 229 |
q4_bp = _backpack_text(state)
|
| 230 |
+
picker = gr.Dropdown(choices=state.backpack_genes if state else [])
|
| 231 |
+
return bp_text, q4_bp, picker, state
|
| 232 |
|
| 233 |
C["q2_pin_btn"].click(
|
| 234 |
fn=on_q2_pin,
|
| 235 |
inputs=[C["q2_selected_gene_text"], C["state"]],
|
| 236 |
+
outputs=[C["q2_backpack_display"], C["q4_backpack_display"],
|
| 237 |
+
C["q4_backpack_picker"], C["state"]],
|
| 238 |
)
|
| 239 |
|
| 240 |
# Table row click → select gene
|
|
|
|
| 262 |
)
|
| 263 |
|
| 264 |
# -- Quest 4 --
|
| 265 |
+
|
| 266 |
+
# Track the currently loaded gene in Quest 4 via state
|
| 267 |
+
def _load_gene(gene_id):
|
| 268 |
+
"""Load protein stats for a gene ID."""
|
| 269 |
+
if not gene_id or not gene_id.strip():
|
| 270 |
+
return get_protein_stats_html("", DATA), ""
|
| 271 |
+
gid = gene_id.strip()
|
| 272 |
+
return get_protein_stats_html(gid, DATA), gid
|
| 273 |
+
|
| 274 |
+
# Load button: type a gene ID and click Load
|
| 275 |
+
C["q4_load_btn"].click(
|
| 276 |
+
fn=_load_gene,
|
| 277 |
+
inputs=[C["q4_gene_input"]],
|
| 278 |
+
outputs=[C["q4_protein_stats_html"], C["q4_gene_input"]],
|
| 279 |
+
)
|
| 280 |
+
|
| 281 |
+
# Also trigger on Enter key in the textbox
|
| 282 |
+
C["q4_gene_input"].submit(
|
| 283 |
+
fn=_load_gene,
|
| 284 |
+
inputs=[C["q4_gene_input"]],
|
| 285 |
+
outputs=[C["q4_protein_stats_html"], C["q4_gene_input"]],
|
| 286 |
+
)
|
| 287 |
+
|
| 288 |
+
# Backpack quick-pick dropdown
|
| 289 |
+
C["q4_backpack_picker"].change(
|
| 290 |
+
fn=lambda gene_id: (get_protein_stats_html(gene_id, DATA), gene_id or ""),
|
| 291 |
+
inputs=[C["q4_backpack_picker"]],
|
| 292 |
+
outputs=[C["q4_protein_stats_html"], C["q4_gene_input"]],
|
| 293 |
+
)
|
| 294 |
+
|
| 295 |
+
# Hidden dropdown receives value from Gene Card "Show Protein" button
|
| 296 |
C["q4_gene_dropdown"].change(
|
| 297 |
+
fn=lambda gene_id: (get_protein_stats_html(gene_id, DATA), gene_id or ""),
|
| 298 |
inputs=[C["q4_gene_dropdown"]],
|
| 299 |
+
outputs=[C["q4_protein_stats_html"], C["q4_gene_input"]],
|
| 300 |
)
|
| 301 |
|
| 302 |
def on_q4_pin(gene_id, state):
|
| 303 |
+
"""Pin a gene from Quest 4, update both backpack displays and charts."""
|
| 304 |
+
gid = gene_id.strip() if gene_id else ""
|
| 305 |
+
bp_text, state = on_pin_gene(gid, state)
|
| 306 |
radar = build_backpack_comparison(state, DATA)
|
| 307 |
heatmap = build_composition_heatmap(state, DATA)
|
| 308 |
q4_bp = _backpack_text(state)
|
| 309 |
+
# Update the backpack picker choices
|
| 310 |
+
picker_update = gr.Dropdown(choices=state.backpack_genes if state else [])
|
| 311 |
+
return bp_text, q4_bp, radar, heatmap, picker_update, state
|
| 312 |
|
| 313 |
C["q4_pin_btn"].click(
|
| 314 |
fn=on_q4_pin,
|
| 315 |
+
inputs=[C["q4_gene_input"], C["state"]],
|
| 316 |
outputs=[
|
| 317 |
C["q2_backpack_display"],
|
| 318 |
C["q4_backpack_display"],
|
| 319 |
C["q4_comparison_bar_plot"],
|
| 320 |
C["q4_composition_heatmap"],
|
| 321 |
+
C["q4_backpack_picker"],
|
| 322 |
C["state"],
|
| 323 |
],
|
| 324 |
)
|
| 325 |
|
| 326 |
+
def _on_q4_tab_select(state):
|
| 327 |
+
bp_genes = state.backpack_genes if state else []
|
| 328 |
+
return (
|
| 329 |
build_backpack_comparison(state, DATA),
|
| 330 |
build_composition_heatmap(state, DATA),
|
| 331 |
_backpack_text(state),
|
| 332 |
+
gr.Dropdown(choices=bp_genes),
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
C["q4_tab"].select(
|
| 336 |
+
fn=_on_q4_tab_select,
|
| 337 |
inputs=[C["state"]],
|
| 338 |
outputs=[C["q4_comparison_bar_plot"], C["q4_composition_heatmap"],
|
| 339 |
+
C["q4_backpack_display"], C["q4_backpack_picker"]],
|
| 340 |
)
|
| 341 |
|
| 342 |
# -- Gene Card --
|
|
|
|
| 361 |
def on_gc_pin(state):
|
| 362 |
bp_text, state = on_pin_gene(state.selected_gene if state else "", state)
|
| 363 |
q4_bp = _backpack_text(state)
|
| 364 |
+
picker = gr.Dropdown(choices=state.backpack_genes if state else [])
|
| 365 |
+
return bp_text, q4_bp, picker, state
|
| 366 |
|
| 367 |
C["gc_pin_card_btn"].click(
|
| 368 |
fn=on_gc_pin,
|
| 369 |
inputs=[C["state"]],
|
| 370 |
+
outputs=[C["q2_backpack_display"], C["q4_backpack_display"],
|
| 371 |
+
C["q4_backpack_picker"], C["state"]],
|
| 372 |
)
|
| 373 |
|
| 374 |
C["gc_download_gene_btn"].click(
|
ui/quest4.py
CHANGED
|
@@ -167,7 +167,8 @@ def build_quest4(gene_choices: list[str]):
|
|
| 167 |
"""Build Quest 4 tab components. Returns dict of components.
|
| 168 |
|
| 169 |
Returned keys (prefixed with ``q4_`` by layout.py):
|
| 170 |
-
tab, gene_dropdown,
|
|
|
|
| 171 |
comparison_bar_plot, composition_heatmap
|
| 172 |
"""
|
| 173 |
with gr.Tab("Protein World", id="quest4") as tab:
|
|
@@ -186,12 +187,26 @@ def build_quest4(gene_choices: list[str]):
|
|
| 186 |
# -- Single-gene selector --
|
| 187 |
with gr.Row():
|
| 188 |
with gr.Column(scale=2):
|
|
|
|
| 189 |
gene_dropdown = gr.Dropdown(
|
| 190 |
-
choices=
|
| 191 |
-
|
| 192 |
-
interactive=True,
|
| 193 |
allow_custom_value=True,
|
| 194 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
pin_btn = gr.Button(
|
| 196 |
"Pin this gene to Backpack",
|
| 197 |
variant="secondary",
|
|
@@ -201,7 +216,7 @@ def build_quest4(gene_choices: list[str]):
|
|
| 201 |
protein_stats_html = gr.HTML(
|
| 202 |
value=(
|
| 203 |
'<div style="padding:16px;color:#757575;text-align:center;">'
|
| 204 |
-
"
|
| 205 |
"</div>"
|
| 206 |
),
|
| 207 |
label="Protein Statistics",
|
|
@@ -234,6 +249,9 @@ def build_quest4(gene_choices: list[str]):
|
|
| 234 |
return {
|
| 235 |
"tab": tab,
|
| 236 |
"gene_dropdown": gene_dropdown,
|
|
|
|
|
|
|
|
|
|
| 237 |
"protein_stats_html": protein_stats_html,
|
| 238 |
"pin_btn": pin_btn,
|
| 239 |
"backpack_display": backpack_display,
|
|
|
|
| 167 |
"""Build Quest 4 tab components. Returns dict of components.
|
| 168 |
|
| 169 |
Returned keys (prefixed with ``q4_`` by layout.py):
|
| 170 |
+
tab, gene_dropdown, gene_input, load_btn, backpack_picker,
|
| 171 |
+
protein_stats_html, pin_btn, backpack_display,
|
| 172 |
comparison_bar_plot, composition_heatmap
|
| 173 |
"""
|
| 174 |
with gr.Tab("Protein World", id="quest4") as tab:
|
|
|
|
| 187 |
# -- Single-gene selector --
|
| 188 |
with gr.Row():
|
| 189 |
with gr.Column(scale=2):
|
| 190 |
+
# Hidden dropdown to receive values from Gene Card "Show Protein"
|
| 191 |
gene_dropdown = gr.Dropdown(
|
| 192 |
+
choices=[],
|
| 193 |
+
visible=False,
|
|
|
|
| 194 |
allow_custom_value=True,
|
| 195 |
)
|
| 196 |
+
with gr.Row():
|
| 197 |
+
gene_input = gr.Textbox(
|
| 198 |
+
label="Type a gene ID (e.g. g05743)",
|
| 199 |
+
placeholder="g05743",
|
| 200 |
+
interactive=True,
|
| 201 |
+
scale=3,
|
| 202 |
+
)
|
| 203 |
+
load_btn = gr.Button("Load", variant="primary", size="sm", scale=1)
|
| 204 |
+
backpack_picker = gr.Dropdown(
|
| 205 |
+
choices=[],
|
| 206 |
+
label="Or pick from your backpack",
|
| 207 |
+
interactive=True,
|
| 208 |
+
allow_custom_value=False,
|
| 209 |
+
)
|
| 210 |
pin_btn = gr.Button(
|
| 211 |
"Pin this gene to Backpack",
|
| 212 |
variant="secondary",
|
|
|
|
| 216 |
protein_stats_html = gr.HTML(
|
| 217 |
value=(
|
| 218 |
'<div style="padding:16px;color:#757575;text-align:center;">'
|
| 219 |
+
"Type a gene ID and click <b>Load</b>, or pick one from your backpack."
|
| 220 |
"</div>"
|
| 221 |
),
|
| 222 |
label="Protein Statistics",
|
|
|
|
| 249 |
return {
|
| 250 |
"tab": tab,
|
| 251 |
"gene_dropdown": gene_dropdown,
|
| 252 |
+
"gene_input": gene_input,
|
| 253 |
+
"load_btn": load_btn,
|
| 254 |
+
"backpack_picker": backpack_picker,
|
| 255 |
"protein_stats_html": protein_stats_html,
|
| 256 |
"pin_btn": pin_btn,
|
| 257 |
"backpack_display": backpack_display,
|