580 lines
23 KiB
Python
580 lines
23 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""Repair oracle canon inside the closed thousand.
|
||
|
||
Swaps sacred names onto junk slots, retargets planet chips onto metals,
|
||
resyncs station descriptions, rewrites stock dossiers, and re-embeds the
|
||
HTML atlas so geomancy symbols stay valid JavaScript.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import re
|
||
import shutil
|
||
from copy import deepcopy
|
||
from pathlib import Path
|
||
|
||
from dossier_laws import LAWS, NIGHTS
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
DART = ROOT / "lib" / "data" / "words_data.dart"
|
||
LEX = ROOT / "oracle_enricher" / "data" / "lexicon.json"
|
||
KB_ASSETS = ROOT / "assets" / "data" / "oracle_kb.json"
|
||
KB_OUT = ROOT / "oracle_enricher" / "out" / "oracle_kb.json"
|
||
CANON_A = ROOT / "assets" / "data" / "traditions_canon.json"
|
||
CANON_B = ROOT / "oracle_enricher" / "data" / "traditions_canon.json"
|
||
HTML = ROOT / "lexicon_viewer.html"
|
||
ALPHA_MD = ROOT / "words_alphabetical.md"
|
||
ALPHA_TXT = ROOT / "words_alphabetical.txt"
|
||
|
||
STOCK_CORE = (
|
||
"сакральный символ",
|
||
"архетипический образ в поле судьбы",
|
||
"ключевую сферу кристаллизации",
|
||
"вектор трансформации",
|
||
"модус судьбы",
|
||
"активный процесс духовной динамики",
|
||
)
|
||
STOCK_SHADOW = ("искажение первообраза", "суета, поспешность")
|
||
STOCK_ASK = ("готов ли ты принять этот урок", "созерцать символ")
|
||
|
||
# Sacred word replaces this living junk word. One death per restore.
|
||
SWAPS = [
|
||
("умеренность", "начаться", "⚖️", "Образ / Архетип"),
|
||
("росток", "рассказать", "🌱", "Образ / Архетип"),
|
||
("ученик", "спросить", "📖", "Образ / Архетип"),
|
||
("терпение", "принести", "🪨", "Образ / Архетип"),
|
||
("войско", "помочь", "⚔️", "Образ / Архетип"),
|
||
("уныние", "пройти", "🌧️", "Образ / Архетип"),
|
||
("созерцание", "назвать", "👁️", "Образ / Архетип"),
|
||
("опасность", "связать", "⚠️", "Образ / Архетип"),
|
||
("свежесть", "написать", "💧", "Образ / Архетип"),
|
||
("осина", "более", "🌳", "Образ / Архетип"),
|
||
("дрок", "пол", "🌼", "Образ / Архетип"),
|
||
("лоза", "мужик", "🍇", "Образ / Архетип"),
|
||
("бузина", "ребята", "🌿", "Образ / Архетип"),
|
||
("императрица", "вновь", "👑", "Образ / Архетип"),
|
||
("император", "наверное", "👑", "Образ / Архетип"),
|
||
("влюбленные", "опять", "💕", "Образ / Архетип"),
|
||
("колесница", "постоянно", "🛞", "Священный Предмет"),
|
||
("дьявол", "тоже", "😈", "Образ / Архетип"),
|
||
("космос", "чуть", "🌌", "Космический Знак"),
|
||
("рябина", "зачем", "🌳", "Образ / Архетип"),
|
||
("ольха", "тут", "🌳", "Образ / Архетип"),
|
||
("ясень", "бывать", "🌳", "Образ / Архетип"),
|
||
("боярышник", "возникать", "🌿", "Образ / Архетип"),
|
||
("остролист", "значить", "🌿", "Образ / Архетип"),
|
||
("орешник", "казаться", "🌰", "Образ / Архетип"),
|
||
("яблоня", "находиться", "🍎", "Образ / Архетип"),
|
||
("тростник", "прийтись", "🌾", "Образ / Архетип"),
|
||
("сосна", "хотеться", "🌲", "Образ / Архетип"),
|
||
("вереск", "показаться", "🌸", "Образ / Архетип"),
|
||
("тис", "выглядеть", "🌲", "Образ / Архетип"),
|
||
]
|
||
|
||
ALIAS = {
|
||
"марс": "железо",
|
||
"меркурий": "ртуть",
|
||
"юпитер": "олово",
|
||
"венера": "медь",
|
||
"сатурн": "свинец",
|
||
"оранжевый": "золотой",
|
||
}
|
||
|
||
TAG_OF = {
|
||
"runes": "Руны",
|
||
"ogham": "Огам",
|
||
"kabbalah": "Каббала",
|
||
"alchemy": "Алхимия",
|
||
"tarot": "Таро",
|
||
"lenormand": "Ленорман",
|
||
"iching": "И Цзин",
|
||
"geomancy": "Геомантия",
|
||
"western_zodiac": "Додекада Созвездий",
|
||
"zodiac": "Китайский гороскоп",
|
||
"cosmology": "Космология",
|
||
"days_of_week": "Септенер",
|
||
"months_of_year": "Додекада",
|
||
}
|
||
|
||
COSMO_LABEL = {
|
||
"upper_world": "Верхний Мир",
|
||
"middle_world": "Срединный Мир",
|
||
"lower_world": "Нижний Мир",
|
||
"axis_mundi": "Мировая Ось",
|
||
}
|
||
|
||
DAY_SCRIPT = {
|
||
"monday": ("יום שני", "月曜", "الإثنين", "йом шени · юэяо · аль-иснайн"),
|
||
"tuesday": ("יום שלישי", "火曜", "الثلاثاء", "йом шлиши · хояо · ас-суляса"),
|
||
"wednesday": ("יום רביעי", "水曜", "الأربعاء", "йом ревии · шуйяо · аль-арбиа"),
|
||
"thursday": ("יום חמישי", "木曜", "الخميس", "йом хамиши · муяо · аль-хамис"),
|
||
"friday": ("יום שישי", "金曜", "الجمعة", "йом шиши · цзиньяо · аль-джумуа"),
|
||
"saturday": ("שבת", "土曜", "السبت", "шаббат · туяо · ас-сабт"),
|
||
"sunday": ("יום ראשון", "日曜", "الأحد", "йом ришон · жияо · аль-ахад"),
|
||
}
|
||
|
||
MONTH_SCRIPT = {
|
||
"january": ("ינואר", "一月", "كانون الثاني", "януар · и-юэ · канун ас-сани"),
|
||
"february": ("פברואר", "二月", "شباط", "февруар · эр-юэ · шубат"),
|
||
"march": ("מרץ", "三月", "آذار", "мерц · сань-юэ · азар"),
|
||
"april": ("אפריל", "四月", "نيسان", "апрель · сы-юэ · нисан"),
|
||
"may": ("מאי", "五月", "أيار", "май · у-юэ · айяр"),
|
||
"june": ("יוני", "六月", "حزيران", "юни · лю-юэ · хузейран"),
|
||
"july": ("יולי", "七月", "تموز", "юли · ци-юэ · таммуз"),
|
||
"august": ("אוגוסט", "八月", "آب", "огуст · ба-юэ · аб"),
|
||
"september": ("ספטמבר", "九月", "أيلول", "септембер · цзю-юэ · айлюль"),
|
||
"october": ("אוקטובר", "十月", "تشرين الأول", "октобер · ши-юэ · тишрин аль-авваль"),
|
||
"november": ("נובמבר", "十一月", "تشرين الثاني", "новембер · шии-юэ · тишрин ас-сани"),
|
||
"december": ("דצמבר", "十二月", "كانون الأول", "децембер · шиэр-юэ · канун аль-авваль"),
|
||
}
|
||
|
||
|
||
def dart_unescape(s: str) -> str:
|
||
out = []
|
||
i = 0
|
||
while i < len(s):
|
||
if s[i] == "\\" and i + 1 < len(s):
|
||
n = s[i + 1]
|
||
out.append({"n": "\n", "t": "\t", '"': '"', "\\": "\\", "$": "$"}.get(n, n))
|
||
i += 2
|
||
else:
|
||
out.append(s[i])
|
||
i += 1
|
||
return "".join(out)
|
||
|
||
|
||
def dart_escape(s: str) -> str:
|
||
return (
|
||
s.replace("\\", "\\\\")
|
||
.replace('"', '\\"')
|
||
.replace("$", "\\$")
|
||
.replace("\n", " ")
|
||
.replace("\r", " ")
|
||
)
|
||
|
||
|
||
def grab(block: str, key: str) -> str:
|
||
m = re.search(rf"{key}:\s*\"((?:\\.|[^\"\\])*)\"", block)
|
||
if not m:
|
||
raise SystemExit(f"no {key} in block")
|
||
return dart_unescape(m.group(1))
|
||
|
||
|
||
def low_has(text: str, needles) -> bool:
|
||
t = (text or "").lower()
|
||
return any(n in t for n in needles)
|
||
|
||
|
||
def split_tags(desc: str):
|
||
tags = re.findall(r"\[[^\]]+\]", desc)
|
||
rest = re.sub(r"\[[^\]]+\]", " ", desc)
|
||
rest = re.sub(r"\s+", " ", rest).strip()
|
||
return tags, rest
|
||
|
||
|
||
def join_desc(tags, rest: str) -> str:
|
||
body = rest.strip()
|
||
if tags and body:
|
||
return " ".join(tags) + " " + body
|
||
if tags:
|
||
return " ".join(tags)
|
||
return body
|
||
|
||
|
||
def ensure_tag(desc: str, tag: str) -> str:
|
||
if tag in desc:
|
||
return desc
|
||
return (tag + " " + desc).strip()
|
||
|
||
|
||
def clean_tags(desc: str) -> str:
|
||
desc = desc.replace("[Планетарный Септенер:", "[Септенер:")
|
||
desc = desc.replace("[Зодиак:", "[Додекада Созвездий:")
|
||
# Нумерология и Матрица — живые теги традиции numerology. Не разворачивать в прозу.
|
||
return re.sub(r"\s+", " ", desc).strip()
|
||
|
||
|
||
def load_dart():
|
||
text = DART.read_text(encoding="utf-8")
|
||
blocks = re.findall(r" WordItem\(\n(?:.*\n)*? \),", text)
|
||
if len(blocks) != 1000:
|
||
raise SystemExit(f"expected 1000 WordItems, got {len(blocks)}")
|
||
items = []
|
||
for b in blocks:
|
||
freq = re.search(r"frequency:\s*([0-9.]+)", b).group(1)
|
||
items.append(
|
||
{
|
||
"id": int(re.search(r"id:\s*(\d+)", b).group(1)),
|
||
"word": grab(b, "word"),
|
||
"pos": grab(b, "partOfSpeech"),
|
||
"freq": freq,
|
||
"cat": grab(b, "category"),
|
||
"desc": grab(b, "description"),
|
||
}
|
||
)
|
||
items.sort(key=lambda x: x["id"])
|
||
return text, items
|
||
|
||
|
||
def write_dart(text: str, items):
|
||
chunks = []
|
||
for it in items:
|
||
chunks.append(
|
||
" WordItem(\n"
|
||
f" id: {it['id']},\n"
|
||
f" word: \"{dart_escape(it['word'])}\",\n"
|
||
f" partOfSpeech: \"{dart_escape(it['pos'])}\",\n"
|
||
f" frequency: {it['freq']},\n"
|
||
f" category: \"{dart_escape(it['cat'])}\",\n"
|
||
f" description: \"{dart_escape(it['desc'])}\",\n"
|
||
" ),"
|
||
)
|
||
new_body = "\n".join(chunks)
|
||
new_text, n = re.subn(
|
||
r" WordItem\(\n(?:.*\n)*? \),",
|
||
lambda m, repl=iter(chunks): next(repl),
|
||
text,
|
||
count=1000,
|
||
)
|
||
if n != 1000:
|
||
raise SystemExit(f"rewrote {n} blocks")
|
||
# The lambda above consumes chunks via closure; verify count of WordItem.
|
||
if new_text.count("WordItem(") != 1000:
|
||
raise SystemExit("dart word count drifted")
|
||
DART.write_text(new_text, encoding="utf-8")
|
||
|
||
|
||
def apply_swaps(items, lexicon, kb):
|
||
by_word = {it["word"]: it for it in items}
|
||
deaths = []
|
||
for new, death, emoji, cat in SWAPS:
|
||
if new in by_word and death not in by_word:
|
||
continue
|
||
if new in by_word and death in by_word:
|
||
raise SystemExit(f"both live: {new} and {death}")
|
||
if death not in by_word:
|
||
raise SystemExit(f"no slot for {new}, missing {death}")
|
||
it = by_word.pop(death)
|
||
it["word"] = new
|
||
it["pos"] = "существительное"
|
||
it["cat"] = cat
|
||
by_word[new] = it
|
||
row = next(r for r in lexicon if r["id"] == it["id"])
|
||
row["word"] = new
|
||
row["emoji"] = emoji
|
||
row["pos"] = "существительное"
|
||
row["category"] = cat
|
||
old = kb.pop(death, None) or {}
|
||
kb.pop(str(it["id"]), None)
|
||
core, shadow, ask, step = LAWS[new]
|
||
entry = deepcopy(old) if isinstance(old, dict) else {}
|
||
entry.update(
|
||
{
|
||
"id": it["id"],
|
||
"word": new,
|
||
"emoji": emoji,
|
||
"pos": "существительное",
|
||
"category": cat,
|
||
"operator": "substance",
|
||
"valence": entry.get("valence") or "liminal",
|
||
"has_canon": True,
|
||
"user_edited": False,
|
||
"core": core,
|
||
"oracle": f"{core} {shadow}",
|
||
"shadow": shadow,
|
||
"asks": ask,
|
||
"step": step,
|
||
"grammar": {
|
||
"modifies": "подставляет соседнему знаку тело образа",
|
||
"operator": "substance",
|
||
"valence": "liminal",
|
||
},
|
||
}
|
||
)
|
||
kb[new] = entry
|
||
deaths.append(f"{death} → {new} (#{it['id']:03d})")
|
||
return deaths
|
||
|
||
|
||
def apply_laws(kb, items):
|
||
by_word = {it["word"]: it for it in items}
|
||
for word, (core, shadow, ask, step) in LAWS.items():
|
||
if word not in by_word:
|
||
continue
|
||
entry = kb.get(word)
|
||
if not isinstance(entry, dict):
|
||
entry = {"id": by_word[word]["id"], "word": word}
|
||
kb[word] = entry
|
||
entry["word"] = word
|
||
entry["id"] = by_word[word]["id"]
|
||
entry["core"] = core
|
||
entry["shadow"] = shadow
|
||
entry["asks"] = ask
|
||
entry["step"] = step
|
||
entry["oracle"] = f"{core} {shadow}"
|
||
entry["has_canon"] = True
|
||
for word, (shadow, ask, step) in NIGHTS.items():
|
||
if word not in by_word or word in LAWS:
|
||
continue
|
||
entry = kb.get(word)
|
||
if not isinstance(entry, dict):
|
||
continue
|
||
if low_has(entry.get("shadow", ""), STOCK_SHADOW):
|
||
entry["shadow"] = shadow
|
||
if low_has(entry.get("asks", ""), STOCK_ASK) or low_has(entry.get("step", ""), STOCK_ASK):
|
||
entry["asks"] = ask
|
||
entry["step"] = step
|
||
|
||
|
||
def rewrite_descriptions(items, kb):
|
||
cores = {}
|
||
for word, entry in kb.items():
|
||
if isinstance(entry, dict) and not str(word).isdigit():
|
||
cores[entry.get("word") or word] = entry.get("core") or ""
|
||
for it in items:
|
||
desc = clean_tags(it["desc"])
|
||
tags, rest = split_tags(desc)
|
||
if low_has(rest, STOCK_CORE) or low_has(rest, ("кристаллизации вашего опыта",)):
|
||
if it["word"] in LAWS:
|
||
rest = LAWS[it["word"]][0]
|
||
else:
|
||
core = cores.get(it["word"], "")
|
||
if core and not low_has(core, STOCK_CORE):
|
||
rest = core
|
||
it["desc"] = join_desc(tags, rest)
|
||
|
||
|
||
def stamp_from_canon(items, canon):
|
||
by_word = {it["word"]: it for it in items}
|
||
for tid, trad in canon.items():
|
||
label = TAG_OF.get(tid)
|
||
if not label:
|
||
continue
|
||
for st in trad.get("stations") or []:
|
||
nodes = []
|
||
if st.get("primary"):
|
||
nodes.append(st["primary"])
|
||
nodes.extend(st.get("cluster") or [])
|
||
for node in nodes:
|
||
word = ALIAS.get(node.get("word"), node.get("word"))
|
||
if word == "начать" or word not in by_word:
|
||
continue
|
||
# Only force a fresh station tag onto restored words and cosmology.
|
||
if tid == "cosmology":
|
||
short = COSMO_LABEL.get(st.get("id"), st.get("name", "Космология"))
|
||
tag = f"[Космология: {short}]"
|
||
by_word[word]["desc"] = ensure_tag(by_word[word]["desc"], tag)
|
||
elif word in LAWS and word not in {it["word"] for it in items if False}:
|
||
pass
|
||
restored = {new for new, *_ in SWAPS}
|
||
for tid, trad in canon.items():
|
||
label = TAG_OF.get(tid)
|
||
if not label:
|
||
continue
|
||
for st in trad.get("stations") or []:
|
||
words = []
|
||
if st.get("primary"):
|
||
words.append(st["primary"].get("word"))
|
||
words.extend(c.get("word") for c in st.get("cluster") or [])
|
||
for raw in words:
|
||
word = ALIAS.get(raw, raw)
|
||
if word not in restored or word not in by_word:
|
||
continue
|
||
tag = f"[{label}: {st.get('name', st.get('id', ''))}]"
|
||
by_word[word]["desc"] = ensure_tag(by_word[word]["desc"], tag)
|
||
|
||
|
||
def attach_scripts(station, pack):
|
||
hebrew, hanzi, arabic, say = pack
|
||
station["hebrew"] = hebrew
|
||
station["hanzi"] = hanzi
|
||
station["arabic"] = arabic
|
||
group = station.get("group") or ""
|
||
if say not in group:
|
||
station["group"] = f"{group} · {say}" if group else say
|
||
|
||
|
||
def repair_canon(canon, items, lex_by_id):
|
||
kab = canon["kabbalah"]["stations"]
|
||
for st in kab:
|
||
if st["id"] == "kether" and " · " not in st.get("group", ""):
|
||
st["group"] = "Высшая триада · Ацилут"
|
||
for st in canon["days_of_week"]["stations"]:
|
||
if st["id"] in DAY_SCRIPT:
|
||
attach_scripts(st, DAY_SCRIPT[st["id"]])
|
||
for st in canon["months_of_year"]["stations"]:
|
||
if st["id"] in MONTH_SCRIPT:
|
||
attach_scripts(st, MONTH_SCRIPT[st["id"]])
|
||
|
||
by_word = {it["word"]: it for it in items}
|
||
missing = []
|
||
for trad in canon.values():
|
||
for st in trad.get("stations") or []:
|
||
if st.get("primary"):
|
||
_retarget(st["primary"], by_word, lex_by_id, missing, st["id"])
|
||
cluster = []
|
||
for node in st.get("cluster") or []:
|
||
if node.get("word") == "начать":
|
||
continue
|
||
_retarget(node, by_word, lex_by_id, missing, st["id"])
|
||
cluster.append(node)
|
||
st["cluster"] = cluster
|
||
if missing:
|
||
raise SystemExit("canon words still missing: " + ", ".join(missing[:30]))
|
||
|
||
|
||
def _retarget(node, by_word, lex_by_id, missing, station_id):
|
||
word = node.get("word")
|
||
word = ALIAS.get(word, word)
|
||
if word not in by_word:
|
||
missing.append(f"{station_id}:{word}")
|
||
return
|
||
it = by_word[word]
|
||
lex = lex_by_id[it["id"]]
|
||
node["word"] = word
|
||
node["id"] = it["id"]
|
||
node["emoji"] = lex.get("emoji") or node.get("emoji") or ""
|
||
node["pos"] = it["pos"]
|
||
node["category"] = it["cat"]
|
||
node["desc"] = it["desc"]
|
||
|
||
|
||
def mirror_kb(kb, items):
|
||
by_word = {it["word"]: it for it in items}
|
||
out = {}
|
||
for it in items:
|
||
entry = kb.get(it["word"])
|
||
if not isinstance(entry, dict):
|
||
raise SystemExit(f"kb missing {it['word']}")
|
||
entry["id"] = it["id"]
|
||
entry["word"] = it["word"]
|
||
entry["pos"] = it["pos"]
|
||
entry["category"] = it["cat"]
|
||
entry["description"] = it["desc"]
|
||
out[it["word"]] = entry
|
||
out[str(it["id"])] = deepcopy(entry)
|
||
return out
|
||
|
||
|
||
def js_json(obj, indent=2):
|
||
raw = json.dumps(obj, ensure_ascii=False, indent=indent)
|
||
return raw.replace("</", "<\\/").replace("\u2028", "\\u2028").replace("\u2029", "\\u2029")
|
||
|
||
|
||
def embed_html(canon, words_data):
|
||
html = HTML.read_text(encoding="utf-8")
|
||
start = html.find("const TRADITIONS_CANON = ")
|
||
end = html.find("\n const CANONICAL_SYSTEMS")
|
||
if start < 0 or end < 0 or end < start:
|
||
raise SystemExit("html canon markers not found")
|
||
block = (
|
||
"const TRADITIONS_CANON = "
|
||
+ js_json(canon, 2)
|
||
+ ";\n\n const WORDS_DATA = "
|
||
+ js_json(words_data, None)
|
||
+ ";"
|
||
)
|
||
html = html[:start] + block + html[end:]
|
||
html = html.replace("10 великих систем", "${trads.length} традиций")
|
||
HTML.write_text(html, encoding="utf-8")
|
||
|
||
|
||
def write_alpha(lexicon):
|
||
rows = sorted(lexicon, key=lambda w: w["word"].lower().replace("ё", "е9"))
|
||
lines = ["# Алфавитный список 1000 слов Оракула", "", "| № | Слово | Эмодзи | ID | Часть речи | Категория |", "|---|---|---|---|---|---|"]
|
||
txt = []
|
||
for i, w in enumerate(rows, 1):
|
||
lines.append(
|
||
f"| {i} | **{w['word']}** | {w.get('emoji','')} | #{w['id']:03d} | {w.get('pos','')} | {w.get('category','')} |"
|
||
)
|
||
txt.append(f"{i}. {w['word']} {w.get('emoji','')} (id: {w['id']})")
|
||
ALPHA_MD.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||
ALPHA_TXT.write_text("\n".join(txt) + "\n", encoding="utf-8")
|
||
|
||
|
||
def assert_clean(items, kb, canon):
|
||
words = [it["word"] for it in items]
|
||
if len(words) != 1000 or len(set(words)) != 1000:
|
||
raise SystemExit("lexicon is not 1000 unique words")
|
||
if [it["id"] for it in items] != list(range(1000)):
|
||
raise SystemExit("ids are not 0..999")
|
||
by_word = set(words)
|
||
ghosts = []
|
||
for tid, trad in canon.items():
|
||
for st in trad["stations"]:
|
||
nodes = [st["primary"]] if st.get("primary") else []
|
||
nodes += st.get("cluster") or []
|
||
for n in nodes:
|
||
if n["word"] not in by_word or n["id"] != next(it["id"] for it in items if it["word"] == n["word"]):
|
||
ghosts.append(f"{tid}:{st['id']}:{n['word']}")
|
||
if ghosts:
|
||
raise SystemExit("ghosts remain: " + ", ".join(ghosts[:20]))
|
||
bad = []
|
||
for w, entry in kb.items():
|
||
if str(w).isdigit() or not isinstance(entry, dict):
|
||
continue
|
||
if low_has(entry.get("core", ""), STOCK_CORE):
|
||
bad.append("core:" + w)
|
||
if low_has(entry.get("shadow", ""), STOCK_SHADOW):
|
||
bad.append("shadow:" + w)
|
||
if low_has(entry.get("asks", ""), ("готов ли ты принять этот урок",)):
|
||
bad.append("ask:" + w)
|
||
if low_has(entry.get("step", ""), ("созерцать символ",)):
|
||
bad.append("step:" + w)
|
||
if bad:
|
||
raise SystemExit("stock remains: " + ", ".join(bad[:20]))
|
||
# id twins match word cores
|
||
for it in items:
|
||
twin = kb[str(it["id"])]
|
||
if twin.get("core") != kb[it["word"]].get("core"):
|
||
raise SystemExit(f"id twin mismatch {it['word']}")
|
||
|
||
|
||
def main():
|
||
text, items = load_dart()
|
||
lexicon = json.loads(LEX.read_text(encoding="utf-8"))
|
||
kb = json.loads(KB_ASSETS.read_text(encoding="utf-8"))
|
||
canon = json.loads(CANON_A.read_text(encoding="utf-8"))
|
||
|
||
deaths = apply_swaps(items, lexicon, kb)
|
||
apply_laws(kb, items)
|
||
rewrite_descriptions(items, kb)
|
||
stamp_from_canon(items, canon)
|
||
lex_by_id = {r["id"]: r for r in lexicon}
|
||
repair_canon(canon, items, lex_by_id)
|
||
kb = mirror_kb(kb, items)
|
||
assert_clean(items, kb, canon)
|
||
|
||
write_dart(text, items)
|
||
LEX.write_text(json.dumps(lexicon, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||
payload = json.dumps(kb, ensure_ascii=False, indent=2) + "\n"
|
||
KB_ASSETS.write_text(payload, encoding="utf-8")
|
||
KB_OUT.write_text(payload, encoding="utf-8")
|
||
canon_raw = json.dumps(canon, ensure_ascii=False, indent=2) + "\n"
|
||
CANON_A.write_text(canon_raw, encoding="utf-8")
|
||
CANON_B.write_text(canon_raw, encoding="utf-8")
|
||
words_data = []
|
||
for it in items:
|
||
words_data.append(
|
||
{
|
||
"id": it["id"],
|
||
"word": it["word"],
|
||
"emoji": lex_by_id[it["id"]].get("emoji", ""),
|
||
"pos": it["pos"],
|
||
"category": it["cat"],
|
||
"description": it["desc"],
|
||
}
|
||
)
|
||
embed_html(canon, words_data)
|
||
write_alpha(lexicon)
|
||
print(f"deaths {len(deaths)}")
|
||
for line in deaths:
|
||
print(" ", line)
|
||
print("words", len(items), "kb", len(kb), "stations", sum(len(t['stations']) for t in canon.values()))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|