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.
182 lines
6.0 KiB
182 lines
6.0 KiB
---
|
|
title: "refactor: Complete explorer.py decomposition — extract tabs, constants, and rendering"
|
|
type: refactor
|
|
status: completed
|
|
date: 2026-04-04
|
|
origin: docs/plans/2026-04-04-002-refactor-explorer-extraction-plan.md
|
|
completed: 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
|
|
|
|
- [x] **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
|
|
|
|
---
|
|
|
|
- [x] **Unit 7: Extract `_render_*` helpers** - SKIPPED
|
|
|
|
**Decision:** UI rendering functions use Streamlit (`st.*`). Per R3.2, UI functions stay in explorer.py.
|
|
|
|
---
|
|
|
|
- [x] **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.
|
|
|
|
---
|
|
|
|
- [x] **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
|
|
|
|
```bash
|
|
# 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)
|
|
|