Skip to content

Comments

feat: add Streamlit web interface with interactive dashboard#6

Merged
mcaxtr merged 20 commits intomainfrom
worktree-feature/web-interface
Feb 24, 2026
Merged

feat: add Streamlit web interface with interactive dashboard#6
mcaxtr merged 20 commits intomainfrom
worktree-feature/web-interface

Conversation

@mcaxtr
Copy link
Owner

@mcaxtr mcaxtr commented Feb 24, 2026

Summary

Add a complete browser-based web interface for SPKMC built with Streamlit and Plotly. This enables self-service experiment management for users who prefer a GUI over the CLI, while maintaining full compatibility with the existing command-line workflow.

43 files changed, ~11,100 insertions, ~600 deletions across 15 focused commits.

What this PR adds

  • Web interface (spkmc/web/) -- Full Streamlit application with dashboard, experiment detail, and settings pages
  • spkmc web CLI command -- Launch the web interface with --port, --host, and --no-browser options
  • Plotly migration -- Replace matplotlib/seaborn with Plotly for interactive charts in both CLI and web
  • Subprocess execution -- Background simulation and AI analysis runners that survive browser refresh
  • Comprehensive test suite -- 127 unit tests for web modules + 49 Playwright E2E tests
  • CI pipeline -- E2E job in GitHub Actions with Chromium and Firefox

Features

Experiments Dashboard

  • Summary stat cards (total experiments, scenarios, completion rate, last activity)
  • Experiment cards with status badges and scenario counts
  • Create Experiment modal with full parameter configuration (network type, distribution, simulation params)
  • Click-through navigation to experiment detail

Experiment Detail

  • Global parameters display with three collapsible cards (Network, Distribution, Simulation)
  • Scenario cards with live status badges (Pending / Running / Completed / Failed)
  • Scenario detail modal with:
    • Interactive Plotly SIR charts with toggleable S/I/R traces
    • Chart type switching (Line / Area / Scatter)
    • Error bands for multi-run results
    • Multi-scenario comparison overlays
    • Export popover (JSON, CSV, Excel, Markdown, HTML)
  • Add/edit scenario flows with parameter override inheritance
  • Run All / Run Individual scenario buttons
  • AI Analysis integration (experiment-level and scenario-level)

Settings / Preferences

  • AI configuration (API key, model selection)
  • Chart preferences (height, colors, Plotly template)
  • Default simulation parameters
  • Storage path configuration
  • Danger zone with reset-to-defaults

Design System

  • Teal palette (#4A9E8E primary) with Plus Jakarta Sans typography
  • Dark sidebar with brand identity
  • Clean white cards with consistent spacing
  • CSS design tokens for maintainability

Architecture

spkmc/web/
├── app.py                    # Entry point, sidebar nav, CSS injection
├── config.py                 # JSON prefs + Streamlit secrets management
├── state.py                  # Typed session state accessors
├── plotting.py               # Core Plotly figure builders
├── components.py             # Reusable UI components (forms, badges, cards)
├── styles.py                 # CSS design system and card renderers
├── runner.py                 # Subprocess simulation runner
├── analysis_runner.py        # AI analysis subprocess runner
└── pages/
    ├── dashboard.py          # Experiments list
    ├── experiment_detail.py  # Single experiment view
    └── settings.py           # Preferences

Key design decisions

Decision Rationale
Subprocess execution Simulations survive browser refresh, page navigation, and UI interactions. Status tracked via filesystem IPC.
Everything is an experiment Even single runs are experiments with one scenario. Unified model simplifies storage and UI.
Parameter inheritance Global params at experiment level; scenarios only override what differs. Overrides visually highlighted.
Filesystem-first storage No database. Experiments, results, and status are portable JSON files.
Plotly over matplotlib Interactive charts that work identically in CLI (browser) and web (embedded in Streamlit).

Breaking changes

  • matplotlib/seaborn removed from core dependencies, replaced by Plotly
    • spkmc plot now produces interactive HTML charts (opens in browser) instead of matplotlib windows
    • Static image export (PNG/PDF/SVG) still works via kaleido
    • Visualizer.plot_result() API signature unchanged
  • New core dependencies: streamlit>=1.48.0, plotly>=5.18.0, kaleido>=0.2.1, humanize>=4.0.0

Test coverage

Unit tests (417 pass, 19 skip)

Module Tests Coverage
test_state.py Session state accessors 578 lines
test_config.py Config persistence and secrets 345 lines
test_runner.py Simulation runner lifecycle 382 lines
test_analysis_runner.py Analysis script generation 336 lines
test_plotting.py Plotly figure builders 354 lines
test_experiment_detail.py Scenario update logic, label collision, result lifecycle 352 lines

E2E tests (49 tests across 4 files)

File Tests What it covers
test_navigation.py 8 Sidebar nav, page routing, title, version, query params
test_dashboard.py 12 Stats cards, create modal, experiment cards, create flow
test_experiment_detail.py 20 Params, scenario cards, detail modal, charts, comparison, export
test_settings.py 10 Preference sections, inputs, reset

E2E uses pre-seeded fixture data with synthetic SIR curves -- no actual simulations run during tests.


Commit log

Commit Scope
chore: update author metadata and gitignore LICENSE, setup.cfg, .gitignore
chore: replace matplotlib/seaborn with plotly and add web deps pyproject.toml, requirements.txt, setup.cfg
feat(models): add global parameters field to Experiment Experiment model
feat(analysis): add scenario-level analysis and formatting AI analyzer, prompts
refactor(io): extract content builders in DataManager Export reuse for web
feat(web): add core infrastructure config, state, app entry
feat(web): add design system styles, components, plotting
refactor(visualization): migrate CLI plotting to plotly plots.py rewrite
feat(web): add simulation and analysis runners Subprocess management
feat(web): add dashboard, experiment detail, settings pages All three pages
feat(cli): add web command spkmc web launcher
test(web): add unit tests for web modules 6 test files
test(e2e): add Playwright E2E test suite 4 test files + fixtures
ci: add E2E job to GitHub Actions Chromium + Firefox
docs: comprehensive web interface documentation README.md

Test plan

  • pytest tests/ --ignore=tests/e2e -- all 417 unit tests pass
  • pytest tests/e2e/ --browser chromium -- E2E tests pass against running Streamlit server
  • spkmc web -- launches web interface, dashboard loads with experiment cards
  • Create experiment via modal, add scenarios, run simulation
  • Verify Plotly charts render with S/I/R toggles and comparison overlays
  • spkmc run -n er -d gamma -- CLI still works with Plotly output
  • spkmc plot <result.json> -- opens interactive chart in browser
  • Settings page persists preferences across sessions

Update author name from "mcaxtr" to "Marcus Castro" in LICENSE
and setup.cfg. Add .claude/, CLAUDE.md, and web runtime directories
to .gitignore.
Replace matplotlib and seaborn with plotly for interactive visualization.
Add streamlit, humanize, kaleido, and pydantic as core dependencies.
Register spkmc.web and spkmc.models packages in setuptools.
Add e2e optional dependency group for pytest-playwright.
Add pytest e2e marker definition.
Add parameters dict to the Experiment model for storing global default
parameters that scenarios inherit from. Wire it through from_config()
to preserve the field when loading experiment configurations.
Add analyze_scenario() method to AIAnalyzer for single-scenario
analysis with dedicated prompt and system prompt. Add formatting
guidelines to all analysis prompts for consistent markdown output
with section headers, blockquotes, and visual separators. Increase
experiment analysis max_tokens from 2000 to 2500.
Extract _build_markdown_content() and _build_html_content() as public
class methods, enabling the web interface to generate export data
without writing to disk. Add to_bytes() for in-memory serialization.
Handle missing kaleido gracefully for plot generation failures.
Add the foundational web interface modules:
- __init__.py: Package initialization with version info
- config.py: WebConfig class for JSON prefs and Streamlit secrets
  management, with SPKMC_WEB_CONFIG_FILE env var override
- state.py: Typed SessionState accessors for all Streamlit session
  state, preventing raw st.session_state access throughout the app
- app.py: Main Streamlit entry point with sidebar navigation,
  page routing, and CSS injection
Add the visual and charting layer:
- styles.py: Complete CSS design system with teal palette, Plus Jakarta
  Sans typography, dark sidebar, card renderers (stat cards, experiment
  cards, scenario cards), and responsive layout utilities
- components.py: Reusable UI components including network/distribution/
  simulation parameter forms, result metric cards, and status badges
- plotting.py: Core Plotly figure builders for SIR curves with error
  bands, state toggles, chart type switching, and multi-scenario
  comparison overlays
Replace matplotlib/seaborn with Plotly for all CLI visualization.
Import core figure builders from spkmc.web.plotting to share code
between CLI and web interface. Update Visualizer class to produce
interactive HTML charts (opens in browser) and support static image
export via kaleido. Update test assertions and docs code examples
for the new Plotly-based API.
Add subprocess-based execution engines:
- runner.py: SimulationRunner class that launches scenarios as
  background subprocesses with dynamic Python script generation,
  filesystem-based status tracking, atomic JSON writes, PID liveness
  checks, and dead-process detection
- analysis_runner.py: AnalysisRunner class for AI-powered analysis
  as background subprocesses, with backup/restore safety for existing
  analysis artifacts and proper API key propagation
Add the three main UI pages:
- dashboard.py: Experiments list with summary stat cards, experiment
  cards with status badges, create experiment modal with full parameter
  configuration, and empty state handling
- experiment_detail.py: Single experiment view with global parameter
  display, scenario cards, scenario detail modal with interactive
  Plotly charts, S/I/R toggles, chart type switching, multi-scenario
  comparison, export popover, and AI analysis integration. Includes
  add/edit scenario flows and update_scenario_in_experiment() for
  safe parameter editing with result file lifecycle management
- settings.py: Preferences page with AI configuration, chart
  preferences, default simulation parameters, storage paths,
  and danger zone reset functionality
Add 'spkmc web' CLI command with --port, --host, and --no-browser
options. Launches the Streamlit server as a subprocess with
preconfigured theme settings. Also update batch->experiment
terminology in comments for consistency.
Add comprehensive test suite for all web interface modules:
- test_state.py: SessionState accessors and typed getters/setters
- test_config.py: WebConfig JSON persistence and secrets management
- test_runner.py: SimulationRunner subprocess lifecycle and status
- test_analysis_runner.py: AnalysisRunner script generation and safety
- test_plotting.py: Plotly figure builders for SIR curves and overlays
- test_experiment_detail.py: Scenario update logic, label collision
  detection, and result file lifecycle on parameter changes
Add end-to-end test suite using Playwright for browser testing:
- conftest.py: Session-scoped Streamlit server fixture, pre-seeded
  experiment data with synthetic SIR curves, page helper functions
- fixtures/: Committed experiment definition (data.json) with
  result JSONs generated dynamically at runtime
- test_navigation.py: Sidebar nav, page routing, title, version
- test_dashboard.py: Stats cards, create modal, experiment cards,
  create experiment flow
- test_experiment_detail.py: Global params, scenario cards, detail
  modal with chart controls, comparison, export, and AI button state
- test_settings.py: Preference sections, inputs, reset button

Update pytest.ini to exclude E2E tests from default test runs and
register the e2e marker.
Add e2e job to GitHub Actions that runs Playwright tests on
ubuntu-latest with Python 3.11 after lint and test jobs pass.
Installs Chromium and Firefox browsers, uploads artifacts on failure.
Add --ignore=tests/e2e to the unit test step to prevent import
errors when pytest-playwright is not installed.
Expand README.md with full web interface documentation including
launch commands (spkmc web), detailed feature descriptions, architecture
diagram, design decisions, configuration guide, workflow walkthrough,
troubleshooting section, and development guide. Add complete project
structure covering all modules (web, analysis, CLI, core, tests).
Consolidate documentation from separate doc files into a single
authoritative reference.
- Bump minimum Python from 3.8 to 3.9 (streamlit>=1.48.0 dropped 3.8)
- Replace os.kill(pid, 0) with psutil.pid_exists() for cross-platform
  dead-process detection in runner.py, analysis_runner.py, and state.py
- Fix path assertion in test_runner.py to use repr() matching script
  embedding (Windows backslash escaping)
- Update CI matrix, pyproject.toml, setup.cfg, and black target-version
- Remove CLAUDE.md from git tracking (already in .gitignore)
- Reformat settings.py, styles.py, experiment_detail.py with black 26.x
- Update pre-commit black from 24.3.0 to 26.1.0 (2026 stable style)
- Pin CI black to >=26,<27 to prevent version drift
- Remove unused os import from runner.py (replaced by psutil)
- Remove unnecessary global statement in config.py (F824)
- Align CI flake8 extend-ignore with pre-commit config
- Use stHeading testid instead of stTitle (Streamlit 1.54+ changed it)
- Replace index-based card lookups with text-based lookups to handle
  card reordering when test_create_experiment_flow persists across
  browser passes (Chromium creates experiment, Firefox sees it at idx 0)
The experiment card text is rendered via st.markdown (non-clickable div),
while the actual navigation trigger is st.button inside the same
container. Find the container by text, then click its button element.
@mcaxtr mcaxtr merged commit a8f65a7 into main Feb 24, 2026
11 checks passed
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.

1 participant