Files
999/scripts/bind_numerology.py

298 lines
12 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Bind Pythagorean numerology onto number-words already inside the thousand.
Does not add cells. Master numbers 11, 22 and 33 stay unreduced.
Rebuilds the stale lexicon.json so its ids match words_data.dart.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from repair_canon_integrity import ( # noqa: E402
CANON_A,
CANON_B,
HTML,
KB_ASSETS,
KB_OUT,
LEX,
embed_html,
ensure_tag,
load_dart,
write_alpha,
write_dart,
)
# word, station id, name (also the tag detail), group, symbol, cluster words, lore
STATIONS = [
(
"один",
"n1",
"1 Характер (Монада)",
"Квадрат Пифагора · воля и эго",
"1",
["воля", "характер"],
"Первопроходец ряда. Ячейка квадрата держит характер и волю: одну точку, из которой начинается счёт.",
),
(
"два",
"n2",
"2 Энергия (Дуада)",
"Квадрат Пифагора · пара и мера сил",
"2",
[],
"Дипломат пары. Ячейка квадрата — запас действия: хватает ли сил на шаг, или шаг берёт силы у другого.",
),
(
"три",
"n3",
"3 Интерес (Триада)",
"Квадрат Пифагора · познание",
"3",
["интерес"],
"Творец. Ячейка квадрата — интерес: внимание, которое выбирает один предмет и не отпускает его.",
),
(
"четыре",
"n4",
"4 Тело (Тетрада)",
"Квадрат Пифагора · опора",
"4",
["тело"],
"Строитель основания. Ячейка квадрата — тело и запас сил: то, на чём держится долгий труд.",
),
(
"пять",
"n5",
"5 Логика (Пентада)",
"Квадрат Пифагора · расчёт",
"5",
[],
"Странник. Ячейка квадрата — логика и чутьё: шаг просчитан, и всё же горизонт ещё не закрыт.",
),
(
"шесть",
"n6",
"6 Труд (Гексада)",
"Квадрат Пифагора · ремесло",
"6",
["труд"],
"Хранитель очага. Ячейка квадрата — труд руками: ремесло, которое заземляет слово.",
),
(
"семь",
"n7",
"7 Удача (Септенер)",
"Квадрат Пифагора · покров",
"7",
[],
"Искатель истины. Ячейка квадрата — везение: покров, которого нет в труде и который трудом не покупается.",
),
(
"восемь",
"n8",
"8 Долг (Октава)",
"Квадрат Пифагора · совесть",
"8",
[],
"Владыка материи. Ячейка квадрата — долг: слово, которое человек держит, когда выгоды уже нет.",
),
(
"девять",
"n9",
"9 Память (Эннеада)",
"Квадрат Пифагора · ум",
"9",
["память", "ум"],
"Мудрец конца ряда. Ячейка квадрата — память и ум: что удержано, прежде чем счёт перейдёт в десяток.",
),
(
"одиннадцать",
"n11",
"11 Духовный проводник",
"Мастер-числа · озарение",
"11",
["истина"],
"Мастер-число. Канал озарения, который не свёртывается в двойку. Проводник несёт свет, а не отчёт.",
),
(
"двадцать два",
"n22",
"22 Великий архитектор",
"Мастер-числа · система",
"22",
[],
"Мастер-число. Строитель, который переводит замысел в систему: двадцать два аркана и двадцать два пути Древа.",
),
(
"тридцать три",
"n33",
"33 Мастер-число (33 ряда Домов)",
"Мастер-числа · служение",
"33",
["любовь"],
"Мастер-число служения и сострадания. Тем же числом названы 33 ряда домов поля: престол считает ряды, а не клетки лексикона.",
),
]
def _load_html_words():
html = HTML.read_text(encoding="utf-8")
marker = "const WORDS_DATA = "
start = html.find(marker)
end = html.find(";\n", start)
if start < 0 or end < 0:
raise SystemExit("WORDS_DATA markers missing")
return json.loads(html[start + len(marker) : end])
def _chip(word, by_id_html, items_by_word):
src = by_id_html[items_by_word[word]["id"]]
it = items_by_word[word]
return {
"id": it["id"],
"word": word,
"emoji": src.get("emoji", ""),
"pos": it["pos"],
"category": it["cat"],
"desc": it["desc"],
}
def _patch_html_maps():
html = HTML.read_text(encoding="utf-8")
css = " .badge-system.system-numerology { border-color: rgba(255, 213, 79, 0.45); background: rgba(255, 213, 79, 0.12); color: #fff3c4; }\n"
anchor = " .badge-system.system-cosmo { border-color: rgba(159, 122, 234, 0.4); background: rgba(159, 122, 234, 0.12); color: #e9d8fd; }\n"
if "system-numerology" not in html:
if anchor not in html:
raise SystemExit("css anchor missing")
html = html.replace(anchor, anchor + css, 1)
sys_row = " { id: 'Нумерология', name: 'Нумерология (Квадрат Пифагора)', icon: '🔢', css: 'system-numerology' },\n { id: 'Матрица', name: '33 ряда Домов', icon: '🏛️', css: 'system-numerology' },\n"
sys_anchor = " { id: 'Месяцы года', name: 'Додекада (12 Месяцев Года)', icon: '🗓️', css: 'system-months' },\n ];"
if "{ id: 'Нумерология'" not in html:
if sys_anchor not in html:
raise SystemExit("CANONICAL_SYSTEMS anchor missing")
html = html.replace(
sys_anchor,
" { id: 'Месяцы года', name: 'Додекада (12 Месяцев Года)', icon: '🗓️', css: 'system-months' },\n" + sys_row + " ];",
1,
)
key_anchor = " 'Алхимия': 'alchemy'\n };"
key_add = " 'Алхимия': 'alchemy',\n 'Нумерология': 'numerology',\n 'Матрица': 'numerology'\n };"
if "'Нумерология': 'numerology'" not in html:
if key_anchor not in html:
raise SystemExit("SYSTEM_TO_CANON_KEY anchor missing")
html = html.replace(key_anchor, key_add, 1)
HTML.write_text(html, encoding="utf-8")
def main():
text, items = load_dart()
by_word = {it["word"]: it for it in items}
for row in STATIONS:
word = row[0]
if word not in by_word:
raise SystemExit(f"primary missing: {word}")
for extra in row[5]:
if extra not in by_word:
raise SystemExit(f"cluster missing: {extra}")
for word, _sid, name, _group, _symbol, cluster, _lore in STATIONS:
tag = f"[Нумерология: {name}]"
by_word[word]["desc"] = ensure_tag(by_word[word]["desc"], tag)
for extra in cluster:
by_word[extra]["desc"] = ensure_tag(by_word[extra]["desc"], tag)
by_word["тридцать три"]["desc"] = ensure_tag(
by_word["тридцать три"]["desc"],
"[Матрица: 33 ряда Домов]",
)
html_words = _load_html_words()
by_html = {w["id"]: w for w in html_words}
if len(by_html) != 1000:
raise SystemExit("html words != 1000")
stations = []
for word, sid, name, group, symbol, cluster, lore in STATIONS:
stations.append(
{
"id": sid,
"name": name,
"symbol": symbol,
"group": group,
"lore": lore,
"primary": _chip(word, by_html, by_word),
"cluster": [_chip(w, by_html, by_word) for w in cluster],
}
)
canon = json.loads(CANON_A.read_text(encoding="utf-8"))
canon["numerology"] = {
"id": "numerology",
"name": "Нумерология и Квадрат Пифагора",
"icon": "🔢",
"count": len(stations),
"description": "Девять ячеек квадрата и три мастер-числа. Код суток свёртывает день, месяц и год по отдельности и оставляет 11, 22 и 33. Управители — числовые слова тысячи.",
"stations": stations,
}
# Refresh chip descriptions from the stamped dictionary.
for trad in canon.values():
for st in trad.get("stations", []):
for node in [st.get("primary")] + list(st.get("cluster") or []):
if isinstance(node, dict) and node.get("word") in by_word:
node["desc"] = by_word[node["word"]]["desc"]
raw_a = json.dumps(canon, ensure_ascii=False, indent=2) + "\n"
CANON_A.write_text(raw_a, encoding="utf-8")
CANON_B.write_text(raw_a, encoding="utf-8")
for kb_path in (KB_ASSETS, KB_OUT):
kb = json.loads(kb_path.read_text(encoding="utf-8"))
for it in items:
for key in (it["word"], str(it["id"])):
entry = kb.get(key)
if isinstance(entry, dict):
entry["description"] = it["desc"]
entry["word"] = it["word"]
entry["id"] = it["id"]
kb_path.write_text(json.dumps(kb, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
write_dart(text, items)
for w in html_words:
it = by_word[w["word"]]
w["description"] = it["desc"]
w["pos"] = it["pos"]
w["category"] = it["cat"]
embed_html(canon, html_words)
_patch_html_maps()
lexicon = [
{
"id": w["id"],
"word": w["word"],
"emoji": w.get("emoji", ""),
"pos": w.get("pos", ""),
"category": w.get("category", ""),
}
for w in sorted(html_words, key=lambda x: x["id"])
]
if len({row["word"] for row in lexicon}) != 1000:
raise SystemExit("lexicon words not unique")
LEX.write_text(json.dumps(lexicon, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
write_alpha(lexicon)
print("numerology stations", len(stations))
print("tagged", by_word["одиннадцать"]["desc"][:80])
print("matrix", "[Матрица:" in by_word["тридцать три"]["desc"])
if __name__ == "__main__":
main()