# v1.9.8 - Sampler Utility and Iterator Extraction (2025-12-29)

## What Changed?

This release continues Phase 2 decomposition by extracting utility functions and iterators from the Sampler class into dedicated modules.
The `_helpers.py` module provides type conversion and progress display utilities, while `_iterators.py` contains count-based and time-based iteration logic.
All functionality is preserved through the existing modular package structure with zero breaking changes.

---

## What's New

### Utility Functions Module (`_helpers.py`)

**What it does**:
Extracts three static utility functions from the Sampler class into a dedicated helpers module. These functions handle type conversion, progress display, and test data generation.

**Functions extracted**:

1. **`sanitize(event: list) -> list`**: Converts raw CSV string values to proper Python types
   - Empty strings → `None`
   - Numbers → `int` (if whole) or `float` (if decimal)
   - Non-numbers → remain as strings

2. **`tqdm_wrapper(iterable, desc=None, show=True)`**: Conditionally wraps iterables with progress bars
   - `show=True`: Displays tqdm progress bar
   - `show=False`: Returns iterable unchanged
   - Useful for interactive vs batch processing

3. **`mock_sample(*args, **kwargs)`**: Generates hardcoded test data `["mock_event"]`
   - Used for unit testing without hardware
   - Accepts any arguments (ignored)

**Code examples**:

```python
# Type conversion
from haniwers.v1.daq.sampler._helpers import sanitize

raw_csv_row = ["100", "200.5", "", "invalid"]
cleaned = sanitize(raw_csv_row)
# Returns: [100, 200.5, None, "invalid"]

# Progress bar display
from haniwers.v1.daq.sampler._helpers import tqdm_wrapper

# Show progress
for item in tqdm_wrapper(range(1000), desc="Processing", show=True):
    process(item)

# No progress (batch mode)
for item in tqdm_wrapper(range(1000), desc="Processing", show=False):
    process(item)
```

### Iterator Functions Module (`_iterators.py`)

**What it does**:
Extracts iteration logic into generator functions for count-based and time-based acquisition loops. These generators encapsulate the control flow for both sampling modes.

**Functions extracted**:

1. **`count_based_iterator(count: int)`**: Generator that yields `count` times
   - Used for `acquire_by_count()` mode
   - Yields exactly N times before stopping

2. **`time_based_iterator(duration: float, sleep_interval: float)`**: Generator for fixed-duration loops
   - Used for `acquire_by_time()` mode
   - Yields until duration elapsed
   - Respects sleep_interval between yields

**Code examples**:

```python
from haniwers.v1.daq.sampler._iterators import count_based_iterator

# Collect exactly 1000 events
for _ in count_based_iterator(1000):
    event = device.read_event()
    csv_writer.write_row(event)

# Time-based acquisition (30 seconds, 0.1s sleep)
for _ in time_based_iterator(30.0, 0.1):
    event = device.read_event()
    csv_writer.write_row(event)
```

---

## Installation

### Quick Start

```bash
# Get the release
git checkout v1.9.8

# Setup
task env:setup

# Run CLI
haniwers-v1 --help
```

---

## What's Different from the Last Version?

### ✅ Added

- New `src/haniwers/v1/daq/sampler/_helpers.py` module with utility functions (sanitize, tqdm_wrapper, mock_sample)
- New `src/haniwers/v1/daq/sampler/_iterators.py` module with generator functions (`count_based_iterator`, `time_based_iterator`)
- Comprehensive docstrings with usage examples for all new modules

### 🔧 Changed

- Reorganized `src/haniwers/v1/daq/sampler/_base.py` to import helpers and iterators from new modules
- No changes to public API or CLI functionality

### 🐛 Fixed

- No bug fixes in this release

---

## Is It Safe to Upgrade?

**Backward Compatible**: ✅ Yes

All existing imports and APIs continue to work without modification.
The extraction of utility functions and iterators into dedicated modules is a pure refactoring with no breaking changes.

---

## Tests Passed

- ✅ Builds without errors
- ✅ All 133 unit tests passing (DAQ module)
- ✅ Sampler functionality tests passing (10 tests covering count/time/run modes)
- ✅ MAC address tests passing (3 essential tests)
- ✅ Helper function tests verified
- ✅ Iterator function tests verified
- ✅ Pre-commit hooks passing (ruff format, YAML, etc.)

---

## Release Details

- **Date**: 2025-12-29
- **Version**: v1.9.8
- **Files Changed**: 3 (created `_helpers.py`, `_iterators.py`; updated `_base.py` imports)
- **Commits**:
  - 6292a99: refactor(daq/sampler): extract static helper functions to _helpers.py
  - b0ce175: refactor(daq/sampler): extract iterator functions to _iterators.py
  - 81b0d45: bump: version 1.9.7 → 1.9.8

---

## Next Steps

### Phase 2.2 (v1.9.9 planned): Reader and Writer Module Decomposition

```text
src/haniwers/v1/daq/sampler/
├── __init__.py           # Re-exports all classes
├── _base.py              # Sampler base class (initialization, coordination)
├── _reader.py            # EventReader (read_event, stream_events, collect_events)
├── _writer.py            # EventWriter (save_events, acquire_by_count, acquire_by_time)
├── _iterators.py         # Iterators (count_based_iterator, time_based_iterator) ✓
└── _helpers.py           # Static helpers (sanitize, tqdm_wrapper, mock_sample) ✓
```

**Benefits of Phase 2.2**:

- Single Responsibility Principle (SRP) fully applied
- Each module ~300-400 lines instead of 1,591 lines
- Improved testability and code organization
- Clearer relationships between reader, writer, and orchestration layers

### Phase 3 (v1.10.0 planned): Technical Debt Resolution

- Complete removal of deprecated `DaqConfig` and `ScanConfig` models
- Removal of TODO comments with implementations
- Integration test stabilization
- Performance optimization if needed

### v1 → v2 Transition

This refactoring prepares v1 for stable maintenance mode while providing:

- Clean codebase for v2 migration
- Reusable helper modules for v2
- Clear architectural patterns to follow

---

## Architecture Decision

This modularization follows the **Single Responsibility Principle (SRP)**:

- Each module has one reason to change
- Dependencies flow downward (high-level → utilities)
- Public API remains unchanged

This pattern will be applied to other large modules in future releases:

- `config/model.py` (1,184 lines) → Phase 2.2
- `cli/options.py` (770 lines) → Phase 2.3

See [project constitution](/.specify/memory/constitution.md) for more details on architectural principles.
