T6: Decompose CLI — main.py → 9 command files, config.py → config_cmd.py
Split infrastructure/cli/: - main.py (330 LOC) → 9 files in infrastructure/cli/commands/: * init.py, forecast.py, scenario.py, whatif.py, compare.py * import_xlsx.py, export_xlsx.py, analyze.py, info.py - config.py (428 LOC) → commands/config_cmd.py (15 sub-commands) - main.py → app.py (entry point: registers all commands) - New: paths.py (MODEL_PATH, DATA_DIR) - New: services.py (composition root + helpers: load_model, save_model, build_services, resolve_currency, convert_value, fmt_amount) - cli/__init__.py (empty) удалён Each command module: <100 LOC. pyproject.toml: cf = infrastructure.cli.app:app tests/test_cli.py: import обновлён на infrastructure.cli.app CLI behavior идентично, все команды работают. Tests: 67/67 pass.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
"""Composition root + общие хелперы для CLI-команд."""
|
||||
from __future__ import annotations
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from rich.console import Console
|
||||
|
||||
from application.forecast import ForecastService
|
||||
from application.scenarios import ScenarioService
|
||||
from domain import CurrencyConverter, FinancialModel
|
||||
from infrastructure.ai.assistant import AssistantService
|
||||
from infrastructure.cli.paths import MODEL_PATH
|
||||
from infrastructure.repositories import JsonFileRepository
|
||||
|
||||
|
||||
console = Console()
|
||||
|
||||
_json_repo = JsonFileRepository()
|
||||
|
||||
|
||||
def load_model() -> FinancialModel:
|
||||
if MODEL_PATH.exists():
|
||||
return _json_repo.load(MODEL_PATH)
|
||||
return FinancialModel()
|
||||
|
||||
|
||||
def save_model(model: FinancialModel) -> None:
|
||||
_json_repo.save(model, MODEL_PATH)
|
||||
|
||||
|
||||
def build_services(model: FinancialModel) -> tuple[ForecastService, ScenarioService, AssistantService, CurrencyConverter]:
|
||||
"""Composition root: собрать граф сервисов с зависимостями."""
|
||||
converter = CurrencyConverter(model.exchange_rates)
|
||||
forecast_service = ForecastService(model)
|
||||
scenario_service = ScenarioService(model)
|
||||
assistant_service = AssistantService(
|
||||
model, forecast_service=forecast_service, converter=converter
|
||||
)
|
||||
return forecast_service, scenario_service, assistant_service, converter
|
||||
|
||||
|
||||
def resolve_currency(model: FinancialModel, currency: str | None) -> tuple[str, str]:
|
||||
target = currency or model.base_currency
|
||||
converter = CurrencyConverter(model.exchange_rates)
|
||||
symbol = converter.get_symbol(target)
|
||||
return target, symbol
|
||||
|
||||
|
||||
def convert_value(
|
||||
converter: CurrencyConverter, amount: Decimal, from_curr: str, to_curr: str
|
||||
) -> Decimal:
|
||||
if to_curr == from_curr:
|
||||
return amount
|
||||
return converter.convert(amount, from_curr, to_curr)
|
||||
|
||||
|
||||
def fmt_amount(amount: Decimal | float, currency: str, symbol: str) -> str:
|
||||
if isinstance(amount, Decimal):
|
||||
return f"{symbol}{amount:,.2f}"
|
||||
return f"{symbol}{amount:,.2f}"
|
||||
Reference in New Issue
Block a user