Fixes the 21 analytics CLIs that crashed with ModuleNotFoundError: No module named 'services.analytics.base' on any real command, repointing them at the canonical services.common.database.get_db.
Builds services/common/commands.py — a declarative CommandLine/Subcommand framework on top of services.common.cli: subcommand declarations build the argparse parser, one connection is opened per invocation and always closed (even on error), results render via canonical print_json, and failures surface as clean Error: + exit 1.
Migrates all 21 analytics CLI modules onto the framework, preserving each documented CLI surface: flags, dest mappings, defaults, --json toggles, and main() entry points (so python3 -m services.analytics.* and the meta-CLI mount are unchanged).
Adds needs_db=False support for pure-computation commands (e.g. evapotranspiration et0) that must run without a database connection.
Fixes latent bugs found along the way: crop_phenology list-stages referenced an unbound db and now works; evapotranspiration water-balance/store-et closed connections outside a finally (leak on error).
Verification
638 tests pass across every migrated module's test file plus tests/test_common_commands.py.
CLI smoke suite 17/17; test_cli_analytics_main and test_common_foundations green.
All migrated modules compile; zero import argparse remaining in the 21 modules.
Net ~1,200 lines of duplicated CLI/DB/JSON boilerplate removed across the 21 modules.
21 analytics CLIs crashed with ModuleNotFoundError on every command
because their main() lazily imported from .base.get_db while
services.analytics.base does not exist. Repoint them at the canonical
services.common.database.get_db.
Introduce services.common.commands.CommandLine, a declarative
subcommand framework built on services.common.cli (run, print_json,
get_connection) that removes per-module argparse/JSON/connection
boilerplate. Migrate crop_phenology and digital_finance onto it,
preserving their full CLI surfaces and output formats; this also
fixes crop_phenology list-stages, which referenced an unbound db.
Add unit tests for the framework.
Generated with Codebuff 🤖
Co-Authored-By: Codebuff <noreply@codebuff.com>
Move the hand-rolled argparse mains in the remaining 19 analytics
modules onto services.common.commands (CommandLine/Subcommand), the
declarative framework built on services.common.cli. Every module keeps
its documented `main()` entry point, --json flag, dest mappings, and
defaults, so `python3 -m services.analytics.*` invocations and the
meta-CLI mount are unchanged.
Adds `needs_db=False` support so pure-computation commands (e.g.
evapotranspiration et0) run without opening a database connection, and
fixes the legacy leak where water-balance/store-et closed connections
outside a finally block.
Net ~350 lines of duplicated CLI boilerplate removed across the 19
modules (21 modules total with the earlier crop_phenology/digital_finance
migration).
Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Summary
ModuleNotFoundError: No module named 'services.analytics.base'on any real command, repointing them at the canonicalservices.common.database.get_db.services/common/commands.py— a declarativeCommandLine/Subcommandframework on top ofservices.common.cli: subcommand declarations build the argparse parser, one connection is opened per invocation and always closed (even on error), results render via canonicalprint_json, and failures surface as cleanError:+ exit 1.destmappings, defaults,--jsontoggles, andmain()entry points (sopython3 -m services.analytics.*and the meta-CLI mount are unchanged).needs_db=Falsesupport for pure-computation commands (e.g.evapotranspiration et0) that must run without a database connection.crop_phenology list-stagesreferenced an unbounddband now works;evapotranspiration water-balance/store-etclosed connections outside afinally(leak on error).Verification
tests/test_common_commands.py.test_cli_analytics_mainandtest_common_foundationsgreen.import argparseremaining in the 21 modules.