"""tiasdata コマンドのエントリポイント."""
import argparse
import sys
from contextlib import closing
from pydantic import ValidationError
from tiasdata import __version__, catalog
from tiasdata.config import load_config
def _cmd_init(args: argparse.Namespace) -> int:
config = load_config()
paths = config.catalog
for directory in (paths.root, paths.store_dir, paths.bundles_dir, paths.backups_dir):
directory.mkdir(parents=True, exist_ok=True)
with closing(catalog.connect(paths.catalog_path, create=True)) as conn:
catalog_result = catalog.apply_migrations(conn, "catalog")
kinds = catalog.count_kinds(conn)
paths.identity.parent.mkdir(parents=True, exist_ok=True)
with closing(catalog.connect(paths.identity, create=True)) as conn:
identity_result = catalog.apply_migrations(conn, "identity")
# 出力にはデータの場所(パス)を出さない.件数と版だけを示す(SPEC_03「出力の規則」)
_print_migration("目録", catalog_result)
_print_migration("対応表", identity_result)
kinds_summary = "・".join(f"{category} {count} 件" for category, count in kinds.items())
print(f"種類: {kinds_summary}")
return 0
def _print_migration(label: str, result: catalog.MigrationResult) -> None:
if result.applied:
print(f"{label}: スキーマの版 {result.before} → {result.after}({result.applied} 件適用)")
else:
print(f"{label}: スキーマの版 {result.after}(最新.適用なし)")
def _format_validation_error(e: ValidationError) -> str:
# 入力値(パス等)を出さず,キーの位置と理由だけを 1 行にまとめる
details = "; ".join(
f"{'.'.join(str(part) for part in error['loc']) or '(全体)'}: {error['msg']}"
for error in e.errors(include_input=False, include_url=False)
)
return f"設定ファイルの検証に失敗した({e.error_count()} 件): {details}"
def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="tiasdata", description="舌診研究の共有データ基盤")
parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
subparsers = parser.add_subparsers(dest="command", metavar="<コマンド>")
init = subparsers.add_parser(
"init",
help="設定ファイルに従って目録・ストアを作り,マイグレーションを適用する",
description="設定ファイルに従って目録・ストア・対応表を作り,未適用のマイグレーションを適用する."
"既にある場合は不足分だけ適用する",
)
init.set_defaults(handler=_cmd_init)
return parser
def _use_utf8_output() -> None:
# Windows ではパイプ・SSH 越しの出力が cp932 になり,手元の UTF-8 端末で化けるため
for stream in (sys.stdout, sys.stderr):
reconfigure = getattr(stream, "reconfigure", None)
if reconfigure is not None:
reconfigure(encoding="utf-8")
def main(argv: list[str] | None = None) -> int:
"""CLI を実行する.
Args:
argv: 引数(省略時は `sys.argv[1:]`).
Returns:
終了コード(0: 成功,1: 入力・データの問題).
"""
_use_utf8_output()
parser = _build_parser()
args = parser.parse_args(argv)
handler = getattr(args, "handler", None)
if handler is None:
parser.print_help()
return 0
try:
return handler(args)
except ValidationError as e:
# ValidationError は ValueError の派生だが,既定の表示は複数行で入力値を含むため
# 個別に整形する
print(f"エラー: {_format_validation_error(e)}", file=sys.stderr)
return 1
except ValueError as e:
message = " ".join(str(e).splitlines())
print(f"エラー: {message}", file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())