T4: Repository pattern — ModelRepository, JsonFileRepository, ExcelRepository

- application/repositories/model_repository.py: ModelRepository Protocol
  with load(path) and save(model, path)
- infrastructure/repositories/json_file_repository.py: JsonFileRepository
  (вынесен из FinancialModel.save/load)
- infrastructure/repositories/excel_repository.py: ExcelRepository
  (бывший ExcelSync, переименован)
- FinancialModel.save/load удалены — теперь только данные
- infrastructure/sync/ удалён (ExcelSync перенесён в repositories/)
- infrastructure/cli/main.py и config.py используют JsonFileRepository +
  ExcelRepository через локальные singletons
- test_excel_sync.py переименован в test для ExcelRepository
- Новый test_repositories.py: 5 тестов для JsonFileRepository + протокол
- test_model.py: использован JsonFileRepository

Tests: 68/68 pass (было 63 + 5 новых).
This commit is contained in:
2026-10-08 16:50:06 +03:00
parent 363d440d53
commit 3f7268d14c
12 changed files with 190 additions and 92 deletions
+5 -2
View File
@@ -14,6 +14,7 @@ from domain import (
Transaction,
)
from infrastructure.cli.i18n import t
from infrastructure.repositories import JsonFileRepository
app = typer.Typer(name="config", help=t("config.help"))
console = Console()
@@ -21,15 +22,17 @@ console = Console()
DATA_DIR = Path("data")
MODEL_PATH = DATA_DIR / "model.json"
_json_repo = JsonFileRepository()
def _load_model() -> FinancialModel:
if MODEL_PATH.exists():
return FinancialModel.load(MODEL_PATH)
return _json_repo.load(MODEL_PATH)
return FinancialModel()
def _save_model(model: FinancialModel) -> None:
model.save(MODEL_PATH)
_json_repo.save(model, MODEL_PATH)
# ── base-currency ──────────────────────────────────────────────
+8 -7
View File
@@ -11,7 +11,7 @@ from infrastructure.cli.config import app as config_app
from infrastructure.cli.i18n import setup_i18n, t
from application.forecast import ForecastService
from application.scenarios import DEFAULT_SCENARIOS, ScenarioService
from infrastructure.sync.excel_sync import ExcelSync
from infrastructure.repositories import ExcelRepository, JsonFileRepository
try:
sys.stdout.reconfigure(encoding="utf-8")
@@ -28,15 +28,18 @@ MODEL_PATH = DATA_DIR / "model.json"
CURRENCY_OPTION = typer.Option(None, "--currency", "-c", help=t("cmd.forecast.opt.currency"))
_json_repo = JsonFileRepository()
_excel_repo = ExcelRepository()
def _load_model() -> FinancialModel:
if MODEL_PATH.exists():
return FinancialModel.load(MODEL_PATH)
return _json_repo.load(MODEL_PATH)
return FinancialModel()
def _save_model(model: FinancialModel) -> None:
model.save(MODEL_PATH)
_json_repo.save(model, MODEL_PATH)
def _get_converter(model: FinancialModel) -> CurrencyConverter:
@@ -205,9 +208,8 @@ def import_xlsx(
path: str = typer.Argument(..., help=t("cmd.import.arg.path")),
) -> None:
"""Импорт данных из Excel"""
sync = ExcelSync()
try:
model = sync.import_model(path)
model = _excel_repo.load(path)
_save_model(model)
console.print(t("cmd.import.ok",
accounts=len(model.accounts),
@@ -227,9 +229,8 @@ def export_xlsx(
) -> None:
"""Экспорт модели в Excel"""
model = _load_model()
sync = ExcelSync()
try:
sync.export_model(model, path)
_excel_repo.save(model, path)
console.print(t("cmd.export.ok", path=path))
except Exception as e:
console.print(f"[red]{t('cmd.export.err', error=str(e))}[/red]")