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