Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

## Interface and handling

- select and visualize the first rank-sorted CopomuS mutation automatically
when browser results arrive (#317)

- select the first browser result and show its interaction visualization
automatically; reuse CSV help text as result-header tooltips and store bundled
examples in directly importable/exportable parameter JSON (#315)
Expand Down Expand Up @@ -345,6 +348,15 @@ energies, restricted partition sums, and trackers.
################################################################################
################################################################################

261027 Alexander Mitrofanov
* gui/copomus.js, scripts/wasm/test-browser.py
* Select the first rank-sorted CopomuS mutation after populating the browser
result table and immediately show its vaRRI and energy-plot visualizations;
restore both after folding and reopening the session (issue #317).
* gui/{index.html,docs/copomus.md}
* Document the automatic initial mutation selection in the interface and
CopomuS browser guide.

261027 Alexander Mitrofanov
* gui/{results.js,sessions.js,index.html}, scripts/wasm/test-browser.py
* Select the first energy-sorted interaction when a computation completes and
Expand Down
13 changes: 11 additions & 2 deletions gui/copomus.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class MutationVisualization {
constructor(root) {
this.root = root;
this.viewer = new InteractionVisualization(root, 'mutation-');
this.frame = this.viewer.frame;
this.title = root.querySelector('[data-role=mutation-visualization-title]');
this.plot = root.querySelector('[data-role=mutation-energy-plot]');
}
Expand Down Expand Up @@ -111,7 +112,7 @@ export class CopomusResultsView {
label.append(input, document.createTextNode(key)); this.picker.append(label);
}
this.search.value = ''; this.key = this.columns.includes('rank') ? 'rank' : this.columns[0]; this.direction = 1;
this.root.hidden = false; this.render(); return this.candidates.length;
this.root.hidden = false; this.render(true); return this.candidates.length;
}

selectedCandidates() {
Expand All @@ -125,8 +126,16 @@ export class CopomusResultsView {
});
}

render() {
render(selectFirst = false) {
const rows = this.selectedCandidates(), columns = this.columns.filter((key) => this.visible.has(key));
// A completed design opens on the top-ranked mutation; later table changes
// keep that selection, matching prediction and evaluation result handling.
if (selectFirst && rows.length) {
this.selected = rows[0];
// Session views populate before their template is attached. Defer Plotly
// to the session-expansion hook in that case so it can measure its panel.
if (this.root.isConnected) this.visualization.show(this.selected, this.request);
}
this.header.replaceChildren();
const viewHeading = document.createElement('th'); viewHeading.scope = 'col'; viewHeading.setAttribute('aria-label', 'Mutation visualization'); this.header.append(viewHeading);
for (const key of columns) {
Expand Down
21 changes: 11 additions & 10 deletions gui/docs/copomus.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,17 @@ folding runs; larger screens remain available through `python/CopomuS.py`.

## Results and reproducibility

The sortable/filterable table contains the complete extended CopomuS TSV. Select
**View** to show the wild-type interaction once in vaRRI, with both designed
substitutions annotated at their original signed sequence coordinates in dark
green. A compact Plotly bar chart beside it compares the `ww`, `wm`, `mw` and
`mm` interaction energies and marks `ww` with a dotted baseline; unavailable
interactions remain empty. Session ZIPs contain `parameter.json` and
`copomus-mutations.tsv`. The JSON keeps native IntaRNA settings and records the
task and CopomuS settings under `gui`, so Load restores the complete editable
run. As with other GUI sessions, all data stays in the tab and disappears on
reload unless downloaded.
The sortable/filterable table contains the complete extended CopomuS TSV. The
first rank-sorted mutation is selected automatically when results arrive; select
another row or **View** button to change it. The visualization shows the wild-type
interaction once in vaRRI, with both designed substitutions annotated at their
original signed sequence coordinates in dark green. A compact Plotly bar chart
beside it compares the `ww`, `wm`, `mw` and `mm` interaction energies and marks
`ww` with a dotted baseline; unavailable interactions remain empty. Session ZIPs
contain `parameter.json` and `copomus-mutations.tsv`. The JSON keeps native
IntaRNA settings and records the task and CopomuS settings under `gui`, so Load
restores the complete editable run. As with other GUI sessions, all data stays
in the tab and disappears on reload unless downloaded.

The WASM workflow vendors no generated runtime files in Git. Build staging
downloads Pyodide 314.0.7 from its npm release, verifies the pinned SHA-256,
Expand Down
2 changes: 1 addition & 1 deletion gui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ <h3 id="mutation-options-title">Compensatory mutation settings</h3>
<section data-role="visualization" aria-label="Compensatory mutation visualization">
<div class="visualization-heading"><h3 data-role="mutation-visualization-title">Compensatory-mutation visualization</h3><a data-role="mutation-varri-link" target="_blank" rel="noopener noreferrer" hidden>Open in vaRRI</a></div>
<p data-role="mutation-visualization-status" role="status">Select View on a mutation to inspect its annotated interaction and energy states.</p>
<p class="input-help">vaRRI shows the wild-type interaction with the designed query and target substitutions annotated in dark green. The chart compares the wild-type, single-mutant and compensatory energies.</p>
<p class="input-help">The first ranked mutation is selected automatically. vaRRI shows the wild-type interaction with the designed query and target substitutions annotated in dark green. The chart compares the wild-type, single-mutant and compensatory energies.</p>
<div class="mutation-visualization-layout">
<iframe data-role="mutation-varri-frame" title="Selected mutation annotated in vaRRI" referrerpolicy="no-referrer" hidden></iframe>
<div data-role="mutation-energy-plot" class="mutation-energy-plot" role="img" aria-label="Interaction energies for the selected mutation" hidden></div>
Expand Down
12 changes: 10 additions & 2 deletions scripts/wasm/test-browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,23 +724,31 @@ def predict():
priority_up = page.get_by_role('button', name='Move Query accessibility earlier in ranking priority')
priority_up.click()
priority_up.click()
mutation_visualization_count = len(visualization_urls)
predict()
mutation_session = active
mutation_session = page.locator(f'.session[data-session-id="{active.get_attribute("data-session-id")}"]')
mutation_view = mutation_session.locator('[data-role=mutation-results-view]')
expect(mutation_view).to_be_visible()
expect(mutation_view.locator('tbody tr')).to_have_count(1)
expect(mutation_view.locator('tbody tr')).to_contain_text('G1C&C10G')
mutation_view.locator('tbody tr').click()
expect(mutation_view.locator('tbody tr button')).to_have_attribute('aria-pressed', 'true')
expect(mutation_view.locator('[data-role=mutation-varri-frame]')).to_be_visible()
expect(mutation_view.locator('[data-role=mutation-energy-plot]')).to_be_visible()
expect(mutation_view.locator('[data-role=mutation-energy-plot] .barlayer')).to_be_visible()
assert len(visualization_urls) == mutation_visualization_count + 1
assert mutation_view.locator('[data-role=mutation-energy-plot] .xtick').all_text_contents() == ['ww', 'wm', 'mw', 'mm']
expect(mutation_view.locator('[data-role=mutation-energy-plot] .ytitle')).to_contain_text('E [kcal/mol]')
mutation_url = mutation_view.locator('[data-role=mutation-varri-frame]').get_attribute('src')
mutation_query = parse_qs(urlparse(mutation_url).query)
assert mutation_query['sequence'] == ['CCCUCCUCCC&GGGAGGAGGG']
assert mutation_query['mutations'] == ['1:10G:006400,2:1C:006400']
assert mutation_view.locator('[data-role=mutation-varri-frame]').bounding_box()['x'] < mutation_view.locator('[data-role=mutation-energy-plot]').bounding_box()['x']
mutation_session.locator('[data-role=toggle]').click()
expect(mutation_session.locator('[data-role=panel]')).not_to_be_visible()
mutation_session.locator('[data-role=toggle]').click()
expect(mutation_view.locator('[data-role=mutation-varri-frame]')).to_be_visible()
expect(mutation_view.locator('[data-role=mutation-energy-plot]')).to_be_visible()
assert len(visualization_urls) == mutation_visualization_count + 2
if args.screenshot:
path = Path(args.screenshot)
page.screenshot(path=str(path.with_name(f'{path.stem}-mutation.png')), full_page=True)
Expand Down
Loading