feat: complete academic symbol lexicon heal, 2-column Cooper extraction, and updated assets
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Geomancy: structured dots, flatten fake groups, lexicon intersections."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
CANON = ROOT / "oracle_enricher" / "data" / "traditions_canon.json"
|
||||
ASSET = ROOT / "assets" / "data" / "traditions_canon.json"
|
||||
LEX = ROOT / "oracle_enricher" / "data" / "lexicon.json"
|
||||
DART = ROOT / "lib" / "data" / "words_data.dart"
|
||||
HTML = ROOT / "lexicon_viewer.html"
|
||||
MD = ROOT / "words_alphabetical.md"
|
||||
|
||||
GEO_TAG_RE = re.compile(r"\[Геомантия:[^\]]*\]\s*")
|
||||
|
||||
# head → feet (Fire, Air, Water, Earth): 1 = single (odd), 2 = double (even)
|
||||
DOTS = {
|
||||
"via": [1, 1, 1, 1],
|
||||
"populus": [2, 2, 2, 2],
|
||||
"conjunctio": [2, 1, 1, 2],
|
||||
"carcer": [1, 2, 2, 1],
|
||||
"fortuna_major": [2, 2, 1, 1],
|
||||
"fortuna_minor": [1, 1, 2, 2],
|
||||
"acquisitio": [2, 1, 2, 1],
|
||||
"amissio": [1, 2, 1, 2],
|
||||
"laetitia": [1, 2, 2, 2],
|
||||
"tristitia": [2, 2, 2, 1],
|
||||
"puella": [1, 2, 1, 1],
|
||||
"puer": [1, 1, 2, 1],
|
||||
"albus": [2, 2, 1, 2],
|
||||
"rubeus": [2, 1, 2, 2],
|
||||
"caput_draconis": [2, 1, 1, 1],
|
||||
"cauda_draconis": [1, 1, 1, 2],
|
||||
}
|
||||
|
||||
META = {
|
||||
"via": ("Вода", "Луна", "путь", ["дорога", "тропа"]),
|
||||
"populus": ("Вода", "Луна", "народ", ["встреча", "мир"]),
|
||||
"conjunctio": ("Воздух", "Меркурий", "союз", ["встреча", "связь"]),
|
||||
"carcer": ("Земля", "Сатурн", "верёвка", ["плен", "цепь", "граница"]),
|
||||
"fortuna_major": ("Земля", "Солнце", "победа", ["защита", "солнце"]),
|
||||
"fortuna_minor": ("Огонь", "Солнце", "успех", ["случай", "помощь"]),
|
||||
"acquisitio": ("Воздух", "Юпитер", "дар", ["золото", "изобилие"]),
|
||||
"amissio": ("Огонь", "Венера", "жертва", ["вдова", "конец"]),
|
||||
"laetitia": ("Воздух", "Юпитер", "радость", ["смех", "пир", "свет"]),
|
||||
"tristitia": ("Земля", "Сатурн", "печаль", ["скорбь", "холод"]),
|
||||
"puella": ("Вода", "Венера", "девушка", ["луна", "чистота"]),
|
||||
"puer": ("Огонь", "Марс", "мальчик", ["огонь", "воля", "страсть"]),
|
||||
"albus": ("Воздух", "Меркурий", "белый", ["чистота", "мудрость", "покой"]),
|
||||
"rubeus": ("Огонь", "Марс", "красный", ["огонь", "кровь", "страсть"]),
|
||||
"caput_draconis": ("Земля", "Северный Узел", "начало", ["вход", "порог", "рост"]),
|
||||
"cauda_draconis": ("Огонь", "Южный Узел", "конец", ["выход", "жертва", "цикл"]),
|
||||
}
|
||||
|
||||
|
||||
def symbol_from(dots: list[int]) -> str:
|
||||
return "\n".join("·" * n for n in dots)
|
||||
|
||||
|
||||
def payload(word: str, dart_by_word: dict, lex_by_word: dict, old: dict | None = None) -> dict:
|
||||
lex = lex_by_word.get(word) or {}
|
||||
old = old or {}
|
||||
return {
|
||||
"id": dart_by_word[word],
|
||||
"word": word,
|
||||
"emoji": lex.get("emoji") or old.get("emoji") or "",
|
||||
"pos": lex.get("pos") or old.get("pos") or "существительное",
|
||||
"category": lex.get("category") or old.get("category") or "",
|
||||
"desc": "",
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
dart_text = DART.read_text(encoding="utf-8")
|
||||
dart_by_word = {
|
||||
w: int(i)
|
||||
for i, w in re.findall(r'WordItem\(\s*id:\s*(\d+),\s*word:\s*"([^"]+)"', dart_text)
|
||||
}
|
||||
lex_by_word = {e["word"]: e for e in json.loads(LEX.read_text(encoding="utf-8"))}
|
||||
canon = json.loads(CANON.read_text(encoding="utf-8"))
|
||||
geo = canon["geomancy"]
|
||||
word_to_tags: dict[str, list[str]] = defaultdict(list)
|
||||
missing = []
|
||||
|
||||
for st in geo["stations"]:
|
||||
sid = st["id"]
|
||||
dots = DOTS[sid]
|
||||
element, planet, prim_w, extra_names = META[sid]
|
||||
st["dots"] = dots
|
||||
st["symbol"] = symbol_from(dots)
|
||||
st["group"] = f"Геомантия · {element} · {planet}"
|
||||
rec = {
|
||||
"via": ("الطريق", "ат-Тарик", "Via", "Путь"),
|
||||
"populus": ("الجماعة", "аль-Джама'а", "Populus", "Народ"),
|
||||
"conjunctio": ("الاجتماع", "аль-Иджтима'", "Conjunctio", "Соединение"),
|
||||
"carcer": ("الحبس", "аль-Хабс", "Carcer", "Тюрьма"),
|
||||
"fortuna_major": ("النصرة الداخلة", "ан-Насра ад-Дахиля", "Fortuna Major", "Большая Удача"),
|
||||
"fortuna_minor": ("النصرة الخارجة", "ан-Насра аль-Хариджа", "Fortuna Minor", "Малая Удача"),
|
||||
"acquisitio": ("القبض الداخل", "аль-Кабд ад-Дахиль", "Acquisitio", "Приобретение"),
|
||||
"amissio": ("القبض الخارج", "аль-Кабд аль-Харидж", "Amissio", "Потеря"),
|
||||
"laetitia": ("الفرح", "аль-Фарах", "Laetitia", "Радость"),
|
||||
"tristitia": ("الحزن", "аль-Хузн", "Tristitia", "Печаль"),
|
||||
"puella": ("نقي الخد", "наки аль-хадд", "Puella", "Девушка"),
|
||||
"puer": ("الانكيس", "аль-Анкийс", "Puer", "Юноша"),
|
||||
"albus": ("البياض", "аль-Байяд", "Albus", "Белый"),
|
||||
"rubeus": ("الحمرة", "аль-Хумра", "Rubeus", "Красный"),
|
||||
"caput_draconis": ("رأس الجوزهر", "ра'с аль-джузхар", "Caput Draconis", "Голова Дракона"),
|
||||
"cauda_draconis": ("ذنب الجوزهر", "занаб аль-джузхар", "Cauda Draconis", "Хвост Дракона"),
|
||||
}.get(sid)
|
||||
if rec:
|
||||
script, latn, latin, ru = rec
|
||||
st["arabic"] = script
|
||||
n = int("".join(ch for ch in st.get("name", "") if ch.isdigit()) or "0") or (
|
||||
list(DOTS).index(sid) + 1
|
||||
)
|
||||
st["name"] = f"{n} {latin} ({latn} / {ru})"
|
||||
if prim_w not in dart_by_word:
|
||||
missing.append(f"primary {sid}/{prim_w}")
|
||||
continue
|
||||
old_p = st.get("primary") or {}
|
||||
st["primary"] = payload(prim_w, dart_by_word, lex_by_word, old_p)
|
||||
extra = []
|
||||
seen = {prim_w}
|
||||
for w in extra_names:
|
||||
if w in seen:
|
||||
continue
|
||||
if w not in dart_by_word:
|
||||
missing.append(f"cluster {sid}/{w}")
|
||||
continue
|
||||
seen.add(w)
|
||||
extra.append(w)
|
||||
st["cluster"] = [payload(w, dart_by_word, lex_by_word) for w in extra]
|
||||
tag = f"[Геомантия: {st['name']}]"
|
||||
word_to_tags[prim_w].append(tag)
|
||||
for w in extra:
|
||||
word_to_tags[w].append(tag)
|
||||
|
||||
if missing:
|
||||
print("WARN", missing)
|
||||
|
||||
geo["description"] = (
|
||||
"16 классических фигур 'Илм ар-рамл: четыре ряда точек (голова → стопы), "
|
||||
"стихия и планета. Пересечения со словарём 999."
|
||||
)
|
||||
geo["count"] = len(geo["stations"])
|
||||
|
||||
text = json.dumps(canon, ensure_ascii=False, indent=2) + "\n"
|
||||
CANON.write_text(text, encoding="utf-8")
|
||||
ASSET.write_text(text, encoding="utf-8")
|
||||
|
||||
dart_lines = dart_text.splitlines(keepends=True)
|
||||
nstamp = 0
|
||||
i = 0
|
||||
while i < len(dart_lines):
|
||||
wm = re.match(r'(\s*word: ")([^"]+)(",\s*)$', dart_lines[i])
|
||||
if wm:
|
||||
word = wm.group(2)
|
||||
for j in range(i + 1, min(i + 12, len(dart_lines))):
|
||||
raw = dart_lines[j].rstrip("\n")
|
||||
dm = re.match(r'^(\s*description: ")(.*)(",)\s*$', raw)
|
||||
if dm:
|
||||
body = GEO_TAG_RE.sub("", dm.group(2)).strip()
|
||||
tags = word_to_tags.get(word)
|
||||
if tags:
|
||||
dart_lines[j] = f"{dm.group(1)}{' '.join(tags)} {body}{dm.group(3)}\n"
|
||||
nstamp += 1
|
||||
else:
|
||||
dart_lines[j] = f"{dm.group(1)}{body}{dm.group(3)}\n"
|
||||
break
|
||||
if dart_lines[j].strip().startswith("WordItem("):
|
||||
break
|
||||
i += 1
|
||||
dart2 = "".join(dart_lines)
|
||||
DART.write_text(dart2, encoding="utf-8")
|
||||
|
||||
html = HTML.read_text(encoding="utf-8")
|
||||
start = html.find(" const TRADITIONS_CANON = ")
|
||||
end = html.find(" const WORDS_DATA = ")
|
||||
if start > 0 and end > start:
|
||||
html = (
|
||||
html[:start]
|
||||
+ " const TRADITIONS_CANON = "
|
||||
+ json.dumps(canon, ensure_ascii=False, indent=2)
|
||||
+ ";\n\n"
|
||||
+ html[end:]
|
||||
)
|
||||
m = re.search(r"const WORDS_DATA = (\[.*?\]);", html, flags=re.DOTALL)
|
||||
if not m:
|
||||
raise SystemExit("WORDS_DATA not found")
|
||||
words = json.loads(m.group(1))
|
||||
dart_descs = dict(re.findall(r'word: "([^"]+)"[\s\S]*?description: "([^"]*)"', dart2))
|
||||
for w in words:
|
||||
wd = w.get("word")
|
||||
if wd in dart_descs:
|
||||
w["description"] = dart_descs[wd]
|
||||
html = html[: m.start(1)] + json.dumps(words, ensure_ascii=False) + html[m.end(1) :]
|
||||
HTML.write_text(html, encoding="utf-8")
|
||||
|
||||
if MD.exists():
|
||||
md = MD.read_text(encoding="utf-8")
|
||||
|
||||
def repl_block(mm: re.Match) -> str:
|
||||
word = mm.group(1)
|
||||
body = mm.group(3)
|
||||
if word not in dart_descs:
|
||||
return mm.group(0)
|
||||
body2 = re.sub(
|
||||
r"\* \*\*Толкование:\*\* .+",
|
||||
f"* **Толкование:** {dart_descs[word]}",
|
||||
body,
|
||||
count=1,
|
||||
)
|
||||
return f"### {word} `{mm.group(2)}`\n{body2}"
|
||||
|
||||
md2 = re.sub(
|
||||
r"### ([^\s`]+) `(#[^`]+)`\n([\s\S]*?)(?=\n### |\n## |\Z)",
|
||||
repl_block,
|
||||
md,
|
||||
)
|
||||
MD.write_text(md2, encoding="utf-8")
|
||||
|
||||
print(f"stamped {nstamp} tagged {len(word_to_tags)}")
|
||||
keys = defaultdict(list)
|
||||
for st in geo["stations"]:
|
||||
gkey = st["group"].split(" · ", 1)[0]
|
||||
keys[gkey].append(st["id"])
|
||||
cl = [c["word"] for c in st["cluster"]]
|
||||
print(
|
||||
f" {st['id']:16} {st['dots']} {st['group']:32} "
|
||||
f"prim={st['primary']['word']:12} {cl}"
|
||||
)
|
||||
print("group keys", {k: len(v) for k, v in keys.items()})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user