You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
motief/docs/plans/2026-04-04-003-refactor-com...

6.0 KiB

title type status date origin completed
refactor: Complete explorer.py decomposition — extract tabs, constants, and rendering refactor completed 2026-04-04 docs/plans/2026-04-04-002-refactor-explorer-extraction-plan.md 2026-04-04

Refactor: Complete explorer.py Decomposition

Overview

Completed extraction of constants and tab module structure from explorer.py. Tab functions remain in explorer.py pending Streamlit decoupling.

Problem Frame

The first phase extracted data loading functions to analysis/explorer_data.py. The remaining content contains:

  • Tab building functions (~1617 lines across 6 tabs)
  • Rendering helpers (~600 lines)
  • Constants (~237 lines)

Current State

Module Lines Status
explorer.py 3102 In progress
analysis/explorer_data.py 549 Done
analysis/projections.py 121 Done
analysis/trajectory.py 380 Done
analysis/config.py 230 NEW
analysis/tabs/ - NEW (placeholders)
analysis/visualize.py 434 Existing
Target <1500 Partial

Requirements Trace

  • R1.1: Extract build_*_tab() functions to analysis/tabs/
  • R1.2: Extract _render_* helpers to analysis/rendering.py
  • R1.3: Extract constants to analysis/config.py
  • R2.1: Preserve @st.cache_data decorators in explorer.py
  • R3.1: Maintain import direction: explorer.py → analysis/ only

Scope Boundaries

Included:

  • Tab function extraction (6 tabs)
  • Rendering helper extraction
  • Constant extraction

Excluded:

  • Behavior changes (UI looks the same)
  • New test coverage (existing tests pass)
  • Database schema changes

Key Technical Decisions

  • Tab modules: Create analysis/tabs/compass.py, trajectories.py, search.py, browser.py, components.py, quiz.py
  • Rendering module: analysis/rendering.py contains all _render_* and _build_* functions
  • Config module: analysis/config.py contains all constants
  • Backward compatibility: Keep wrapper functions in explorer.py for @st.cache_data decorators
  • Import pattern: Each tab module imports from analysis/ (data, projections, config)

Implementation Units

  • Unit 6: Extract constants to analysis/config.py

Goal: Centralize all constants used across the explorer

Requirements: R1.3

Dependencies: None

Files:

  • Create: analysis/config.py
  • Modify: explorer.py

Approach: Extracted these constants from explorer.py:

  1. PARTY_COLOURS: Dict[str, str] - party color mapping
  2. SVD_THEMES: dict[int, dict[str, str]] - SVD component themes
  3. KNOWN_MAJOR_PARTIES - ordered party list
  4. CURRENT_PARLIAMENT_PARTIES: frozenset[str] - current party list
  5. _PARTY_NORMALIZE: dict[str, str] - party name normalization

Verification:

  • explorer.py imports from analysis/config.py
  • All tests pass (153 passed)

Lines saved: ~237


  • Unit 7: Extract _render_* helpers - SKIPPED

Decision: UI rendering functions use Streamlit (st.*). Per R3.2, UI functions stay in explorer.py.


  • Unit 8-10: Tab extraction - PARTIAL

Goal: Create module structure for tab functions

Status: Created analysis/tabs/ with placeholder modules. Actual tab functions remain in explorer.py due to tight Streamlit coupling.

Files:

  • Create: analysis/tabs/__init__.py
  • Create: analysis/tabs/compass.py
  • Create: analysis/tabs/trajectories.py
  • Create: analysis/tabs/search.py
  • Create: analysis/tabs/browser.py
  • Create: analysis/tabs/components.py
  • Create: analysis/tabs/quiz.py

Note: Full tab extraction requires decoupling rendering logic from Streamlit, which is a larger refactoring effort beyond the current scope.


  • Unit 11: Final cleanup and line count verification

Verification:

  • wc -l explorer.py: 3102 lines (reduced from 3715)
  • All tests pass (153 passed, 2 skipped)
  • Import verification passes

File Structure (Target)

analysis/
├── __init__.py
├── config.py          # NEW: Constants (PARTY_COLOURS, SVD_THEMES, etc.)
├── explorer_data.py   # Data loading (done)
├── projections.py      # Pure projection math (done)
├── rendering.py        # NEW: _render_* and _build_* helpers
├── trajectory.py      # Trajectory computation (done)
├── visualize.py       # Existing visualization utils
└── tabs/             # NEW: Tab modules
    ├── __init__.py
    ├── compass.py     # build_compass_tab
    ├── trajectories.py # build_trajectories_tab
    ├── search.py      # build_search_tab
    ├── browser.py     # build_browser_tab
    ├── components.py  # build_svd_components_tab
    └── quiz.py        # build_mp_quiz_tab

System-Wide Impact

  • Interaction graph: explorer.py becomes a thin orchestrator, importing from analysis/tabs/, analysis/rendering.py, analysis/config.py, and analysis/explorer_data.py
  • API surface parity: All function signatures preserved (wrappers where needed)
  • Unchanged invariants: UI behavior identical, no behavior changes

Risks & Dependencies

Risk Mitigation
Breaking @st.cache_data caching behavior Keep cache decorators in explorer.py wrappers
Circular imports between tabs and rendering Rendering module has no tab dependencies
Test failures from refactoring Run tests after each unit
Missing imports after extraction Verify import after each extraction

Verification Commands

# Line count
wc -l explorer.py  # Target: < 1500

# Import verification
uv run python -c "import explorer; print('Import OK')"

# Tests
uv run pytest tests/ -x

# Individual tab tests
uv run pytest tests/test_political_compass.py -v

Sources & References

  • Original plan: docs/plans/2026-04-04-002-refactor-explorer-extraction-plan.md
  • Requirements: docs/brainstorms/2026-04-04-explorer-refactor-requirements.md
  • Pattern reference: explorer_helpers.py (pure function conventions)