Skip to content

Add reusable linked-grid template + FileManager results store#395

Open
t0mdavid-m wants to merge 9 commits into
mainfrom
claude/kind-heisenberg-u6dVm
Open

Add reusable linked-grid template + FileManager results store#395
t0mdavid-m wants to merge 9 commits into
mainfrom
claude/kind-heisenberg-u6dVm

Conversation

@t0mdavid-m

Copy link
Copy Markdown
Member

Summary

Introduces a production-ready, tool-agnostic linked-grid visualization stack for OpenMS-Insight components, along with a SQLite-indexed results store in FileManager. This enables any downstream app (FLASHApp, quantms-web, etc.) to render cross-linked multi-panel dashboards with a single function call, while persisting layouts and managing large datasets efficiently.

Key Changes

New: src/view/grid.py — Reusable Linked Grid Template

  • render_linked_grid() — Core function that renders one experiment's grid of OpenMS-Insight components with a shared StateManager for cross-linking (clicks in one panel filter others). Enforces ≤3 columns per row (oracle invariant) and supports per-component height overrides.
  • LayoutManager — Full UI for editing/saving/uploading grid layouts. Parameterized by component vocabulary and storage keys so it works identically across FLASHDeconv, FLASHTnT, FLASHQuant, etc. Persists trimmed internal-name layouts + side-by-side flag to FileManager.
  • Store Protocol — Minimal structural interface (get_results, store_data, result_exists, remove_results) so the grid never imports concrete FileManager; any object implementing these four methods works.

Distills two near-identical FLASH manager implementations into one frozen, vendorable module with zero tool/MS-specific knowledge.

Enhanced: src/workflow/FileManager.py — SQLite-Indexed Results Store

  • store_data(dataset_id, name_tag, data) — Automatically detects format and stores:
    • Polars/Pandas DataFrames → parquet (.pq) for performance
    • Other data → compressed pickle (.pkl.gz)
    • Indexed in SQLite by (dataset_id, name_tag) for fast lookup
  • get_results(dataset_id, name_tags) — Retrieves stored data with proper format restoration (pandas DataFrame, polars LazyFrame, or pyarrow Dataset)
  • result_path(dataset_id, name_tag) — Returns on-disk parquet PATH for Insight components' data_path= argument (subprocess preprocessing + disk cache, no in-memory load)
  • store_file() / parquet_sink() — File storage with atomic rename and SQLite registration
  • Pickle serialization support — Enables LayoutManager to persist layout JSON through FileManager

New: src/common/common.py::show_linked_grid()

  • Thin wrapper over render_linked_grid() that owns multi-experiment + side-by-side page concern
  • One independent StateManager per experiment (no cross-experiment leakage)
  • Renders two experiments side-by-side when exactly two + side_by_side=True; otherwise stacks with dividers

New: content/visualization_template.py — Linked Grid Demo Page

  • Self-contained showcase: Table ↔ LinePlot ↔ Heatmap ↔ SequenceView cross-linking
  • Exercises LayoutManager (edit/save/upload layouts) and multi-experiment rendering
  • Uses example parquet fixtures under example-data/insight/

New: example-data/insight/ — Example Fixtures

  • _make_example.py — Generator script for tiny, hand-built parquet files (20 scans, 400 peaks, 3 sequences)
  • spectra.parquet — Master table (scan_id, rt, ms_level, precursor_mz, n_peaks)
  • peaks.parquet — Per-peak long format (scan_id, peak_id, mass, intensity, is_annotated, ion_label)
  • heat.parquet — Peak map (scan_id, rt, mass, intensity, peak_id)
  • sequences.parquet — Per-scan sequences (scan_id, sequence, precursor_charge)

Stable IDs enable cross-linking: peak_id is globally unique, reused across

https://claude.ai/code/session_017kD4FyAsNvW6VFTZwVvSne

claude added 7 commits June 3, 2026 12:04
…ager + demo)

- src/view/grid.py (NEW): tool-agnostic render_linked_grid(layout, builders, state_key, ...)
  + parameterized LayoutManager (folds the two FLASH*LayoutManager classes: <=3 cols,
  N rows/experiments, side-by-side, validation, JSON save/load). builders = comp_name ->
  zero-arg factory -> BaseComponent over one shared StateManager. No data/hashing (moved
  into Insight components). AST-verified: zero MS/FLASH identifiers.
- src/view.py -> src/view/__init__.py (package; preserves raw_data_viewer's `from src import view`).
- src/workflow/FileManager.py: ported the (dataset_id, name) results store + get_results(as_path=True)
  + result_path() returning the .pq path to feed Insight data_path=.
- src/common/common.py: show_linked_grid(layout, builders, *, tool, side_by_side) one-liner
  (one StateManager per experiment).
- content/visualization_template.py (NEW) + app.py registration: Table<->LinePlot<->Heatmap<->
  SequenceView linked-grid demo + LayoutManager + side-by-side over example-data/insight/ parquet.
- tests/test_view_grid.py (+13): construct-smoke for all 4 components, render_linked_grid,
  show_linked_grid, LayoutManager round-trip, and the data_path subprocess path.

Verified: pytest 74 passed/2 skipped; parse + construct-smoke green.

https://claude.ai/code/session_017kD4FyAsNvW6VFTZwVvSne
- _handle_setting_buttons: validate the trimmed (internal-name) uploaded
  layout BEFORE expanding, matching the oracle handleSettingButtons (the
  (... needed) dependency check is a no-op on the trimmed form; it fires
  later at Save time). Validating expanded labels wrongly rejected
  hand-crafted uploads.
- drop unused typing imports (Any, Sequence).
test_show_linked_grid_one_state_manager_per_experiment compared id() of
StateManagers it didn't retain, so GC could reuse an id within a run and make
the id-based set flaky in full-session order (passed in isolation / the gate,
but could report 1 distinct id instead of 2). Assert on the stable _session_key
(demo__exp0 / demo__exp1) — the actual one-StateManager-per-experiment invariant.
Verification harness only; no change to any reviewed unit.
Round-5 finding 3-grid-003: uploading a layout JSON with a wholly-empty
experiment ([]) wiped the whole layout. expand() dropped the empty experiment,
so len(layout) < num_experiments(=len(uploaded)) tripped the
reset-on-count-mismatch and replaced the upload with blanks. The oracle
handleSettingButtons inline-expand keeps an empty experiment as a [] stub, so
the counts match and nothing is reset. Add expand(drop_empty_experiments=) and
pass False on the upload path (edit mode still drops empties). Test added.
… 4 Dockerfiles)

The template viewer page (visualization_template.py) imports openms-insight, but it
was not installed by any Dockerfile. Add an insight-build stage (node:21) to all four
Dockerfiles (Dockerfile{,.arm}, Dockerfile_simple{,.arm}) that clones the openms-insight
branch claude/kind-heisenberg-u6dVm, builds its Vue bundle, syncs dist into the package,
then pip-installs the source tree (hatchling force-includes the bundled dist). Installs
from source so no PyPI publish is required.
…esh-clone fix)

In a fresh clone openms_insight/js-component/ does not exist (only held the gitignored
dist/), so cp had no parent dir. mkdir -p it first. Validated against a fresh clone:
clone -> npm build -> sync -> pip wheel bundles the Vue dist (incl. index.js).
@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@t0mdavid-m, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 59 minutes and 58 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 421bec48-25b8-48fc-b422-38808dd6fab4

📥 Commits

Reviewing files that changed from the base of the PR and between 6aac16b and 4e64166.

⛔ Files ignored due to path filters (4)
  • example-data/insight/heat.parquet is excluded by !**/*.parquet
  • example-data/insight/peaks.parquet is excluded by !**/*.parquet
  • example-data/insight/sequences.parquet is excluded by !**/*.parquet
  • example-data/insight/spectra.parquet is excluded by !**/*.parquet
📒 Files selected for processing (14)
  • .github/workflows/ci.yml
  • Dockerfile
  • Dockerfile.arm
  • Dockerfile_simple
  • Dockerfile_simple.arm
  • app.py
  • content/visualization_template.py
  • example-data/insight/_make_example.py
  • requirements.txt
  • src/common/common.py
  • src/view/__init__.py
  • src/view/grid.py
  • src/workflow/FileManager.py
  • tests/test_view_grid.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/kind-heisenberg-u6dVm

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

claude added 2 commits June 5, 2026 09:34
…ayer)

The migration added module-level "import polars as pl" to src/workflow/FileManager.py
(parquet/as_path support for the Insight data-layer), but polars was not in
requirements.txt -- only pyarrow (transitive via streamlit) was. The GUI test suite
(pytest test_gui.py) installs requirements.txt and imports FileManager via the
Workflow pages, so all 12 launches failed with "No module named 'polars'".
Pin polars==1.41.2 (the proven version; FLASHApp uses polars>=1.0.0).

Note: requirements.txt is pip-compiled; the external pyproject.toml source should
also list polars so future recompiles keep it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants