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 toanalysis/tabs/ - R1.2: Extract
_render_*helpers toanalysis/rendering.py - R1.3: Extract constants to
analysis/config.py - R2.1: Preserve
@st.cache_datadecorators 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.pycontains all_render_*and_build_*functions - Config module:
analysis/config.pycontains all constants - Backward compatibility: Keep wrapper functions in explorer.py for
@st.cache_datadecorators - 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:
PARTY_COLOURS: Dict[str, str]- party color mappingSVD_THEMES: dict[int, dict[str, str]]- SVD component themesKNOWN_MAJOR_PARTIES- ordered party listCURRENT_PARLIAMENT_PARTIES: frozenset[str]- current party list_PARTY_NORMALIZE: dict[str, str]- party name normalization
Verification:
explorer.pyimports fromanalysis/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, andanalysis/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)