154 lines
6.5 KiB
Python
Executable File
154 lines
6.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
staging_inspect.py
|
|
==================
|
|
Инспектор и анализатор ТЕСТОВОЙ базы данных (oracle_passports_staging.json).
|
|
Позволяет сравнивать тестовую базу с основной, просматривать карточки и делать выгрузки.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import argparse
|
|
from pathlib import Path
|
|
from typing import Dict, Any
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from oracle_enricher.enricher.passport_schema import (
|
|
PROMPTS_78,
|
|
render_passport_markdown,
|
|
count_filled_fields,
|
|
)
|
|
|
|
MAIN_DB_PATH = PROJECT_ROOT / "assets" / "data" / "oracle_passports.json"
|
|
STAGING_DB_PATH = PROJECT_ROOT / "assets" / "data" / "oracle_passports_staging.json"
|
|
OUT_DIR = PROJECT_ROOT / "out" / "staging_cards"
|
|
|
|
def load_db(path: Path) -> Dict[str, Dict[str, Any]]:
|
|
if not path.exists():
|
|
print(f"[ERROR] База не найдена: {path}")
|
|
sys.exit(1)
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
|
|
def print_comparison_stats(main_db: Dict[str, Any], staging_db: Dict[str, Any]) -> None:
|
|
total_words = len(staging_db)
|
|
|
|
main_filled = sum(e.get("filled_count", 0) for e in main_db.values())
|
|
staging_filled = sum(e.get("filled_count", 0) for e in staging_db.values())
|
|
delta = staging_filled - main_filled
|
|
max_total = total_words * 78
|
|
|
|
main_complete = sum(1 for e in main_db.values() if e.get("filled_count", 0) >= 78)
|
|
staging_complete = sum(1 for e in staging_db.values() if e.get("filled_count", 0) >= 78)
|
|
|
|
print("\n" + "═" * 70)
|
|
print("📊 СРАВНИТЕЛЬНЫЙ АНАЛИЗ: ОСНОВНАЯ БАЗА vs ТЕСТОВАЯ БАЗА (STAGING)")
|
|
print("═" * 70)
|
|
print(f" Всего знаков в реестре: {total_words}")
|
|
print(f" Заполнено ячеек в ОСНОВНОЙ: {main_filled} / {max_total} ({main_filled/max_total*100:.2f}%)")
|
|
print(f" Заполнено ячеек в ТЕСТОВОЙ: {staging_filled} / {max_total} ({staging_filled/max_total*100:.2f}%)")
|
|
print(f" Прирост данных в тестовой базе: +{delta} ячеек (+{delta/max_total*100:.2f}% всего каталога)")
|
|
print(f" Полных карточек (78/78): ОСНОВНАЯ: {main_complete} | ТЕСТОВАЯ: {staging_complete}")
|
|
print("─" * 70)
|
|
print(f"{'РАЗДЕЛ КАТАЛОГА':<40} {'ОСНОВНАЯ':>12} {'ТЕСТОВАЯ':>12} {'ПРИРОСТ':>10}")
|
|
print("─" * 70)
|
|
|
|
for sec in ["I", "II", "III", "IV", "V", "VI"]:
|
|
sec_fields = [p.key for p in PROMPTS_78 if p.section_num == sec]
|
|
sec_max = len(sec_fields) * total_words
|
|
|
|
cnt_main = sum(1 for e in main_db.values() for k in sec_fields if e.get("fields", {}).get(k))
|
|
cnt_staging = sum(1 for e in staging_db.values() for k in sec_fields if e.get("fields", {}).get(k))
|
|
sec_delta = cnt_staging - cnt_main
|
|
|
|
sec_name = f"Раздел {sec}"
|
|
print(f"{sec_name:<40} {cnt_main:>6}/{sec_max} {cnt_staging:>6}/{sec_max} {f'+{sec_delta}':>10}")
|
|
|
|
print("═" * 70 + "\n")
|
|
|
|
def find_entry(db: Dict[str, Any], query: str) -> Optional[Dict[str, Any]]:
|
|
q = query.strip().lower()
|
|
if q.isdigit():
|
|
idx_str = str(int(q))
|
|
if idx_str in db:
|
|
return db[idx_str]
|
|
for e in db.values():
|
|
if e.get("word", "").lower() == q:
|
|
return e
|
|
return None
|
|
|
|
def show_diff(main_db: Dict[str, Any], staging_db: Dict[str, Any], query: str) -> None:
|
|
main_e = find_entry(main_db, query)
|
|
stag_e = find_entry(staging_db, query)
|
|
|
|
if not stag_e:
|
|
print(f"[ERROR] Знак «{query}» не найден в тестовой базе.")
|
|
return
|
|
|
|
word = stag_e.get("word", "")
|
|
idx = stag_e.get("id", 0)
|
|
|
|
print(f"\n═ Построчный DIFF для #{idx:03d} «{word.upper()}» (Основная -> Тестовая) ═")
|
|
main_fields = main_e.get("fields", {}) if main_e else {}
|
|
stag_fields = stag_e.get("fields", {})
|
|
|
|
diff_count = 0
|
|
for p in PROMPTS_78:
|
|
mv = main_fields.get(p.key, "")
|
|
sv = stag_fields.get(p.key, "")
|
|
if mv != sv:
|
|
diff_count += 1
|
|
print(f"\n[#{p.index:02d} | Раздел {p.section_num}] {p.title}:")
|
|
if mv:
|
|
print(f" ОСНОВНАЯ : {mv}")
|
|
else:
|
|
print(" ОСНОВНАЯ : (пусто)")
|
|
print(f" ТЕСТОВАЯ : {sv}")
|
|
|
|
print(f"\nВсего различий: {diff_count} полей из 78.\n")
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Инспектор тестовой базы данных Оракула 999")
|
|
parser.add_argument("--stats", action="store_true", help="Сравнить статистику основной и тестовой базы")
|
|
parser.add_argument("--view", type=str, help="Просмотреть канонический Markdown карточки из тестовой базы (ID или слово)")
|
|
parser.add_argument("--diff", type=str, help="Показать построчный diff между основной и тестовой базой для знака")
|
|
parser.add_argument("--export-card", type=str, help="Экспортировать карточку из тестовой базы в файл out/staging_cards/...")
|
|
|
|
args = parser.parse_args()
|
|
|
|
main_db = load_db(MAIN_DB_PATH)
|
|
staging_db = load_db(STAGING_DB_PATH)
|
|
|
|
if args.stats or (not args.view and not args.diff and not args.export_card):
|
|
print_comparison_stats(main_db, staging_db)
|
|
|
|
if args.view:
|
|
e = find_entry(staging_db, args.view)
|
|
if e:
|
|
print(render_passport_markdown(e))
|
|
else:
|
|
print(f"[ERROR] Знак «{args.view}» не найден.")
|
|
|
|
if args.diff:
|
|
show_diff(main_db, staging_db, args.diff)
|
|
|
|
if args.export_card:
|
|
e = find_entry(staging_db, args.export_card)
|
|
if e:
|
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
idx_str = f"{e.get('id', 0):03d}"
|
|
word = e.get("word", "")
|
|
out_file = OUT_DIR / f"{idx_str}_{word}_staging.md"
|
|
with open(out_file, "w", encoding="utf-8") as f:
|
|
f.write(render_passport_markdown(e))
|
|
print(f"✓ Карточка сохранена в: {out_file}")
|
|
else:
|
|
print(f"[ERROR] Знак «{args.export_card}» не найден.")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|