refactor: introduce weather capability composition - #71
Conversation
📝 WalkthroughWalkthroughAdds capability-based provider composition for weather contexts, including metadata, optional AQICN air-quality supplementation, dated-context routing, CLI wiring, documentation, and async test coverage. ChangesCapability composition
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant CapabilityProviderSet
participant WeatherProvider
participant AQICNProvider
CLI->>CapabilityProviderSet: construct provider composition
CapabilityProviderSet->>WeatherProvider: fetch weather context
WeatherProvider-->>CapabilityProviderSet: WeatherContextSnapshot
CapabilityProviderSet->>AQICNProvider: fetch missing current air quality
AQICNProvider-->>CapabilityProviderSet: AirQualitySnapshot
CapabilityProviderSet-->>CLI: composed weather context
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #71 +/- ##
========================================
Coverage 99.75% 99.76%
========================================
Files 39 41 +2
Lines 7424 7580 +156
Branches 416 425 +9
========================================
+ Hits 7406 7562 +156
Misses 13 13
Partials 5 5 ☔ View full report in Codecov by Harness. |
1075795 to
9a3b918
Compare
|
/agentic_review |
Code Review by Qodo
1.
|
9a3b918 to
a7440df
Compare
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit a7440df |
a7440df to
706f574
Compare
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 706f574 |
706f574 to
ecf4e29
Compare
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit ecf4e29 |
PR Summary by QodoRefactor weather providers into composable capability sets
AI Description
Diagram
High-Level Assessment
Files changed (6)
|
| return await self.fetch(latitude, longitude, forecast_date=forecast_date) | ||
|
|
||
|
|
||
| async def _fetch_context( |
There was a problem hiding this comment.
1. Duplicated dated dispatch 🐞 Bug ⚙ Maintainability
CapabilityProviderSet introduces _fetch_context() that re-implements the same “current vs forecast_date” dispatch already provided by weather_context.fetch_weather_context(), creating two sources of truth that can drift in behavior and error semantics. This is especially risky because both paths are exercised in production (service.py uses fetch_weather_context(), while CapabilityProviderSet uses _fetch_context()).
Agent Prompt
### Issue description
`weather_briefing/capabilities.py` defines `_fetch_context()` to route between `fetch()` and `fetch_for_date()`, but the repo already has `weather_briefing/weather_context.py:fetch_weather_context()` that does the same routing. Keeping both increases the chance of future drift (e.g., one adds new validation or different error messages while the other doesn’t).
### Issue Context
- `CapabilityProviderSet.fetch()` currently calls `_fetch_context()`.
- `BriefingService` still calls `fetch_weather_context()` directly.
### Fix Focus Areas
- Prefer a single shared dispatch implementation (either call `fetch_weather_context()` from `capabilities.py`, or move dispatch into a shared helper used by both).
- If the callable-guard behavior is desired, consider adding it to the shared helper so both code paths behave consistently.
- weather_briefing/capabilities.py[61-114]
- weather_briefing/weather_context.py[665-676]
- weather_briefing/service.py[331-338]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
|
Code review by qodo was updated up to the latest commit ecf4e29 |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
weather_briefing/cli.py (2)
477-480: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRedundant recomputation of the already-built
active_namesfilter for logging.
active_names(built in the loop above) already contains exactly the providers for whichname != WeatherProviderName.QWEATHER or _qweather_is_configured(settings). This generator recomputes the identical condition instead of reusingactive_names, risking future drift if the filter logic changes in only one place.♻️ Reuse active_names instead of recomputing the filter
_LOGGER.info( "Weather provider order providers=%s", - ",".join(name for name in names if name != WeatherProviderName.QWEATHER or _qweather_is_configured(settings)), + ",".join(active_names), )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@weather_briefing/cli.py` around lines 477 - 480, Update the Weather provider order log in the surrounding flow to reuse the already-built active_names collection instead of regenerating and refiltering names. Preserve the existing comma-separated logging format and the active_names ordering.
414-437: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winProvider-capability classification table is a Python constant.
_WEATHER_PROVIDER_METADATAmaps provider name → capability set — a classification table. The repo already hasprovider_defaults.jsonplusreference_string_tuple(...)infrastructure used for comparable reference data (e.g.weather_provider_order,qweather_lifestyle_index_typesinconfig.py). Consider moving this mapping into that same JSON file for consistency, since adding/adjusting a provider's declared capabilities would then not require a code change.As per coding guidelines, "Keep domain reference data such as geographic bounds, classification tables, and matching patterns in validated data files rather than Python constants."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@weather_briefing/cli.py` around lines 414 - 437, Move the provider-to-capabilities classification from _WEATHER_PROVIDER_METADATA in weather_briefing/cli.py into provider_defaults.json, using the existing reference_string_tuple(...) configuration infrastructure and validation patterns. Update the metadata-loading logic to read the JSON-backed values while preserving the current ProviderCapabilities structure and provider behavior.Source: Coding guidelines
weather_briefing/capabilities.py (1)
61-86: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCore capability layer hardcodes a specific vendor's env var name.
self.air_qualityis typed as the genericAirQualityProvider | None, but the raisedWeatherContextErrormessages at lines 75 and 85 hardcode "AQICN_API_TOKEN"/"AQICN fallback". If a non-AQICN air-quality provider is ever plugged into this slot, the error text becomes misleading. As per coding guidelines,**/*.pyshould "keep core data platform-neutral and place vendor or delivery syntax in adapters," so this vendor-specific wording belongs at the CLI/adapter boundary (e.g., surfaced viaair_quality_metadata/config validation) rather than baked intoCapabilityProviderSet.♻️ Example: keep the core message provider-neutral
- raise WeatherContextError("Weather source did not provide air quality; configure AQICN_API_TOKEN") + raise WeatherContextError("Weather source did not provide air quality and no air-quality provider is configured")- raise WeatherContextError("Weather source did not provide air quality and AQICN fallback failed") from None + raise WeatherContextError("Weather source did not provide air quality and the configured air-quality provider failed") from None🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@weather_briefing/capabilities.py` around lines 61 - 86, Update CapabilityProviderSet.fetch to remove AQICN-specific wording from both WeatherContextError messages. Use provider-neutral descriptions for missing or failed air-quality fallback, and leave vendor-specific token/configuration guidance to the CLI or adapter validation layer.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@weather_briefing/capabilities.py`:
- Around line 61-86: Update CapabilityProviderSet.fetch to remove AQICN-specific
wording from both WeatherContextError messages. Use provider-neutral
descriptions for missing or failed air-quality fallback, and leave
vendor-specific token/configuration guidance to the CLI or adapter validation
layer.
In `@weather_briefing/cli.py`:
- Around line 477-480: Update the Weather provider order log in the surrounding
flow to reuse the already-built active_names collection instead of regenerating
and refiltering names. Preserve the existing comma-separated logging format and
the active_names ordering.
- Around line 414-437: Move the provider-to-capabilities classification from
_WEATHER_PROVIDER_METADATA in weather_briefing/cli.py into
provider_defaults.json, using the existing reference_string_tuple(...)
configuration infrastructure and validation patterns. Update the
metadata-loading logic to read the JSON-backed values while preserving the
current ProviderCapabilities structure and provider behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c645762b-4342-431b-9d4c-5a134ea432e3
📒 Files selected for processing (6)
docs/design.mddocs/notes.mdtests/test_capabilities.pytests/test_cli.pyweather_briefing/capabilities.pyweather_briefing/cli.py
What changed
Introduce a composable capability layer for weather, air quality, and supplementary regional providers. Existing weather providers now run through the new abstraction while Open-Meteo remains the primary weather source.
Why
This establishes the extension boundary needed to add regional data sources without growing provider-specific conditionals in the briefing service.
Validation
Summary by CodeRabbit
New Features
Documentation
Tests