368 lines
16 KiB
Python
368 lines
16 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Скрипт инициализации и миграции базы данных 1000 Паспортов Знаков «Оракула 999».
|
|
Создает assets/data/oracle_passports.json и связывает все 78 полей
|
|
с существующими данными (words_data.dart, oracle_kb.json, traditions_canon.json).
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
sys.path.insert(0, os.path.abspath("oracle_enricher"))
|
|
from enricher.passport_schema import (
|
|
PROMPTS_78,
|
|
empty_passport_record,
|
|
render_passport_markdown,
|
|
count_filled_fields,
|
|
)
|
|
|
|
DART_WORDS_PATH = "lib/data/words_data.dart"
|
|
KB_PATH = "assets/data/oracle_kb.json"
|
|
TRADITIONS_PATH = "assets/data/traditions_canon.json"
|
|
PASSPORTS_OUT_PATH = "assets/data/oracle_passports.json"
|
|
PASSPORTS_ENRICHER_PATH = "oracle_enricher/data/oracle_passports.json"
|
|
|
|
|
|
def load_words_from_dart():
|
|
with open(DART_WORDS_PATH, "r", encoding="utf-8") as f:
|
|
content = f.read()
|
|
|
|
pattern = re.compile(
|
|
r'WordItem\s*\(\s*id:\s*(\d+),\s*word:\s*"([^"]+)",\s*partOfSpeech:\s*"([^"]+)",\s*frequency:\s*([\d\.]+),\s*category:\s*"([^"]+)",\s*description:\s*"(.*?)",\s*\)',
|
|
re.DOTALL
|
|
)
|
|
words = []
|
|
for m in pattern.findall(content):
|
|
wid = int(m[0])
|
|
words.append({
|
|
"id": wid,
|
|
"word": m[1].strip(),
|
|
"pos": m[2].strip(),
|
|
"frequency": float(m[3]),
|
|
"category": m[4].strip(),
|
|
"description": m[5].strip(),
|
|
})
|
|
return words
|
|
|
|
|
|
def load_kb():
|
|
if not os.path.exists(KB_PATH):
|
|
return {}
|
|
with open(KB_PATH, "r", encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
# Нормализуем ключи: по слову и по id
|
|
kb_map = {}
|
|
for k, v in data.items():
|
|
if isinstance(v, dict):
|
|
kb_map[k.lower().strip()] = v
|
|
if "id" in v:
|
|
kb_map[str(v["id"])] = v
|
|
return kb_map
|
|
|
|
|
|
def load_traditions():
|
|
if not os.path.exists(TRADITIONS_PATH):
|
|
return {}
|
|
with open(TRADITIONS_PATH, "r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
|
|
|
|
def build_tradition_lookup(traditions_data):
|
|
"""Индексирует станции традиций по словам."""
|
|
lookup = {}
|
|
for canon_id, canon in traditions_data.items():
|
|
if not isinstance(canon, dict):
|
|
continue
|
|
canon_name = canon.get("name", canon_id)
|
|
stations = canon.get("stations", [])
|
|
for st in stations:
|
|
st_name = st.get("name", "")
|
|
st_symbol = st.get("symbol", "")
|
|
st_lore = st.get("lore", "")
|
|
|
|
# 1. Primary
|
|
prim = st.get("primary")
|
|
if isinstance(prim, dict) and prim.get("word"):
|
|
pw = str(prim["word"]).lower().strip()
|
|
lookup.setdefault(pw, []).append({
|
|
"canon_id": canon_id,
|
|
"canon_name": canon_name,
|
|
"station_name": st_name,
|
|
"symbol": st_symbol,
|
|
"lore": st_lore,
|
|
"is_primary": True,
|
|
})
|
|
elif isinstance(prim, str) and prim.strip():
|
|
pw = prim.lower().strip()
|
|
lookup.setdefault(pw, []).append({
|
|
"canon_id": canon_id,
|
|
"canon_name": canon_name,
|
|
"station_name": st_name,
|
|
"symbol": st_symbol,
|
|
"lore": st_lore,
|
|
"is_primary": True,
|
|
})
|
|
|
|
# 2. Cluster / Intersections
|
|
cluster = st.get("cluster", [])
|
|
if isinstance(cluster, list):
|
|
for cl in cluster:
|
|
if isinstance(cl, dict) and cl.get("word"):
|
|
cw = str(cl["word"]).lower().strip()
|
|
lookup.setdefault(cw, []).append({
|
|
"canon_id": canon_id,
|
|
"canon_name": canon_name,
|
|
"station_name": st_name,
|
|
"symbol": st_symbol,
|
|
"lore": st_lore,
|
|
"is_primary": False,
|
|
})
|
|
elif isinstance(cl, str) and cl.strip():
|
|
cw = cl.lower().strip()
|
|
lookup.setdefault(cw, []).append({
|
|
"canon_id": canon_id,
|
|
"canon_name": canon_name,
|
|
"station_name": st_name,
|
|
"symbol": st_symbol,
|
|
"lore": st_lore,
|
|
"is_primary": False,
|
|
})
|
|
return lookup
|
|
|
|
|
|
def main():
|
|
print("=" * 70)
|
|
print("ИНИЦИАЛИЗАЦИЯ И МИГРАЦИЯ БАЗЫ ДАННЫХ 1000 ПАСПОРТОВ ОРАКУЛА 999")
|
|
print("=" * 70)
|
|
|
|
words = load_words_from_dart()
|
|
print(f"Загружено слов из Dart: {len(words)}")
|
|
|
|
kb = load_kb()
|
|
print(f"Загружено записей базы знаний oracle_kb: {len(kb)}")
|
|
|
|
traditions = load_traditions()
|
|
trad_lookup = build_tradition_lookup(traditions)
|
|
print(f"Проиндексировано слов с мантическими станциями: {len(trad_lookup)}")
|
|
|
|
# Если уже есть существующая база паспортов — загружаем для объединения
|
|
existing_passports = {}
|
|
if os.path.exists(PASSPORTS_OUT_PATH):
|
|
try:
|
|
with open(PASSPORTS_OUT_PATH, "r", encoding="utf-8") as f:
|
|
raw_p = json.load(f)
|
|
if isinstance(raw_p, dict):
|
|
existing_passports = {str(v.get("id", k)): v for k, v in raw_p.items()}
|
|
elif isinstance(raw_p, list):
|
|
existing_passports = {str(item["id"]): item for item in raw_p}
|
|
print(f"Найдено существующих паспортов в базе: {len(existing_passports)}")
|
|
except Exception as e:
|
|
print(f"Ошибка чтения старых паспортов: {e}")
|
|
|
|
passports_db = {}
|
|
total_filled_across_db = 0
|
|
|
|
for w in words:
|
|
wid = w["id"]
|
|
wid_str = str(wid)
|
|
id_pad = str(wid).zfill(3)
|
|
word_clean = w["word"].lower().strip()
|
|
|
|
kb_entry = kb.get(word_clean) or kb.get(wid_str) or {}
|
|
existing_p = existing_passports.get(wid_str) or {}
|
|
|
|
# Базовая структура
|
|
record = empty_passport_record({
|
|
"id": wid,
|
|
"word": w["word"],
|
|
"pos": w["pos"],
|
|
"category": w["category"],
|
|
"emoji": kb_entry.get("emoji", existing_p.get("emoji", "")),
|
|
})
|
|
|
|
# Если уже были сохраненные поля из предыдущих запусков
|
|
if "fields" in existing_p:
|
|
for k, v in existing_p["fields"].items():
|
|
if v and v.strip():
|
|
record["fields"][k] = v.strip()
|
|
|
|
f = record["fields"]
|
|
|
|
# ── РАЗДЕЛ I ──
|
|
if not f.get("index_and_name"):
|
|
emojis = record["emoji"] or "✦"
|
|
f["index_and_name"] = f"{id_pad} | {w['word'].upper()} | {emojis}"
|
|
if not f.get("grammatical_class"):
|
|
f["grammatical_class"] = w["pos"]
|
|
if not f.get("functional_category"):
|
|
f["functional_category"] = w["category"]
|
|
|
|
# Сетевой статус
|
|
stations = trad_lookup.get(word_clean, [])
|
|
if not f.get("network_status"):
|
|
if stations:
|
|
primary_names = [f"{s['canon_name']}: {s['station_name']}" for s in stations if s["is_primary"]]
|
|
cross_names = [f"{s['canon_name']}: {s['station_name']}" for s in stations if not s["is_primary"]]
|
|
if primary_names:
|
|
f["network_status"] = f"Узловая станция (Первичное слово: {', '.join(primary_names[:2])})"
|
|
else:
|
|
f["network_status"] = f"Узловая станция (Пересечение: {', '.join(cross_names[:2])})"
|
|
else:
|
|
f["network_status"] = "Свободный знак поля"
|
|
|
|
# ── РАЗДЕЛ II: Кросс-мантическая сеть ──
|
|
trad_mapping = {
|
|
"runes": "mantic_runes",
|
|
"ogham": "mantic_ogham",
|
|
"tarot": "mantic_tarot",
|
|
"lenormand": "mantic_lenormand",
|
|
"kabbalah": "mantic_kabbalah",
|
|
"geomancy": "mantic_geomancy",
|
|
"iching": "mantic_iching",
|
|
"alchemy": "mantic_alchemy",
|
|
"western_zodiac": "mantic_zodiac_west",
|
|
"zodiac": "mantic_zodiac_chinese",
|
|
"days_of_week": "mantic_septener",
|
|
"months_of_year": "mantic_dodecade",
|
|
"numerology": "mantic_numerology",
|
|
"cosmology": "mantic_cosmology",
|
|
}
|
|
|
|
station_laws_acc = []
|
|
for s in stations:
|
|
c_id = s["canon_id"]
|
|
field_key = trad_mapping.get(c_id)
|
|
if field_key and not f.get(field_key):
|
|
role_str = "Первичное слово" if s["is_primary"] else "Пересечение"
|
|
f[field_key] = f"[{s['station_name']} {s['symbol']} / Роль: {role_str} / Сакральный узел]"
|
|
if s.get("lore"):
|
|
station_laws_acc.append(f"{s['station_name']}: {s['lore']}")
|
|
|
|
# Слои (layers) из базы знаний
|
|
for l in kb_entry.get("layers", []):
|
|
sys_name = l.get("system", "").lower()
|
|
title = l.get("title", "")
|
|
law = l.get("law", "")
|
|
if "рун" in sys_name and not f.get("mantic_runes"):
|
|
f["mantic_runes"] = f"[{title} / Роль: Слой / {law[:60]}]"
|
|
elif "таро" in sys_name and not f.get("mantic_tarot"):
|
|
f["mantic_tarot"] = f"[{title} / Роль: Слой / {law[:60]}]"
|
|
elif "огам" in sys_name and not f.get("mantic_ogham"):
|
|
f["mantic_ogham"] = f"[{title} / Роль: Слой / {law[:60]}]"
|
|
elif "ленорман" in sys_name and not f.get("mantic_lenormand"):
|
|
f["mantic_lenormand"] = f"[{title} / Роль: Слой / {law[:60]}]"
|
|
elif "каббал" in sys_name and not f.get("mantic_kabbalah"):
|
|
f["mantic_kabbalah"] = f"[{title} / Роль: Слой / {law[:60]}]"
|
|
elif "геомант" in sys_name and not f.get("mantic_geomancy"):
|
|
f["mantic_geomancy"] = f"[{title} / Роль: Слой / {law[:60]}]"
|
|
elif "и цзин" in sys_name and not f.get("mantic_iching"):
|
|
f["mantic_iching"] = f"[{title} / Роль: Слой / {law[:60]}]"
|
|
elif "алхим" in sys_name and not f.get("mantic_alchemy"):
|
|
f["mantic_alchemy"] = f"[{title} / Роль: Слой / {law[:60]}]"
|
|
|
|
if not f.get("mantic_station_laws") and station_laws_acc:
|
|
f["mantic_station_laws"] = " • ".join(station_laws_acc[:3])
|
|
|
|
# ── РАЗДЕЛ III: Языки и графика ──
|
|
kb_langs = kb_entry.get("languages", {})
|
|
lang_mapping = {
|
|
"шумерский": "script_sumer_akkad",
|
|
"древнеегипетский": "script_egypt",
|
|
"иврит": "script_hebrew",
|
|
"древнегреческий": "script_antiquity",
|
|
"латынь": "script_antiquity",
|
|
"древнеирландский": "script_celtic",
|
|
"древнескандинавский": "script_runic_germanic",
|
|
"старославянский": "script_slavic",
|
|
"санскрит": "script_india_sanskrit",
|
|
"авестийский": "script_persia",
|
|
"древнекитайский": "script_china",
|
|
"японский": "script_japan",
|
|
"арабский": "script_islamic",
|
|
}
|
|
for lang_name, lang_val in kb_langs.items():
|
|
field_k = lang_mapping.get(lang_name.lower())
|
|
if field_k and not f.get(field_k) and lang_val:
|
|
f[field_k] = str(lang_val)
|
|
|
|
# ── РАЗДЕЛ IV: Трехполярная матрица ──
|
|
if not f.get("state_neutral"):
|
|
clean_desc = re.sub(r'\[.*?\]\s*', '', w['description']).strip()
|
|
if clean_desc:
|
|
f["state_neutral"] = clean_desc
|
|
if not f.get("state_creative") and kb_entry.get("core"):
|
|
f["state_creative"] = kb_entry["core"]
|
|
if not f.get("state_destructive") and kb_entry.get("shadow"):
|
|
f["state_destructive"] = kb_entry["shadow"]
|
|
|
|
# ── РАЗДЕЛ V: Культуры ──
|
|
kb_cults = kb_entry.get("cultures", {})
|
|
cult_mapping = {
|
|
"Шумер и Месопотамия": ("culture_sumer_light", "culture_sumer_shadow"),
|
|
"Древний Египет": ("culture_egypt_light", "culture_egypt_shadow"),
|
|
"Иудейская традиция и Каббала": ("culture_kabbalah_light", "culture_kabbalah_shadow"),
|
|
"Античность (Греция и Рим)": ("culture_antiquity_light", "culture_antiquity_shadow"),
|
|
"Кельты и друиды": ("culture_celtic_light", "culture_celtic_shadow"),
|
|
"Скандинавия и германцы": ("culture_nordic_light", "culture_nordic_shadow"),
|
|
"Славянская традиция": ("culture_slavic_light", "culture_slavic_shadow"),
|
|
"Ведическая Индия": ("culture_india_light", "culture_india_shadow"),
|
|
"Персия и зороастризм": ("culture_persia_light", "culture_persia_shadow"),
|
|
"Древний Китай": ("culture_china_light", "culture_china_shadow"),
|
|
"Япония и синто": ("culture_japan_light", "culture_japan_shadow"),
|
|
"Ислам и суфизм": ("culture_islam_light", "culture_islam_shadow"),
|
|
"Алхимия и герметизм": ("culture_hermetic_light", "culture_hermetic_shadow"),
|
|
"Мезоамерика и Анды": ("culture_mesoamerica_light", "culture_mesoamerica_shadow"),
|
|
"Полинезия и Океания": ("culture_polynesia_light", "culture_polynesia_shadow"),
|
|
}
|
|
for cult_name, val in kb_cults.items():
|
|
pair = cult_mapping.get(cult_name)
|
|
if pair:
|
|
light_k, shadow_k = pair
|
|
if not f.get(light_k) and val:
|
|
f[light_k] = str(val)[:250]
|
|
|
|
# ── РАЗДЕЛ VI: Синтез ──
|
|
if not f.get("semantic_core") and kb_entry.get("core"):
|
|
f["semantic_core"] = kb_entry["core"]
|
|
if not f.get("will_vector") and kb_entry.get("asks"):
|
|
f["will_vector"] = kb_entry["asks"]
|
|
if not f.get("shadow_warning") and kb_entry.get("shadow"):
|
|
f["shadow_warning"] = kb_entry["shadow"]
|
|
|
|
# Подсчет заполненных
|
|
filled, total_78 = count_filled_fields(record)
|
|
record["filled_count"] = filled
|
|
record["is_complete"] = (filled == total_78)
|
|
record["updated_at"] = datetime.now().isoformat()
|
|
total_filled_across_db += filled
|
|
|
|
passports_db[str(wid)] = record
|
|
|
|
# Сохраняем в файл
|
|
os.makedirs(os.path.dirname(PASSPORTS_OUT_PATH), exist_ok=True)
|
|
with open(PASSPORTS_OUT_PATH, "w", encoding="utf-8") as f_out:
|
|
json.dump(passports_db, f_out, ensure_ascii=False, indent=2)
|
|
|
|
# Дублируем в oracle_enricher/data/
|
|
os.makedirs(os.path.dirname(PASSPORTS_ENRICHER_PATH), exist_ok=True)
|
|
with open(PASSPORTS_ENRICHER_PATH, "w", encoding="utf-8") as f_out:
|
|
json.dump(passports_db, f_out, ensure_ascii=False, indent=2)
|
|
|
|
avg_filled = total_filled_across_db / len(words)
|
|
print("\n" + "=" * 70)
|
|
print("ИТОГИ ИНИЦИАЛИЗАЦИИ БАЗЫ ПАСПОРТОВ 000–999:")
|
|
print(f"Всего паспортов в базе: {len(passports_db)}")
|
|
print(f"Всего заполненных параметров по всей базе: {total_filled_across_db} из {len(words)*78}")
|
|
print(f"Среднее заполнение на одну карточку: {avg_filled:.1f} из 78 полей")
|
|
print(f"Файл базы сохранен в: {PASSPORTS_OUT_PATH} и {PASSPORTS_ENRICHER_PATH}")
|
|
print("=" * 70)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|