#!/usr/bin/env python3 """ USLaP Schema Consolidation — Phases 2, 3, 4 Phase 3: Bitig cleanup (drop 3 empty, merge 2 pairs) — 5 tables eliminated Phase 2: Body consolidation (46 tables → 5) — 41 tables eliminated Phase 4: Foundation/Mechanism (16 → 2) — 14 tables eliminated Total: ~60 tables eliminated Must run AFTER Phase 1 (RU mirrors already done). """ import sqlite3, os, sys, json from datetime import datetime DB = os.path.join(os.path.dirname(__file__), "uslap_database_v3.db") # ═══════════════════════════════════════════════════════════════════ # PHASE 3: Bitig cleanup # ═══════════════════════════════════════════════════════════════════ BITIG_DROP = ["bitig_bridge_xref", "bitig_dispersal_edges", "bitig_sibling_propagation"] BITIG_MERGE_CORRECTIONS = { "source": "bitig_investigation", "target": "bitig_corrections", # investigation cols: inv_id, word, meaning, status, issue, next_step, quf* # corrections cols: corr_id, what_was_wrong, corrected_to, evidence, affects, status, quf* # Strategy: add source='investigation' column, map inv cols into corrections structure } BITIG_MERGE_INTEL = { "source": "bitig_operator_profiles", "target": "bitig_intelligence_summary", # intel cols: intel_id, category, dp_code, frequency, operation_signature, peak_period, # case_ids, target_language, intelligence_assessment, notes, quf* # profiles cols: profile_id, name, role, period, location, access_type, dp_codes, # method, cover_story, evidence, naming_pattern, intel_refs, notes, quf_token, quf* # Strategy: profiles have very different schema → store as JSON in notes column } # ═══════════════════════════════════════════════════════════════════ # PHASE 2: Body consolidation — 46 tables → 5 # ═══════════════════════════════════════════════════════════════════ # Tables that go into body_data (the main unified table) BODY_DATA_TABLES = [ "body_architecture", "body_chemistry", "body_colour_therapy", "body_creation_stages", "body_diagnostics", "body_movement_chains", "body_nodes", "body_preservation", "body_skeletal_map", "body_sound_therapy", "body_substances", "body_technical", "death_mechanism", "emotional_disorders", "food_contrasts", "food_production_cycle", "healing_protocols", "heart_compilation", "lifecycle_architecture", "mortality_intelligence", "nafs_architecture", "nutrition_architecture", "nutrition_intelligence", "pelvis_tissue", "perception_contamination", "perception_hierarchy", "qalb_states", "sensory_architecture", "sensory_diagnostics", "sensory_disorders", "social_health", "spirit_infusion", "transition_states", ] # Tables that go into body_cross_refs_unified BODY_XREF_TABLES = [ "body_cross_refs", "nafs_cross_refs", "sensory_cross_refs", "nutrition_cross_refs", "lifecycle_cross_refs", "body_edges", ] # Tables that go into body_prayer_map_unified BODY_PRAYER_TABLES = [ "prayer_states", "prayer_transitions", "sensory_prayer_map", "therapy_prayer_map", "nafs_prayer_map", "heptad_prayer_map", ] # Tables that stay as-is BODY_KEEP = ["body_heptad_meta", "body_extraction_intel"] # Common columns to extract (if present) — rest goes to specific_data JSON COMMON_COLS = [ "category", "arabic", "transliteration", "english", "description", "root_letters", "aa_root_id", "quranic_ref", "score", "status", "q_gate", "u_gate", "f_gate", ] QUF_COLS = ["quf_q", "quf_u", "quf_f", "quf_pass", "quf_date", "quf_token"] # ═══════════════════════════════════════════════════════════════════ # PHASE 4: Foundation/Mechanism consolidation # ═══════════════════════════════════════════════════════════════════ FOUNDATION_TABLES = [ "f1_two_originals", "f2_script_downstream", "f2_decay_criteria", "f3_frozen_stage_model", "f4_decay_gradient", "f5_destruction_timeline", "f6_manuscript_evidence", "f7_direction_of_flow", ] MECHANISM_TABLES = [ "m1_shift_descriptions", "m2_detection_patterns", "m3_scholars", "m4_networks", "m5_qur_verification", "shift_lookup", "bi_shift_lookup", ] def get_subsystem(table_name): """Map table name to subsystem category.""" if table_name.startswith("body_"): return table_name.replace("body_", "") if table_name.startswith("nafs_"): return "nafs" if table_name.startswith("sensory_"): return "sensory" if table_name.startswith("nutrition_"): return "nutrition" if table_name.startswith("lifecycle_"): return "lifecycle" if table_name.startswith("perception_"): return "perception" if table_name.startswith("food_"): return "food" mapping = { "death_mechanism": "mortality", "emotional_disorders": "nafs", "healing_protocols": "healing", "heart_compilation": "qalb", "mortality_intelligence": "mortality", "pelvis_tissue": "skeletal", "qalb_states": "qalb", "social_health": "social", "spirit_infusion": "spirit", "transition_states": "lifecycle", } return mapping.get(table_name, table_name) def get_layer(table_name): """Map foundation/mechanism table to layer ID.""" if table_name.startswith("f"): # f1_two_originals → F1, f2_script_downstream → F2, etc. return table_name.split("_")[0].upper() if table_name.startswith("m"): return table_name.split("_")[0].upper() if table_name == "shift_lookup": return "M1" if table_name == "bi_shift_lookup": return "M1_BITIG" return table_name def run(dry=False): conn = sqlite3.connect(DB) conn.execute("PRAGMA foreign_keys = OFF;") cur = conn.cursor() # Save and drop ALL triggers first (they block ALTER/INSERT) print("\n=== Saving and dropping triggers ===") triggers = cur.execute("SELECT name, sql, tbl_name FROM sqlite_master WHERE type='trigger' ORDER BY name").fetchall() print(f" {len(triggers)} triggers saved") if not dry: for tname, _, _ in triggers: cur.execute(f'DROP TRIGGER IF EXISTS [{tname}]') print(f" All triggers dropped") dropped = [] # ═══════════════════════════════════════════════════════════════ # PHASE 3: BITIG CLEANUP # ═══════════════════════════════════════════════════════════════ print("\n" + "="*60) print(" PHASE 3: Bitig Cleanup") print("="*60) # 3a: Drop 3 empty tables for tbl in BITIG_DROP: count = cur.execute(f"SELECT COUNT(*) FROM [{tbl}]").fetchone()[0] print(f" {tbl}: {count} rows", end="") if count == 0: if not dry: cur.execute(f"DROP TABLE [{tbl}]") dropped.append(tbl) print(" → DROPPED" if not dry else " → [DRY] would drop") else: print(" → SKIPPED (not empty!)") # 3b: Merge bitig_investigation → bitig_corrections inv_rows = cur.execute("SELECT * FROM bitig_investigation").fetchall() inv_cols = [d[1] for d in cur.execute("PRAGMA table_info(bitig_investigation)").fetchall()] print(f"\n Merging bitig_investigation ({len(inv_rows)}) → bitig_corrections") if not dry: try: cur.execute("ALTER TABLE bitig_corrections ADD COLUMN source TEXT DEFAULT 'correction'") except sqlite3.OperationalError: pass inserted = 0 for row in inv_rows: row_dict = dict(zip(inv_cols, row)) # Map: inv_id→corr_id, word→what_was_wrong, issue→corrected_to, # next_step→evidence, meaning→affects vals = ( row_dict.get("inv_id", ""), row_dict.get("word", "") + ": " + row_dict.get("issue", ""), row_dict.get("next_step", ""), row_dict.get("meaning", ""), row_dict.get("status", ""), row_dict.get("status", ""), row_dict.get("quf_q"), row_dict.get("quf_u"), row_dict.get("quf_f"), row_dict.get("quf_pass"), row_dict.get("quf_date"), "investigation", ) try: cur.execute("""INSERT INTO bitig_corrections (corr_id, what_was_wrong, corrected_to, evidence, affects, status, quf_q, quf_u, quf_f, quf_pass, quf_date, source) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""", vals) inserted += 1 except Exception as e: print(f" Skip {row_dict.get('inv_id')}: {e}") print(f" Inserted {inserted} rows") cur.execute("DROP TABLE bitig_investigation") dropped.append("bitig_investigation") # 3c: Merge bitig_operator_profiles → bitig_intelligence_summary prof_rows = cur.execute("SELECT * FROM bitig_operator_profiles").fetchall() prof_cols = [d[1] for d in cur.execute("PRAGMA table_info(bitig_operator_profiles)").fetchall()] print(f"\n Merging bitig_operator_profiles ({len(prof_rows)}) → bitig_intelligence_summary") if not dry: try: cur.execute("ALTER TABLE bitig_intelligence_summary ADD COLUMN source TEXT DEFAULT 'intel'") except sqlite3.OperationalError: pass inserted = 0 for row in prof_rows: row_dict = dict(zip(prof_cols, row)) # Map profile data into intel structure, store extras in notes as JSON extras = json.dumps({k: v for k, v in row_dict.items() if k not in ("profile_id", "name", "role", "dp_codes", "quf_q", "quf_u", "quf_f", "quf_pass", "quf_date") and v is not None}, ensure_ascii=False) vals = ( row_dict.get("profile_id", ""), row_dict.get("role", "operator_profile"), row_dict.get("dp_codes", ""), 0, # frequency row_dict.get("name", ""), # operation_signature row_dict.get("period", ""), # peak_period "", # case_ids "", # target_language row_dict.get("method", ""), # intelligence_assessment extras, # notes row_dict.get("quf_q"), row_dict.get("quf_u"), row_dict.get("quf_f"), row_dict.get("quf_pass"), row_dict.get("quf_date"), "operator_profile", ) try: cur.execute("""INSERT INTO bitig_intelligence_summary (intel_id, category, dp_code, frequency, operation_signature, peak_period, case_ids, target_language, intelligence_assessment, notes, quf_q, quf_u, quf_f, quf_pass, quf_date, source) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", vals) inserted += 1 except Exception as e: print(f" Skip {row_dict.get('profile_id')}: {e}") print(f" Inserted {inserted} rows") cur.execute("DROP TABLE bitig_operator_profiles") dropped.append("bitig_operator_profiles") # ═══════════════════════════════════════════════════════════════ # PHASE 2: BODY CONSOLIDATION # ═══════════════════════════════════════════════════════════════ print("\n" + "="*60) print(" PHASE 2: Body Consolidation (46 → 5)") print("="*60) # 2a: Create body_data unified table if not dry: cur.execute("""CREATE TABLE IF NOT EXISTS body_data ( body_id INTEGER PRIMARY KEY AUTOINCREMENT, subsystem TEXT NOT NULL, subtable TEXT NOT NULL, orig_id TEXT, category TEXT, arabic TEXT, transliteration TEXT, english TEXT, description TEXT, root_letters TEXT, aa_root_id TEXT, quranic_ref TEXT, score REAL, status TEXT, q_gate TEXT, u_gate TEXT, f_gate TEXT, specific_data TEXT, quf_q TEXT, quf_u TEXT, quf_f TEXT, quf_pass TEXT, quf_date TEXT, quf_token TEXT )""") print(" Created body_data table") # Migrate each body data table total_migrated = 0 for tbl in BODY_DATA_TABLES: try: count = cur.execute(f"SELECT COUNT(*) FROM [{tbl}]").fetchone()[0] except: print(f" {tbl}: NOT FOUND — skipping") continue if count == 0: print(f" {tbl}: 0 rows → DROP") if not dry: cur.execute(f"DROP TABLE [{tbl}]") dropped.append(tbl) continue cols = [d[1] for d in cur.execute(f"PRAGMA table_info([{tbl}])").fetchall()] rows = cur.execute(f"SELECT * FROM [{tbl}]").fetchall() subsystem = get_subsystem(tbl) if not dry: inserted = 0 for row in rows: row_dict = dict(zip(cols, row)) # Extract common columns orig_id = row_dict.get(cols[0], "") # First col is always the PK category_val = row_dict.get("category", row_dict.get("section", row_dict.get("level", row_dict.get("stage_order", "")))) arabic_val = row_dict.get("arabic", "") translit = row_dict.get("transliteration", "") english = row_dict.get("english", row_dict.get("component", row_dict.get("domain", ""))) desc = row_dict.get("description", row_dict.get("function", row_dict.get("health_impact", row_dict.get("biological_process", "")))) root_letters = row_dict.get("root_letters", "") aa_root = row_dict.get("aa_root_id", "") qur_ref = row_dict.get("quranic_ref", row_dict.get("quranic_text", "")) score_val = row_dict.get("score", None) status_val = row_dict.get("status", "") q_gate = row_dict.get("q_gate", "") u_gate = row_dict.get("u_gate", "") f_gate = row_dict.get("f_gate", "") # Everything else goes to specific_data JSON skip_keys = {"rowid_pk", cols[0], "category", "section", "level", "stage_order", "arabic", "transliteration", "english", "component", "domain", "description", "function", "health_impact", "biological_process", "root_letters", "aa_root_id", "quranic_ref", "quranic_text", "score", "status", "q_gate", "u_gate", "f_gate", "quf_q", "quf_u", "quf_f", "quf_pass", "quf_date", "quf_token"} specific = {k: v for k, v in row_dict.items() if k not in skip_keys and v is not None and str(v).strip()} specific_json = json.dumps(specific, ensure_ascii=False) if specific else None vals = ( subsystem, tbl, orig_id, str(category_val) if category_val else None, arabic_val, translit, english, desc, root_letters, aa_root, qur_ref, score_val, status_val, q_gate, u_gate, f_gate, specific_json, row_dict.get("quf_q"), row_dict.get("quf_u"), row_dict.get("quf_f"), row_dict.get("quf_pass"), row_dict.get("quf_date"), row_dict.get("quf_token"), ) try: cur.execute("""INSERT INTO body_data (subsystem, subtable, orig_id, category, arabic, transliteration, english, description, root_letters, aa_root_id, quranic_ref, score, status, q_gate, u_gate, f_gate, specific_data, quf_q, quf_u, quf_f, quf_pass, quf_date, quf_token) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", vals) inserted += 1 except Exception as e: print(f" Skip {tbl}/{orig_id}: {e}") total_migrated += inserted cur.execute(f"DROP TABLE [{tbl}]") dropped.append(tbl) print(f" {tbl}: {count} rows → body_data ({inserted} inserted)") else: print(f" [DRY] {tbl}: {count} rows → body_data") total_migrated += count print(f"\n Total body_data rows: {total_migrated}") # 2b: Create body_cross_refs_unified if not dry: cur.execute("""CREATE TABLE IF NOT EXISTS body_cross_refs_unified ( xref_id INTEGER PRIMARY KEY AUTOINCREMENT, subsystem TEXT NOT NULL, orig_id TEXT, source_table TEXT, source_id TEXT, target_table TEXT, target_id TEXT, relationship TEXT, quranic_ref TEXT, description TEXT, notes TEXT, aa_root_id TEXT, quf_q TEXT, quf_u TEXT, quf_f TEXT, quf_pass TEXT, quf_date TEXT )""") print("\n Created body_cross_refs_unified table") xref_total = 0 for tbl in BODY_XREF_TABLES: try: count = cur.execute(f"SELECT COUNT(*) FROM [{tbl}]").fetchone()[0] except: print(f" {tbl}: NOT FOUND — skipping") continue cols = [d[1] for d in cur.execute(f"PRAGMA table_info([{tbl}])").fetchall()] rows = cur.execute(f"SELECT * FROM [{tbl}]").fetchall() subsystem = get_subsystem(tbl) if not dry: inserted = 0 for row in rows: rd = dict(zip(cols, row)) # body_edges has different schema: from_node, to_node, edge_type, label if tbl == "body_edges": vals = ( subsystem, rd.get(cols[0], ""), "body_nodes", str(rd.get("from_node", "")), "body_nodes", str(rd.get("to_node", "")), rd.get("edge_type", ""), rd.get("quranic_ref", ""), rd.get("label", ""), rd.get("notes", ""), rd.get("aa_root_id", ""), None, None, None, None, None, ) else: vals = ( subsystem, rd.get(cols[0], ""), rd.get("source_table", ""), rd.get("source_id", ""), rd.get("target_table", ""), rd.get("target_id", ""), rd.get("relationship", ""), rd.get("quranic_ref", ""), rd.get("description", ""), rd.get("notes", ""), rd.get("aa_root_id", ""), rd.get("quf_q"), rd.get("quf_u"), rd.get("quf_f"), rd.get("quf_pass"), rd.get("quf_date"), ) try: cur.execute("""INSERT INTO body_cross_refs_unified (subsystem, orig_id, source_table, source_id, target_table, target_id, relationship, quranic_ref, description, notes, aa_root_id, quf_q, quf_u, quf_f, quf_pass, quf_date) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", vals) inserted += 1 except Exception as e: print(f" Skip {tbl}/{rd.get(cols[0])}: {e}") xref_total += inserted cur.execute(f"DROP TABLE [{tbl}]") dropped.append(tbl) print(f" {tbl}: {count} rows → body_cross_refs_unified ({inserted})") else: print(f" [DRY] {tbl}: {count} rows → body_cross_refs_unified") xref_total += count print(f" Total cross_refs: {xref_total}") # 2c: Create body_prayer_map_unified if not dry: cur.execute("""CREATE TABLE IF NOT EXISTS body_prayer_map_unified ( map_id INTEGER PRIMARY KEY AUTOINCREMENT, subsystem TEXT NOT NULL, subtable TEXT NOT NULL, orig_id TEXT, prayer_state TEXT, specific_data TEXT, quf_q TEXT, quf_u TEXT, quf_f TEXT, quf_pass TEXT, quf_date TEXT )""") print("\n Created body_prayer_map_unified table") prayer_total = 0 for tbl in BODY_PRAYER_TABLES: try: count = cur.execute(f"SELECT COUNT(*) FROM [{tbl}]").fetchone()[0] except: print(f" {tbl}: NOT FOUND — skipping") continue cols = [d[1] for d in cur.execute(f"PRAGMA table_info([{tbl}])").fetchall()] rows = cur.execute(f"SELECT * FROM [{tbl}]").fetchall() subsystem = get_subsystem(tbl) if not dry: inserted = 0 for row in rows: rd = dict(zip(cols, row)) orig_id = rd.get(cols[0], "") prayer_state = rd.get("prayer_state", rd.get("from_state", rd.get("arabic", rd.get("heptad", "")))) skip = {"rowid_pk", cols[0], "prayer_state", "from_state", "quf_q", "quf_u", "quf_f", "quf_pass", "quf_date"} specific = {k: v for k, v in rd.items() if k not in skip and v is not None and str(v).strip()} specific_json = json.dumps(specific, ensure_ascii=False) if specific else None vals = ( subsystem, tbl, orig_id, str(prayer_state) if prayer_state else None, specific_json, rd.get("quf_q"), rd.get("quf_u"), rd.get("quf_f"), rd.get("quf_pass"), rd.get("quf_date"), ) try: cur.execute("""INSERT INTO body_prayer_map_unified (subsystem, subtable, orig_id, prayer_state, specific_data, quf_q, quf_u, quf_f, quf_pass, quf_date) VALUES (?,?,?,?,?,?,?,?,?,?)""", vals) inserted += 1 except Exception as e: print(f" Skip {tbl}/{orig_id}: {e}") prayer_total += inserted cur.execute(f"DROP TABLE [{tbl}]") dropped.append(tbl) print(f" {tbl}: {count} rows → body_prayer_map_unified ({inserted})") else: print(f" [DRY] {tbl}: {count} rows → body_prayer_map_unified") prayer_total += count print(f" Total prayer_map: {prayer_total}") # ═══════════════════════════════════════════════════════════════ # PHASE 4: FOUNDATION/MECHANISM CONSOLIDATION # ═══════════════════════════════════════════════════════════════ print("\n" + "="*60) print(" PHASE 4: Foundation/Mechanism Consolidation") print("="*60) # 4a: foundation_data if not dry: cur.execute("""CREATE TABLE IF NOT EXISTS foundation_data ( fnd_id INTEGER PRIMARY KEY AUTOINCREMENT, layer TEXT NOT NULL, subtable TEXT NOT NULL, orig_id TEXT, specific_data TEXT, lang TEXT DEFAULT 'EN', quf_q TEXT, quf_u TEXT, quf_f TEXT, quf_pass TEXT, quf_date TEXT )""") print(" Created foundation_data table") fnd_total = 0 for tbl in FOUNDATION_TABLES: try: count = cur.execute(f"SELECT COUNT(*) FROM [{tbl}]").fetchone()[0] except: print(f" {tbl}: NOT FOUND — skipping") continue cols = [d[1] for d in cur.execute(f"PRAGMA table_info([{tbl}])").fetchall()] rows = cur.execute(f"SELECT * FROM [{tbl}]").fetchall() layer = get_layer(tbl) if not dry: inserted = 0 for row in rows: rd = dict(zip(cols, row)) orig_id = rd.get(cols[1], "") if len(cols) > 1 else "" # skip rowid_pk lang = rd.get("lang", "EN") skip = {"rowid_pk", "quf_q", "quf_u", "quf_f", "quf_pass", "quf_date", "lang"} specific = {k: v for k, v in rd.items() if k not in skip and v is not None and str(v).strip()} specific_json = json.dumps(specific, ensure_ascii=False) if specific else None vals = ( layer, tbl, orig_id, specific_json, lang, rd.get("quf_q"), rd.get("quf_u"), rd.get("quf_f"), rd.get("quf_pass"), rd.get("quf_date"), ) try: cur.execute("""INSERT INTO foundation_data (layer, subtable, orig_id, specific_data, lang, quf_q, quf_u, quf_f, quf_pass, quf_date) VALUES (?,?,?,?,?,?,?,?,?,?)""", vals) inserted += 1 except Exception as e: print(f" Skip {tbl}: {e}") fnd_total += inserted cur.execute(f"DROP TABLE [{tbl}]") dropped.append(tbl) print(f" {tbl}: {count} rows → foundation_data ({inserted})") else: print(f" [DRY] {tbl}: {count} rows → foundation_data") fnd_total += count print(f" Total foundation_data: {fnd_total}") # 4b: mechanism_data if not dry: cur.execute("""CREATE TABLE IF NOT EXISTS mechanism_data ( mech_id INTEGER PRIMARY KEY AUTOINCREMENT, layer TEXT NOT NULL, subtable TEXT NOT NULL, orig_id TEXT, specific_data TEXT, lang TEXT DEFAULT 'EN', quf_q TEXT, quf_u TEXT, quf_f TEXT, quf_pass TEXT, quf_date TEXT )""") print("\n Created mechanism_data table") mech_total = 0 for tbl in MECHANISM_TABLES: try: count = cur.execute(f"SELECT COUNT(*) FROM [{tbl}]").fetchone()[0] except: print(f" {tbl}: NOT FOUND — skipping") continue cols = [d[1] for d in cur.execute(f"PRAGMA table_info([{tbl}])").fetchall()] rows = cur.execute(f"SELECT * FROM [{tbl}]").fetchall() layer = get_layer(tbl) if not dry: inserted = 0 for row in rows: rd = dict(zip(cols, row)) # First non-rowid column is the semantic ID id_col = cols[0] if cols[0] != "rowid_pk" else (cols[1] if len(cols) > 1 else "") orig_id = rd.get(id_col, "") skip = {"rowid_pk", "quf_q", "quf_u", "quf_f", "quf_pass", "quf_date", "quf_token", "lang"} specific = {k: v for k, v in rd.items() if k not in skip and v is not None and str(v).strip()} specific_json = json.dumps(specific, ensure_ascii=False) if specific else None vals = ( layer, tbl, orig_id, specific_json, rd.get("lang", "EN"), rd.get("quf_q"), rd.get("quf_u"), rd.get("quf_f"), rd.get("quf_pass"), rd.get("quf_date"), ) try: cur.execute("""INSERT INTO mechanism_data (layer, subtable, orig_id, specific_data, lang, quf_q, quf_u, quf_f, quf_pass, quf_date) VALUES (?,?,?,?,?,?,?,?,?,?)""", vals) inserted += 1 except Exception as e: print(f" Skip {tbl}: {e}") mech_total += inserted cur.execute(f"DROP TABLE [{tbl}]") dropped.append(tbl) print(f" {tbl}: {count} rows → mechanism_data ({inserted})") else: print(f" [DRY] {tbl}: {count} rows → mechanism_data") mech_total += count print(f" Total mechanism_data: {mech_total}") # ═══════════════════════════════════════════════════════════════ # RECREATE VIEWS + TRIGGERS # ═══════════════════════════════════════════════════════════════ print("\n" + "="*60) print(" Recreating views and triggers") print("="*60) if not dry: # Compatibility views for body tables body_views = {} for tbl in BODY_DATA_TABLES: body_views[tbl] = f"""CREATE VIEW [{tbl}] AS SELECT body_id, orig_id, category, arabic, transliteration, english, description, root_letters, aa_root_id, quranic_ref, score, status, q_gate, u_gate, f_gate, specific_data, quf_q, quf_u, quf_f, quf_pass, quf_date FROM body_data WHERE subtable = '{tbl}'""" for tbl in BODY_XREF_TABLES: body_views[tbl] = f"""CREATE VIEW [{tbl}] AS SELECT xref_id, orig_id, source_table, source_id, target_table, target_id, relationship, quranic_ref, description, notes, aa_root_id, quf_q, quf_u, quf_f, quf_pass, quf_date FROM body_cross_refs_unified WHERE subsystem = '{get_subsystem(tbl)}'""" for tbl in BODY_PRAYER_TABLES: body_views[tbl] = f"""CREATE VIEW [{tbl}] AS SELECT map_id, orig_id, prayer_state, specific_data, quf_q, quf_u, quf_f, quf_pass, quf_date FROM body_prayer_map_unified WHERE subtable = '{tbl}'""" # Foundation/Mechanism views for tbl in FOUNDATION_TABLES: layer = get_layer(tbl) body_views[tbl] = f"""CREATE VIEW [{tbl}] AS SELECT fnd_id, orig_id, specific_data, lang, quf_q, quf_u, quf_f, quf_pass, quf_date FROM foundation_data WHERE subtable = '{tbl}'""" for tbl in MECHANISM_TABLES: body_views[tbl] = f"""CREATE VIEW [{tbl}] AS SELECT mech_id, orig_id, specific_data, lang, quf_q, quf_u, quf_f, quf_pass, quf_date FROM mechanism_data WHERE subtable = '{tbl}'""" created_views = 0 for vname, vsql in body_views.items(): try: cur.execute(vsql) created_views += 1 except Exception as e: print(f" View error {vname}: {e}") print(f" Created {created_views} compatibility views") # Also ensure the main lattice views exist main_views = { "a1_entries": """CREATE VIEW IF NOT EXISTS a1_entries AS SELECT entry_id, score, en_term, ru_term, fa_term, ar_word, root_id, root_letters, qur_refs, pattern, inversion_type, network_id, allah_name_id, phonetic_chain, source_form, ds_corridor, decay_level, dp_codes, ops_applied, foundation_refs, notes, qur_meaning, quf_q, quf_u, quf_f, quf_pass FROM entries""", "a1_записи": """CREATE VIEW IF NOT EXISTS [a1_записи] AS SELECT entry_id AS запись_id, score AS балл, ru_term AS рус_термин, ar_word AS ар_слово, root_id AS корень_id, root_letters AS корневые_буквы, qur_meaning AS коранич_значение, pattern AS паттерн, allah_name_id AS имя_аллаха_id, network_id AS сеть_id, phonetic_chain AS фонетическая_цепь, inversion_type AS тип_инверсии, source_form AS исходная_форма, foundation_refs AS основание, quf_q, quf_u, quf_f, quf_pass, quf_date, quf_token FROM entries WHERE ru_term IS NOT NULL""", "a2_names_of_allah": "CREATE VIEW IF NOT EXISTS a2_names_of_allah AS SELECT * FROM names_of_allah", "a3_quran_refs": "CREATE VIEW IF NOT EXISTS a3_quran_refs AS SELECT * FROM quran_refs", "a6_country_names": "CREATE VIEW IF NOT EXISTS a6_country_names AS SELECT * FROM country_names", "child_schema": """CREATE VIEW IF NOT EXISTS child_schema AS SELECT child_id AS entry_id, shell_name, shell_language, orig_class, orig_root, orig_lemma, orig_meaning, operation_role, shell_meaning, inversion_direction, phonetic_chain, qur_anchors, dp_codes, nt_code, pattern, parent_op, gate_status, notes FROM child_entries""", "m1_phonetic_shifts": None, # Now handled by mechanism_data view "persian_a1_mad_khil": """CREATE VIEW IF NOT EXISTS persian_a1_mad_khil AS SELECT entry_id, fa_term, ar_word, root_letters, qur_meaning, pattern, allah_name_id, network_id, phonetic_chain, inversion_type, source_form, foundation_refs, score, root_id, quf_q, quf_u, quf_f, quf_pass, quf_date, quf_token FROM entries WHERE fa_term IS NOT NULL""", } for vname, vsql in main_views.items(): if vsql is None: continue try: cur.execute(vsql) except Exception as e: if "already exists" not in str(e): print(f" Main view error {vname}: {e}") # Restore triggers — only for tables that still exist current_tables = set(r[0] for r in cur.execute( "SELECT name FROM sqlite_master WHERE type='table'").fetchall()) restored = 0 skipped = 0 for tname, tsql, ttbl in triggers: if tsql is None: skipped += 1 continue if ttbl not in current_tables: skipped += 1 continue if 'NEW.relationship' in tsql: skipped += 1 continue # Fix references to a1_entries (now a view) if 'a1_entries' in tsql and 'a1_entries' not in current_tables: tsql = tsql.replace('a1_entries', 'entries') try: cur.execute(tsql) restored += 1 except: skipped += 1 print(f" Restored {restored} triggers (skipped {skipped})") # ═══════════════════════════════════════════════════════════════ # FINAL VERIFY # ═══════════════════════════════════════════════════════════════ print("\n" + "="*60) print(" FINAL VERIFICATION") print("="*60) if not dry: conn.commit() tables = cur.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='table'").fetchone()[0] views = cur.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='view'").fetchone()[0] trigs = cur.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='trigger'").fetchone()[0] print(f" Tables: 163 → {tables}") print(f" Views: {views}") print(f" Triggers: {trigs}") print(f" Dropped: {len(dropped)} tables") # Key counts for tbl in ["body_data", "body_cross_refs_unified", "body_prayer_map_unified", "foundation_data", "mechanism_data", "entries", "names_of_allah"]: try: c = cur.execute(f"SELECT COUNT(*) FROM [{tbl}]").fetchone()[0] print(f" {tbl}: {c}") except: print(f" {tbl}: ERROR") # Integrity ic = cur.execute("PRAGMA integrity_check").fetchone()[0] print(f" Integrity: {ic}") else: print(f" [DRY RUN] Would drop {len(BITIG_DROP) + len(BODY_DATA_TABLES) + len(BODY_XREF_TABLES) + len(BODY_PRAYER_TABLES) + len(FOUNDATION_TABLES) + len(MECHANISM_TABLES) + 2} tables") conn.close() print("\nDone.") if __name__ == "__main__": dry = "--dry" in sys.argv if dry: print("=== DRY RUN ===") else: print("=== LIVE RUN ===") run(dry=dry)