feat: Add daily energy breakdown by month to energy command#58
Conversation
Implements new --month option to show daily energy data for a specific month, complementing the existing --months option for monthly summaries. Features: - New '--month' option displays daily energy breakdown for a single month - Maintains existing '--months' option for monthly summary - Smart routing: single month shows daily data, multiple months show summary - Rich formatted output with progress bars and color-coded efficiency - Plain text fallback for non-Rich environments - Comprehensive error handling for invalid inputs Usage: # Daily breakdown (new) nwp-cli energy --year 2025 --month 12 # Monthly summary (existing) nwp-cli energy --year 2025 --months 10,11,12 Changes: - CLI command now supports both --month and --months options - Year parameter made optional in decorator, validated in function - New output formatters for daily energy data - Rich output formatter enhanced with daily energy table method - Handler logic routes to appropriate formatter based on month count Tests: All 378 tests pass Linting: All checks pass (0 errors)
…ndencies Make Click and Rich optional dependencies that are only required for the CLI, allowing the core library to be used without these dependencies. Changes: - Move 'click' from install_requires to extras_require[cli] - Update extras_require[cli] to include both click and rich - Update README with detailed installation instructions for: * Basic library installation (without CLI) * CLI installation with rich formatting * Optional rich library usage - Add CLI requirement note to Command Line Interface section - Improve installation documentation clarity Benefits: - Smaller dependency footprint for library-only users - Clear separation between library and CLI requirements - Better support for embedded/minimal environments - Optional rich formatting for enhanced output This change is backward compatible as the [cli] extra provides all dependencies needed for full CLI functionality.
Rich is only used by the CLI framework, so there's no practical use case for installing rich without click. Simplified documentation to only show two clear installation options: 1. Library only: pip install nwp500-python 2. Library with CLI: pip install nwp500-python[cli] This makes the installation instructions clearer and more accurate about when Rich is needed.
Since click was moved to optional [cli] extra, the test environment needs to include it to run CLI tests. Added 'cli' to extras in [testenv:default] so that both click and rich are installed during testing. This fixes the CI test failure where CLI modules fail to import due to missing click dependency.
Fixed inconsistent title style in README.rst where 'Quick Reference' was using level 3 underline (~~~) when it should use level 2 (---) to maintain proper reStructuredText hierarchy under the level 1 'Command Line Interface' section. This fixes the twine distribution check error: 'Inconsistent title style: skip from level 2 to 4'
There was a problem hiding this comment.
Pull request overview
This PR adds a daily energy breakdown feature to the CLI and refactors dependencies to make Click and Rich optional. The daily breakdown shows per-day consumption data when querying a single month, while multiple months continue to show monthly summaries. The dependency refactoring reduces the core library footprint from 5 to 3 required dependencies by moving Click and Rich to an optional [cli] extra.
Key changes:
- New
--monthoption for daily energy breakdown with rich formatting - Smart routing between daily/monthly views based on input
- Click and Rich moved to optional
[cli]extra in setup.cfg - Updated installation documentation with clear guidance on when to use
[cli]extra
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/nwp500/cli/main.py | Adds --month parameter validation and routing logic for daily vs monthly energy views |
| src/nwp500/cli/handlers.py | Implements conditional rendering based on single vs multiple months, improves device info formatting |
| src/nwp500/cli/output_formatters.py | Adds daily energy formatting functions with plain text and rich table support |
| src/nwp500/cli/rich_output.py | Implements rich-formatted daily energy tables with progress bars and color coding |
| setup.cfg | Moves Click from required to optional [cli] extra alongside Rich |
| tox.ini | Adds [cli] extra to test environment dependencies |
| README.rst | Documents basic vs CLI installation options and restructures installation sections |
src/nwp500/cli/output_formatters.py
Outdated
| for day_data in month_data.data: | ||
| total_wh = day_data.total_usage | ||
| hp_wh = day_data.heat_pump_usage | ||
| he_wh = day_data.heat_element_usage | ||
| hp_time = day_data.heat_pump_time | ||
| he_time = day_data.heat_element_time | ||
|
|
||
| # Use day index + 1 as day number (assuming data is ordered by day) | ||
| day_num = month_data.data.index(day_data) + 1 | ||
|
|
There was a problem hiding this comment.
Using .index() in a loop results in O(n²) complexity. Consider using enumerate() instead: for day_num, day_data in enumerate(month_data.data, start=1):
| for day_data in month_data.data: | |
| total_wh = day_data.total_usage | |
| hp_wh = day_data.heat_pump_usage | |
| he_wh = day_data.heat_element_usage | |
| hp_time = day_data.heat_pump_time | |
| he_time = day_data.heat_element_time | |
| # Use day index + 1 as day number (assuming data is ordered by day) | |
| day_num = month_data.data.index(day_data) + 1 | |
| for day_num, day_data in enumerate(month_data.data, start=1): | |
| total_wh = day_data.total_usage | |
| hp_wh = day_data.heat_pump_usage | |
| he_wh = day_data.heat_element_usage | |
| hp_time = day_data.heat_pump_time | |
| he_time = day_data.heat_element_time |
src/nwp500/cli/output_formatters.py
Outdated
| for day_data in month_data.data: | ||
| day_num = month_data.data.index(day_data) + 1 |
There was a problem hiding this comment.
Using .index() in a loop results in O(n²) complexity. Consider using enumerate() instead: for day_num, day_data in enumerate(month_data.data, start=1):
| for day_data in month_data.data: | |
| day_num = month_data.data.index(day_data) + 1 | |
| for day_num, day_data in enumerate(month_data.data, start=1): |
src/nwp500/cli/__main__.py
Outdated
| year: int | None, | ||
| months: str | None, | ||
| month: int | None, |
There was a problem hiding this comment.
The year parameter is documented as required but typed as optional (int | None). Consider making it truly required by using type=int, required=True in the click.option decorator instead of checking for None in the function body. This provides better error messages and matches the documented behavior.
src/nwp500/cli/__main__.py
Outdated
| if year is None: | ||
| _logger.error("--year is required") | ||
| return |
There was a problem hiding this comment.
The error message should use Click's built-in error handling for better UX. Since year is marked as required in the help text, either make it truly required in the decorator or use click.ClickException to provide a proper exit code: raise click.ClickException('--year is required')
Implemented the following improvements based on Copilot review: 1. Fixed O(n²) complexity in energy formatters - Replaced month_data.data.index(day_data) + 1 with enumerate(month_data.data, start=1) - Applied fix in both format_daily_energy_usage() and print_daily_energy_usage() - Improves performance from quadratic to linear time 2. Made year parameter truly required in CLI - Changed --year from required=False to required=True in @click.option - Updated function signature from year: int | None to year: int - Removes manual validation and lets Click handle requirement 3. Improved error handling using Click's standard exceptions - Replaced _logger.error() calls with click.ClickException() - Provides proper exit codes and consistent error formatting - Better user experience with standard CLI error messages All tests pass (378/378) No linting errors
Apply ruff formatting to improve code consistency.
Description
This PR implements two major improvements:
Features
1. Daily Energy Breakdown (New CLI Feature)
Implements new
--monthoption to show daily energy data for a specific month, complementing the existing--monthsoption for monthly summaries.New '--month' option:
Maintains '--months' option:
Smart routing:
Output formatting:
Comprehensive error handling:
2. Installation Documentation & Dependency Refactoring
Made Click optional, allowing the core library to be used without CLI framework. Rich formatting is included with Click in the CLI extra since it's only used for CLI output.
Changes:
Installation Options:
Core Dependencies (always required):
CLI Extra Dependencies (only when using [cli]):
Documentation improvements:
Usage Examples
Daily Breakdown (New Feature)
Shows daily energy consumption for December 2025 with:
Monthly Summary (Existing Feature)
Shows monthly summary across October-December 2025.
Code Review Improvements
Addressed all Copilot code review comments:
Performance: Eliminated O(n²) complexity
month_data.data.index(day_data) + 1withenumerate(month_data.data, start=1)format_daily_energy_usage()andprint_daily_energy_usage()Parameter Handling: Made year truly required
--yearfromrequired=Falsetorequired=Trueyear: int | Nonetoyear: intError Handling: Improved UX with Click exceptions
_logger.error()withclick.ClickException()Benefits
✓ Daily energy analysis for detailed consumption insights
✓ Smaller dependency footprint for library-only users (3 vs 5 dependencies)
✓ Clear separation between library and CLI requirements
✓ Better support for embedded/minimal environments
✓ Rich formatting available for CLI when needed
✓ Better code quality with improved performance and error handling
✓ 100% backward compatible - no breaking changes
✓ Clearer installation instructions in README
Testing & CI
✅ All 378 existing tests pass
✅ Linting: 0 errors, 227 pre-existing warnings
✅ Type checking: No new errors
✅ Manual testing: Daily and monthly modes verified
✅ Error cases: All validated
✅ Core library: Works without click dependency
✅ CLI: Works with [cli] extra
✅ CI Tests: All passing (Python 3.13 and 3.14)
✅ Build Distribution: PASSING
✅ Code review: All suggestions addressed
CI Status
Backward Compatibility
✅ Fully backward compatible
--monthsusage unchanged--monthparameterpip install nwp500-python[cli]Files Changed
src/nwp500/cli/__main__.py- Energy command enhancements and improved error handlingsrc/nwp500/cli/handlers.py- Handler logic with smart routingsrc/nwp500/cli/output_formatters.py- Daily energy formatters with performance improvementssrc/nwp500/cli/rich_output.py- Rich UI components for daily energysetup.cfg- Dependency restructuring (click to [cli] extra)README.rst- Installation documentationtox.ini- Test environment configurationCommits
feat: Add daily energy breakdown by month to nwp-cli energy command
docs: Update installation instructions for optional CLI and Rich dependencies
docs: Remove misleading 'Optional Rich Formatting' section
fix: Add cli extra to tox testing dependencies
fix: Correct RST title hierarchy in README
refactor: Address code review comments from Copilot