#!/usr/bin/env python3 """Переиндексация метаданных и модулей конфигураций 1С → cache/1c_meta/index.sqlite. Параметры: --config, -c файл настроек утилиты (JSON) --baseconf, -b какие выгрузки 1С индексировать (crm3-26, target, …) Примеры: python tools/index/index_1c.py reindex -j 12 python tools/index/index_1c.py reindex --full -j 12 python tools/index/index_1c.py reindex --full -b crm3-26 -j 12 python tools/index/index_1c.py reindex -b target -b ws-rhana -t Documents,Catalogs python tools/index/index_1c.py status python tools/index/index_1c.py list Поиск: python tools/index/query_1c.py … """ from __future__ import annotations import argparse import sys from pathlib import Path HERE = Path(__file__).resolve().parent if str(HERE) not in sys.path: sys.path.insert(0, str(HERE)) from index1c.build import build_project_index, default_workers # noqa: E402 from index1c import read_version_file # noqa: E402 from index1c.cli import ( # noqa: E402 BaseconfError, add_tool_args, handle_baseconf_error, index_db_missing_message, make_parent_parser, resolve_project_root, validate_baseconfs, ) from index1c.config import ( # noqa: E402 CONFIG_ALIASES, DEFAULT_SKIP_TYPES, cache_dir, discover_configs, resolve_object_type, ) from index1c import db as dbmod # noqa: E402 def _parse_types(raw: str | None) -> set[str] | None: if not raw: return None types: set[str] = set() for part in raw.replace(";", ",").split(","): part = part.strip() if not part: continue types.add(resolve_object_type(part)) return types or None def _select_configs_for_reindex(args: argparse.Namespace, root: Path) -> list: """Какие конфигурации 1С индексировать.""" if args.baseconf: try: names = validate_baseconfs(root, args.baseconf, require_indexed=False) except BaseconfError as e: print(str(e), file=sys.stderr) sys.exit(e.exit_code) return discover_configs(root, names) # без -b: все известные с src/ return discover_configs(root, None) def cmd_reindex(args: argparse.Namespace) -> int: root = resolve_project_root(args) configs = _select_configs_for_reindex(args, root) if not configs: print( "Нет конфигураций для индексации (нужна папка /src/).\n" "Укажите -b crm3-26 или проверьте: python tools/index/index_1c.py list", file=sys.stderr, ) return 1 types = _parse_types(args.types) skip = set(DEFAULT_SKIP_TYPES) if args.include_skipped: skip.clear() if args.skip_types: for t in args.skip_types.replace(";", ",").split(","): t = t.strip() if t: skip.add(resolve_object_type(t)) workers = args.workers if args.workers and args.workers > 0 else default_workers() # --full без -b → пересоздать всю БД wipe_db = bool(args.full and not args.baseconf) print( f"Режим: {'full-wipe' if wipe_db else ('full' if args.full else 'incremental')}, " f"workers={workers}, baseconf={[c.name for c in configs]}, modules={not args.no_modules}" ) summary = build_project_index( configs, root, full=args.full or wipe_db, wipe_db=wipe_db, types=types, skip_types=skip, index_modules=not args.no_modules, workers=workers, write_md=args.md, ) print( f"Готово: {summary['db']} ({summary['elapsed_sec']}s) " f"meta+{summary['meta_parsed']}/skip{summary['meta_skipped']} " f"mod+{summary['modules_parsed']}/skip{summary['modules_skipped']} " f"removed={summary['files_removed']} err={summary['errors']}" ) return 0 def cmd_status(args: argparse.Namespace) -> int: root = resolve_project_root(args) db_path = dbmod.global_db_path(cache_dir(root)) if not db_path.is_file(): print(index_db_missing_message(root), file=sys.stderr) return 1 conn = dbmod.connect(db_path, readonly=True) try: print(f"БД: {db_path} ({db_path.stat().st_size // (1024 * 1024)} МБ)") for row in conn.execute("SELECT key, value FROM meta ORDER BY key"): print(f" meta.{row['key']} = {row['value']}") print("Конфигурации 1С в индексе (--baseconf):") for c in dbmod.list_configs(conn): print( f"- {c['name']}: objects={c['objects']} fields={c['fields']} " f"refs={c['refs']} modules={c['modules']} @ {c['updated_at']}" ) finally: conn.close() return 0 def cmd_list_configs(args: argparse.Namespace) -> int: root = resolve_project_root(args) found = discover_configs(root, None) print("Выгрузки 1С в репозитории (для --baseconf / -b):") for c in found: aliases = [k for k, v in CONFIG_ALIASES.items() if v == c.name] alias_s = f" алиасы: {', '.join(aliases)}" if aliases else "" print(f"- {c.name}{alias_s}") db_path = dbmod.global_db_path(cache_dir(root)) if db_path.is_file(): conn = dbmod.connect(db_path, readonly=True) try: print("\nПроиндексировано (в index.sqlite):") for c in dbmod.list_configs(conn): print(f"- {c['name']}") finally: conn.close() else: print(f"\nИндекс отсутствует. {index_db_missing_message(root)}") return 0 def build_parser() -> argparse.ArgumentParser: tool_parent = make_parent_parser(include_baseconf=False) reindex_parent = make_parent_parser(include_baseconf=True, baseconf_mode="index") p = argparse.ArgumentParser( description="Построение единого SQLite-индекса метаданных и BSL-модулей 1С.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=( "Конфигурация 1С: --baseconf (-b). Файл настроек утилиты: --config (-c).\n" "Алиасы baseconf: target→crm3-26, source→crm3-dev" ), ) p.add_argument( "--version", action="version", version=f"%(prog)s {read_version_file()}", ) add_tool_args(p) sub = p.add_subparsers(dest="cmd", required=True) r = sub.add_parser( "reindex", help="Построить / обновить index.sqlite", parents=[reindex_parent], formatter_class=argparse.RawDescriptionHelpFormatter, description="Построение или инкрементальное обновление index.sqlite.", ) r.add_argument("--types", "-t", help="Только типы: Documents,Catalogs,рс,рн,…") r.add_argument("--skip-types", help="Дополнительно пропустить типы") r.add_argument("--include-skipped", action="store_true") r.add_argument( "--full", action="store_true", help="Полная пересборка (вся БД или только указанные -b)", ) r.add_argument("--no-modules", action="store_true", help="Не индексировать .bsl") r.add_argument( "--workers", "-j", type=int, default=0, help=f"Потоков (по умолчанию {default_workers()})", ) r.add_argument("--md", action="store_true", help="Markdown в cache/1c_meta/md/") r.set_defaults(func=cmd_reindex) st = sub.add_parser( "status", help="Состояние index.sqlite", parents=[tool_parent], formatter_class=argparse.RawDescriptionHelpFormatter, description="Размер БД, meta и список проиндексированных конфигураций.", ) st.set_defaults(func=cmd_status) lc = sub.add_parser( "list", help="Выгрузки в репозитории и в индексе", parents=[tool_parent], formatter_class=argparse.RawDescriptionHelpFormatter, description="Выгрузки 1С с src/ в репозитории и что уже в index.sqlite.", ) lc.set_defaults(func=cmd_list_configs) return p def main(argv: list[str] | None = None) -> int: parser = build_parser() args = parser.parse_args(argv) return int(args.func(args)) if __name__ == "__main__": raise SystemExit(main())