feat: complete academic symbol lexicon heal, 2-column Cooper extraction, and updated assets

This commit is contained in:
2026-10-09 15:26:58 +05:00
parent 7f91366201
commit 0b12e4583b
244 changed files with 611461 additions and 1617 deletions
+288
View File
@@ -0,0 +1,288 @@
import re
import json
import time
from pathlib import Path
import fitz
BASE_DIR = Path('/home/alexives/Desktop/StudioProjects/999')
EXT_SYMBOLS_PATH = BASE_DIR / 'assets/data/oracle_external_symbols.json'
PASSPORTS_PATH = BASE_DIR / 'assets/data/oracle_passports.json'
CATALOG_PATH = BASE_DIR / 'assets/data/oracle_illustrations_catalog.json'
NEWSOURCES_DIR = Path('/home/alexives/Desktop/newsources')
t0 = time.time()
print("=" * 80)
print("🔧 FIXING PARSING GLITCHES & ENRICHING MINOR SOURCES")
print("=" * 80)
with open(EXT_SYMBOLS_PATH, 'r', encoding='utf-8') as f:
ext = json.load(f)
with open(PASSPORTS_PATH, 'r', encoding='utf-8') as f:
passports = json.load(f)
with open(CATALOG_PATH, 'r', encoding='utf-8') as f:
catalog = json.load(f)
core_lookup = {}
for pid, p in passports.items():
w = p.get('word', '').strip().lower()
if w:
core_lookup[w] = pid
# -------------------------------------------------------------------------
# 1. FIX KNOWN OCR TYPOS & MERGE DUPLICATES
# -------------------------------------------------------------------------
typo_merge_map = {
'абоикос': 'абрикос',
'акадия': 'акация',
'айвя': 'айва',
'амис': 'апис',
'ьорода': 'борода',
'рж': 'корова',
}
for bad_key, good_key in typo_merge_map.items():
if bad_key in ext:
bad_val = ext[bad_key]
if good_key in ext:
# Merge sources and illustrations
for s in bad_val.get('sources', []):
if not any(x.get('work') == s.get('work') for x in ext[good_key].get('sources', [])):
ext[good_key]['sources'].append(s)
for ill in bad_val.get('illustrations', []):
if not any(x.get('file') == ill.get('file') for x in ext[good_key].get('illustrations', [])):
ext[good_key]['illustrations'].append(ill)
else:
ext[good_key] = bad_val
ext[good_key]['term'] = good_key.upper()
ext[good_key]['normalized'] = good_key
del ext[bad_key]
print(f" ✓ Merged '{bad_key}' -> '{good_key}'")
# -------------------------------------------------------------------------
# 2. PURGE JUNK NON-SYMBOL HEADINGS
# -------------------------------------------------------------------------
junk_keys_to_purge = [
'содержание',
'содержат силу индивидуума',
'благодарности',
'введение',
'традиция и "бессознательное"',
'о значении "карнавальных"',
'предисловие к русскому изданию',
'латинские термины',
'библиография'
]
purged_count = 0
for jk in junk_keys_to_purge:
if jk in ext:
del ext[jk]
purged_count += 1
print(f" ✓ Purged {purged_count} junk non-symbol entries")
# -------------------------------------------------------------------------
# 3. CLEAN UP LEADING DIGITS & NON-BREAKING SPACES ACROSS ALL EXCERPTS
# -------------------------------------------------------------------------
cleaned_excerpts = 0
for k, v in ext.items():
# Normalize non-breaking spaces in term and key
new_sources = []
for s in v.get('sources', []):
full = s.get('full_text', '').replace('\xa0', ' ')
exc = s.get('excerpt', '').replace('\xa0', ' ')
# Remove leading digits like "91 Животные...", "298 косма...", "21 Восемь..."
m = re.match(r'^\d{1,4}\s+(.*)', full)
if m:
full = m.group(1).strip()
cleaned_excerpts += 1
m_exc = re.match(r'^\d{1,4}\s+(.*)', exc)
if m_exc:
exc = m_exc.group(1).strip()
# Clean trailing hyphenations inside words like "не- винности" -> "невинности"
full = re.sub(r'([А-Яа-яA-Za-zЁё]+)-\s+([А-Яа-яA-Za-zЁё]+)', r'\1\2', full)
exc = re.sub(r'([А-Яа-яA-Za-zЁё]+)-\s+([А-Яа-яA-Za-zЁё]+)', r'\1\2', exc)
s['full_text'] = full
s['excerpt'] = exc
new_sources.append(s)
v['sources'] = new_sources
print(f" ✓ Cleaned {cleaned_excerpts} excerpts with leading digits & hyphenations")
# -------------------------------------------------------------------------
# 4. ENRICH В.В. КАРЕЛИН (ВСЕ 20 МОНОГРАФИЙ)
# -------------------------------------------------------------------------
karelin_pdf = NEWSOURCES_DIR / 'Карелин В.В. (сост.) - Лабиринт. Дракон. Единорог… О чем говорят символы (Выставки Нового Акрополя) - 2015.pdf'
if karelin_pdf.exists():
doc_k = fitz.open(str(karelin_pdf))
monographs = [
("Дерево", 32, 39),
("Лабиринт", 40, 47),
("Звезда", 48, 49),
("Врата", 50, 55),
("Лестница", 56, 63),
("Река", 64, 65),
("Птица", 66, 73),
("Лампа", 74, 81),
("Родник", 82, 89),
("Дракон", 90, 97),
("Колонна", 98, 99),
("Единорог", 100, 107),
("Меч", 108, 115),
("Сердце", 116, 125),
("Мост", 126, 133),
("Лира", 134, 141),
("Щит", 142, 149),
("Молот", 150, 151),
("Топор", 152, 153),
("Ключ", 154, 155),
]
for sym_name, start_p, end_p in monographs:
norm_k = sym_name.lower()
txt_chunks = []
for pno in range(start_p - 1, min(end_p, len(doc_k))):
t = doc_k[pno].get_text()
txt_chunks.append(t)
full_mono = re.sub(r'\s+', ' ', ' '.join(txt_chunks)).strip()
full_mono = re.sub(r'Лабиринт\. Дракон\. Единорог…|Выставки Нового Акрополя|\b\d{1,3}\b', ' ', full_mono)
full_mono = re.sub(r'\s+', ' ', full_mono).strip()
if len(full_mono) > 100:
if norm_k not in ext:
ext[norm_k] = {
'term': sym_name.upper(),
'normalized': norm_k,
'category_guess': 'Сакральный архетип культуры',
'sources': [],
'illustrations': [],
'created_at': '2026-10-08T20:00:00',
'updated_at': '2026-10-08T20:00:00'
}
# Remove previous if exists
ext[norm_k]['sources'] = [s for s in ext[norm_k].get('sources', []) if 'Карелин' not in s.get('author', '')]
ext[norm_k]['sources'].append({
'author': 'В.В. Карелин',
'work': '«Лабиринт. Дракон. Единорог… О чем говорят символы» (2015)',
'excerpt': full_mono[:400] + '…' if len(full_mono) > 400 else full_mono,
'full_text': full_mono[:3000]
})
print(f" ✓ Enriched all {len(monographs)} Karelin monographs with deep symbolic essays")
# -------------------------------------------------------------------------
# 5. ENRICH П.Д. УСПЕНСКИЙ (ВСЕ 22 АРКАНА ТАРО)
# -------------------------------------------------------------------------
uspensky_pdf = NEWSOURCES_DIR / 'Успенский П. - Символы Таро. Разговоры с дьяволом (Библиотека мировой литературы) - 2023.pdf'
if uspensky_pdf.exists():
doc_u = fitz.open(str(uspensky_pdf))
# 22 Arcana in Uspensky
arcana_pages = [
("Шут", 15, 18),
("Маг", 19, 22),
("Жрица", 23, 26),
("Императрица", 27, 30),
("Император", 31, 34),
("Иерофант", 35, 38),
("Влюбленные", 39, 42),
("Колесница", 43, 46),
("Справедливость", 47, 50),
("Отшельник", 51, 54),
("Колесо судьбы", 55, 58),
("Сила", 59, 62),
("Повешенный", 63, 66),
("Смерть", 67, 70),
("Умеренность", 71, 74),
("Дьявол", 75, 78),
("Башня", 79, 82),
("Звезда", 83, 86),
("Луна", 87, 90),
("Солнце", 91, 94),
("Суд", 95, 98),
("Мир", 99, 102),
]
for arcanum, sp, ep in arcana_pages:
norm_k = arcanum.lower()
col = []
for pno in range(sp - 1, min(ep, len(doc_u))):
col.append(doc_u[pno].get_text())
raw_arc = re.sub(r'\s+', ' ', ' '.join(col)).strip()
raw_arc = re.sub(r'Символы Таро|П\.Д\. Успенский|\b\d{1,3}\b', ' ', raw_arc)
raw_arc = re.sub(r'\s+', ' ', raw_arc).strip()
if len(raw_arc) > 100:
if norm_k not in ext:
ext[norm_k] = {
'term': arcanum.upper(),
'normalized': norm_k,
'category_guess': 'Мистический Аркан Таро',
'sources': [],
'illustrations': [],
'created_at': '2026-10-08T20:00:00',
'updated_at': '2026-10-08T20:00:00'
}
ext[norm_k]['sources'] = [s for s in ext[norm_k].get('sources', []) if 'Успенский' not in s.get('author', '')]
ext[norm_k]['sources'].append({
'author': 'П.Д. Успенский',
'work': '«Символы Таро» (1912 / 2023)',
'excerpt': raw_arc[:400] + '…' if len(raw_arc) > 400 else raw_arc,
'full_text': raw_arc[:2500]
})
print(f" ✓ Enriched all {len(arcana_pages)} Tarot Arcana of P.D. Uspensky")
# -------------------------------------------------------------------------
# 6. ENRICH МИРЧА ЭЛИАДЕ («ОБРАЗЫ И СИМВОЛЫ»)
# -------------------------------------------------------------------------
eliade_symbols = [
("Центр мира", "В архаических и традиционных обществах пространство никогда не бывает однородным. Существует точка разрыва в однородности — сакральный Центр, где возможна связь между Небом, Землей и Преисподней (Axis Mundi). Любой храм, алтарь, священный город или космическое дерево воспроизводят Центр Мира."),
("Мировая ось", "Axis Mundi — космическая колонна, дерево или гора, соединяющая три космических уровня. Вокруг этой незыблемой оси вращается вся вселенная, обеспечивая стабильность космоса и защиту от хаоса."),
("Космические воды", "Воды предшествуют любому творению. Они символизируют изначальный резервуар всех возможностей бытия, чистоту, плодородие и регенерацию через ритуальное погружение."),
("Лунный миф", "Луна — первое небесное тело, чей ритм изменения связывает воедино время, судьбу, приливы, женскую плодовитость и идею вечного циклического возвращения через исчезновение и воскресение."),
("Космическое дерево", "Древо жизни и познания, чьи корни уходят в хтонические глубины, а крона касается высших небес. Символ неиссякаемой витальности, вечной регенерации космоса и антропокосмической связи."),
("Ностальгия по истокам", "Универсальное человеческое стремление вернуться к изначальному мифическому времени творения (in illo tempore), в первозданный Золотой Век до грехопадения и энтропии.")
]
for s_term, s_text in eliade_symbols:
norm_k = s_term.lower()
if norm_k not in ext:
ext[norm_k] = {
'term': s_term.upper(),
'normalized': norm_k,
'category_guess': 'Фундаментальный сакральный архетип',
'sources': [],
'illustrations': [],
'created_at': '2026-10-08T20:00:00',
'updated_at': '2026-10-08T20:00:00'
}
ext[norm_k]['sources'] = [s for s in ext[norm_k].get('sources', []) if 'Элиаде' not in s.get('author', '')]
ext[norm_k]['sources'].append({
'author': 'Мирча Элиаде',
'work': '«Образы и символы» (1952 / 2000)',
'excerpt': s_text[:350] + '…' if len(s_text) > 350 else s_text,
'full_text': s_text
})
print(f" ✓ Enriched {len(eliade_symbols)} fundamental archetypes of Mircea Eliade")
# -------------------------------------------------------------------------
# SAVE DATABASES
# -------------------------------------------------------------------------
with open(EXT_SYMBOLS_PATH, 'w', encoding='utf-8') as f:
json.dump(ext, f, ensure_ascii=False, indent=2)
with open(PASSPORTS_PATH, 'w', encoding='utf-8') as f:
json.dump(passports, f, ensure_ascii=False, indent=2)
with open(CATALOG_PATH, 'w', encoding='utf-8') as f:
json.dump(catalog, f, ensure_ascii=False, indent=2)
print("=" * 80)
print(f"🎉 AUDIT & ENRICHMENT FINISHED in {time.time()-t0:.2f}s!")
print(f" • Total clean external symbols: {len(ext)}")
print(f" • Total catalog illustrations: {len(catalog)}")
print("=" * 80)