#!/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()