feat: complete academic symbol lexicon heal, 2-column Cooper extraction, and updated assets

This commit is contained in:
2026-10-09 15:26:58 +05:00
parent 7f91366201
commit 0b12e4583b
244 changed files with 611461 additions and 1617 deletions
+318
View File
@@ -0,0 +1,318 @@
#!/usr/bin/env python3
import json
import re
import os
DART_PATH = "lib/data/words_data.dart"
KB_PATH = "assets/data/oracle_kb.json"
KB_OUT_PATH = "oracle_enricher/out/oracle_kb.json"
TRADITIONS_PATH = "assets/data/traditions_canon.json"
TRADITIONS_ENRICHER_PATH = "oracle_enricher/data/traditions_canon.json"
HTML_PATH = "lexicon_viewer.html"
def load_dart_words():
with open(DART_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
)
items = {}
for m in pattern.findall(content):
wid = int(m[0])
items[wid] = {
"id": wid,
"word": m[1].strip(),
"pos": m[2].strip(),
"frequency": float(m[3]),
"category": m[4].strip(),
"description": m[5].strip()
}
return items
def clean_desc(desc):
clean = re.sub(r'\[.*?\]\s*', '', desc).strip()
return clean
def parse_layers(desc):
matches = re.findall(r'\[([^\]:]+):\s*([^\]]+)\]', desc)
clean = clean_desc(desc)
layers = []
for sys_name, title in matches:
layers.append({
"system": sys_name.strip(),
"title": title.strip(),
"law": clean
})
return layers
def generate_core(clean, word, pos, cat):
# Take first significant segment
parts = re.split(r'[,;.]\s*', clean)
if len(parts) >= 2 and len(parts[0]) < 25:
core_candidate = f"{parts[0].strip().capitalize()}, {parts[1].strip()}."
elif len(parts) >= 1:
core_candidate = f"{parts[0].strip().capitalize()}."
else:
core_candidate = f"{clean.capitalize()}."
return core_candidate
def generate_oracle(clean, word, pos, cat):
c_lower = clean[0].lower() + clean[1:] if len(clean) > 1 else clean.lower()
# Strip trailing punctuation
c_clean = c_lower.rstrip('.,;')
if pos == 'глагол':
return (
f"Импульс знака «{word}» воплощает {c_clean}. "
f"В раскладе это прямой вектор трансформации: ситуация требует не выжидания, а точного и осознанного действия. "
f"Знак проверяет, готов ли вопрошающий взять ответственность за последствия своего шага."
)
elif pos == 'прилагательное':
return (
f"Качество знака «{word}» проявляется как {c_clean}. "
f"В пространстве оракула оно задает тон и вибрацию всего поля: это не застывшая характеристика, а живой закон восприятия. "
f"Символ спрашивает, видишь ли ты истинную природу этого свойства или подменяешь её внешней формой."
)
elif cat == 'Архетип Человека':
return (
f"Архетип «{word}» несет в себе {c_clean}. "
f"В раскладе он указывает на ключевую роль сознания или явление проводника силы. "
f"Это зеркало духа, в котором отражается зрелость вопрошающего и его готовность следовать своему призванию."
)
elif cat in ['Священный Предмет', 'Сакральный Символ']:
return (
f"Сакральный знак «{word}» удерживает {c_clean}. "
f"В поле гадания он выступает как фокус и орудие преображения: материальная опора для невидимой силы духа. "
f"Знак напоминает, что форма священна лишь до тех пор, пока сквозь нее сияет изначальный смысл."
)
elif cat == 'Космический Знак':
return (
f"Космический ритм знака «{word}» открывает {c_clean}. "
f"В раскладе это высший закон времени и пространства, неподвластный сиюминутным желаниям. "
f"Символ призывает согласовать личные намерения с великим круговоротом мироздания."
)
else:
return (
f"Архетип «{word}» утверждает: {c_clean}. "
f"В пространстве оракула этот образ обнажает глубинную суть происходящего, снимая наслоения иллюзий. "
f"Он служит ориентиром для поиска равновесия между внутренним миром и внешними вызовами."
)
def generate_shadow(word, pos, cat, clean):
if pos == 'глагол':
return "Суета, поспешность, насилие над естественным ритмом или, напротив, паралич воли и бегство от действия."
elif pos == 'прилагательное':
return "Крайность проявления, ригидность, слепота к противоположному качеству, искажение меры."
elif cat == 'Архетип Человека':
return "Гордыня роли, высокомерие, догматизм, манипуляция или страх перед собственной уязвимостью."
elif cat in ['Священный Предмет', 'Сакральный Символ']:
return "Фетишизация внешней оболочки, суеверие, утрата живой духовной связи за слепым следованием обряду."
elif cat == 'Космический Знак':
return "Фатализм, ощущение беспомощности перед роком, слепое подчинение внешним циклам."
else:
return "Искажение первообраза, пленение иллюзией, страх встречи с подлинной реальностью или бегство в фантазии."
def generate_asks(word, pos, cat):
if pos == 'глагол':
return f"Какое назревшее действие в теме «{word}» ты откладываешь, и что мешает сделать первый честный шаг?"
elif pos == 'прилагательное':
return f"Где в твоей жизни сейчас требуется проявить качество «{word}», а где оно стало маской?"
elif cat == 'Архетип Человека':
return f"Какую грань архетипа «{word}» ты отрицаешь в себе или слепо проецируешь на окружающих?"
else:
return f"Где в твоей ситуации сейчас говорит суть знака «{word}» — и готов ли ты принять этот урок без прикрас?"
def generate_step(word, pos, cat):
if pos == 'глагол':
return f"Соверши прямо сегодня одно конкретное действие, прямо воплощающее импульс «{word}»."
elif pos == 'прилагательное':
return f"Прояви качество «{word}» в ближайшем бытовом или внутреннем решении."
elif cat == 'Архетип Человека':
return f"Взгляни на текущую трудность глазами зрелого архетипа «{word}» и поступи соответственно его достоинству."
else:
return f"Выдели несколько минут тишины, чтобы созерцать символ «{word}» и согласовать с ним свои приоритеты."
def main():
print("1. Loading words from Dart...")
words = load_dart_words()
print(f"Loaded {len(words)} words.")
print("\n2. Updating assets/data/oracle_kb.json...")
with open(KB_PATH, "r", encoding="utf-8") as f:
kb = json.load(f)
stub_fixed_count = 0
for wid, it in words.items():
str_id = str(wid)
w_name = it["word"]
clean = clean_desc(it["description"])
extracted_layers = parse_layers(it["description"])
# Check existing record by ID or create
rec = kb.get(str_id, {})
if not rec:
rec = kb.get(w_name, {})
is_stub = False
core = rec.get("core", "")
oracle = rec.get("oracle", "")
if (
not core
or "функция знака" in core.lower()
or "образ без прямой карты" in core.lower()
or not oracle
or "знак держит функция знака" in oracle.lower()
):
is_stub = True
if is_stub:
rec["id"] = wid
rec["word"] = w_name
rec["pos"] = it["pos"]
rec["category"] = it["category"]
rec["lookup"] = "A"
rec["has_canon"] = True
rec["core"] = generate_core(clean, w_name, it["pos"], it["category"])
rec["oracle"] = generate_oracle(clean, w_name, it["pos"], it["category"])
rec["shadow"] = generate_shadow(w_name, it["pos"], it["category"], clean)
rec["asks"] = generate_asks(w_name, it["pos"], it["category"])
rec["step"] = generate_step(w_name, it["pos"], it["category"])
if extracted_layers:
rec["layers"] = extracted_layers
stub_fixed_count += 1
else:
# Even if not stub, ensure layers from dart are merged if missing
if extracted_layers and not rec.get("layers"):
rec["layers"] = extracted_layers
# Ensure both keys exist in kb and match
kb[str_id] = rec
kb[w_name] = rec
with open(KB_PATH, "w", encoding="utf-8") as f:
json.dump(kb, f, ensure_ascii=False, indent=2)
print(f"Fixed {stub_fixed_count} stub entries in {KB_PATH}.")
if os.path.exists(os.path.dirname(KB_OUT_PATH)):
with open(KB_OUT_PATH, "w", encoding="utf-8") as f:
json.dump(kb, f, ensure_ascii=False, indent=2)
print(f"Synced {KB_OUT_PATH}.")
print("\n3. Updating traditions_canon.json...")
with open(TRADITIONS_PATH, "r", encoding="utf-8") as f:
tc = json.load(f)
for trad_key, trad in tc.items():
stations = trad.get("stations", [])
for st in stations:
# Primary word
prim = st.get("primary")
if prim and isinstance(prim, dict):
p_id = prim.get("id")
if p_id in words:
# If primary has empty or short desc, fill it with rich description
if not prim.get("desc"):
prim["desc"] = words[p_id]["description"]
# Cluster words
cluster = st.get("cluster", [])
for cl in cluster:
if isinstance(cl, dict):
c_id = cl.get("id")
if c_id in words and not cl.get("desc"):
cl["desc"] = words[c_id]["description"]
with open(TRADITIONS_PATH, "w", encoding="utf-8") as f:
json.dump(tc, f, ensure_ascii=False, indent=2)
print(f"Updated {TRADITIONS_PATH}.")
with open(TRADITIONS_ENRICHER_PATH, "w", encoding="utf-8") as f:
json.dump(tc, f, ensure_ascii=False, indent=2)
print(f"Synced {TRADITIONS_ENRICHER_PATH}.")
print("\n4. Updating lexicon_viewer.html...")
with open(HTML_PATH, "r", encoding="utf-8") as f:
html = f.read()
# Build clean WORDS_DATA list
words_data_list = []
# Sort by ID 0..999
for wid in sorted(words.keys()):
it = words[wid]
words_data_list.append({
"id": it["id"],
"word": it["word"],
"emoji": kb.get(str(wid), {}).get("emoji", "✨"),
"pos": it["pos"],
"category": it["category"],
"description": it["description"]
})
# 4a. Replace WORDS_DATA in HTML
json_words = json.dumps(words_data_list, ensure_ascii=False)
html = re.sub(
r'const WORDS_DATA = \[.*?\];',
f'const WORDS_DATA = {json_words};',
html,
flags=re.DOTALL
)
# 4b. Replace TRADITIONS_CANON in HTML
json_tc = json.dumps(tc, ensure_ascii=False, indent=2)
html = re.sub(
r'const TRADITIONS_CANON = \{.*?\n\s*\};\n',
f'const TRADITIONS_CANON = {json_tc};\n',
html,
flags=re.DOTALL
)
# 4c. Ensure CANONICAL_SYSTEMS recognizes Септенер and Додекада
old_systems_pattern = r'const CANONICAL_SYSTEMS = \[.*?\];'
new_systems = """const CANONICAL_SYSTEMS = [
{ id: 'Руны', name: 'Старший Футарк (Руны)', icon: 'ᚱ', css: 'system-runes' },
{ id: 'Огам', name: 'Кельтский Огам', icon: '᚛', css: 'system-ogham' },
{ id: 'Каббала', name: 'Каббала / Сефирот', icon: '✡️', css: 'system-kabbalah' },
{ id: 'Алхимия', name: 'Алхимия (Magnum Opus)', icon: '⚗️', css: 'system-alchemy' },
{ id: 'Таро', name: 'Старшие Арканы Таро', icon: '🎴', css: 'system-tarot' },
{ id: 'Ленорман', name: 'Оракул Ленорман', icon: '🪶', css: 'system-lenormand' },
{ id: 'И Цзин', name: 'И Цзин (Книга Перемен)', icon: '☯️', css: 'system-iching' },
{ id: 'Геомантия', name: 'Арабская Геомантия', icon: '⌛', css: 'system-geomancy' },
{ id: 'Додекада Созвездий', name: 'Классический гороскоп', icon: '♈', css: 'system-western' },
{ id: 'Китайский гороскоп', name: 'Китайский Зодиак', icon: '🐉', css: 'system-zodiac' },
{ id: 'Космология', name: 'Космология Трёх Миров', icon: '🌌', css: 'system-cosmo' },
{ id: 'Септенер', alias: 'Дни недели', name: 'Септенер (7 Дней Недели)', icon: '📅', css: 'system-days' },
{ id: 'Додекада', alias: 'Месяцы года', name: 'Додекада (12 Месяцев Года)', icon: '🗓️', css: 'system-months' },
{ id: 'Дни недели', name: 'Септенер (7 Дней Недели)', icon: '📅', css: 'system-days' },
{ id: 'Месяцы года', name: 'Додекада (12 Месяцев Года)', icon: '🗓️', css: 'system-months' },
];"""
html = re.sub(old_systems_pattern, new_systems, html, flags=re.DOTALL)
# 4d. Ensure SYSTEM_TO_CANON_KEY has Септенер and Додекада
old_key_pattern = r'const SYSTEM_TO_CANON_KEY = \{.*?\};'
new_key = """const SYSTEM_TO_CANON_KEY = {
'Септенер': 'days_of_week',
'Дни недели': 'days_of_week',
'Додекада': 'months_of_year',
'Месяцы года': 'months_of_year',
'Руны': 'runes',
'Таро': 'tarot',
'Огам': 'ogham',
'Каббала': 'kabbalah',
'Ленорман': 'lenormand',
'Космология': 'cosmology',
'Геомантия': 'geomancy',
'И Цзин': 'iching',
'Додекада Созвездий': 'western_zodiac',
'Китайский гороскоп': 'zodiac',
'Алхимия': 'alchemy'
};"""
html = re.sub(old_key_pattern, new_key, html, flags=re.DOTALL)
with open(HTML_PATH, "w", encoding="utf-8") as f:
f.write(html)
print(f"Updated {HTML_PATH} successfully!")
if __name__ == "__main__":
main()