Spaces:
Sleeping
Sleeping
File size: 12,450 Bytes
7cc8e29 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | #!/usr/bin/env python3
"""
Build SQLite database from USLaP_Final_Data_Consolidated_Master_v2.xlsx
Preserves UTF-8 Arabic text exactly as stored.
Creates one table per primary sheet.
Adds cross-reference table linking ENTRY_ID → PARENT_OP → DP_REF.
"""
import sqlite3
import openpyxl
import re
from pathlib import Path
def clean_column_name(col):
"""Convert Excel column header to valid SQLite column name."""
if col is None:
return "unknown"
# Remove non-alphanumeric, replace spaces with underscore
col = str(col).strip()
col = re.sub(r'[^\w\s]', '', col) # Remove punctuation
col = re.sub(r'\s+', '_', col) # Replace spaces with underscore
col = col.lower()
if not col:
return "unknown"
return col
def extract_parent_ops(parent_op_str):
"""Extract individual parent operations from strings like 'UMD-RL1 + UMD-ST1'"""
if not parent_op_str:
return []
# Split by various separators
ops = re.split(r'[·,\+\s]+', str(parent_op_str))
# Filter for operation codes (UMD- prefix)
parent_ops = []
for op in ops:
op = op.strip()
if op and re.match(r'^UMD-', op):
parent_ops.append(op)
return parent_ops
def extract_dp_codes(dp_string):
"""Extract individual DP codes from strings like 'DP08 · DP07 · DP11 · DP15'"""
if not dp_string:
return []
# Split by various separators
codes = re.split(r'[·,\s]+', str(dp_string))
# Filter for DP codes (start with DP followed by digits or word)
dp_codes = []
for code in codes:
code = code.strip()
if code and (re.match(r'^DP\d+', code) or re.match(r'^DP-', code)):
dp_codes.append(code)
return dp_codes
def create_tables(conn, cursor):
"""Create tables for each primary sheet."""
# Create UMD_OPERATIONS table
cursor.execute('''
CREATE TABLE IF NOT EXISTS umd_operations (
op_id TEXT PRIMARY KEY,
op_name TEXT,
op_class TEXT,
qur_primary TEXT,
qur_secondary TEXT,
op_structure TEXT,
founding_instances TEXT,
dp_always_active TEXT,
gate_shortcut TEXT,
np_layer TEXT,
darvo_active TEXT,
notes TEXT,
status TEXT,
last_updated TEXT
)
''')
# Create CHILD_SCHEMA table
cursor.execute('''
CREATE TABLE IF NOT EXISTS child_schema (
entry_id TEXT PRIMARY KEY,
shell_name TEXT,
shell_language TEXT,
orig_class TEXT,
orig_root TEXT,
orig_lemma TEXT,
orig_meaning TEXT,
operation_role TEXT,
shell_meaning TEXT,
inversion_direction TEXT,
phonetic_chain TEXT,
qur_anchors TEXT,
dp_codes TEXT,
nt_code TEXT,
pattern TEXT,
parent_op TEXT,
gate_status TEXT,
notes TEXT
)
''')
# Create DP_REGISTER table
cursor.execute('''
CREATE TABLE IF NOT EXISTS dp_register (
dp_code TEXT PRIMARY KEY,
name TEXT,
class TEXT,
trigger TEXT,
mechanism TEXT,
qur_anchor TEXT,
example TEXT,
distinct_from TEXT,
protocol_note TEXT,
status TEXT
)
''')
# Create ATT_TERMS table
cursor.execute('''
CREATE TABLE IF NOT EXISTS att_terms (
term_id TEXT PRIMARY KEY,
arabic TEXT,
transliteration TEXT,
translation TEXT,
root TEXT,
qur_anchor TEXT,
function_in_lattice TEXT,
umd_op_ref TEXT,
dp_ref TEXT,
inversion_type TEXT,
notes TEXT
)
''')
# Create PHONETIC_REVERSAL table
cursor.execute('''
CREATE TABLE IF NOT EXISTS phonetic_reversal (
shift_code TEXT PRIMARY KEY,
from_modern TEXT,
to_orig TEXT,
class TEXT,
mechanism TEXT,
attested_example TEXT,
entry_ref TEXT,
reliability TEXT,
notes TEXT,
status TEXT
)
''')
# Create SESSION_INDEX table
cursor.execute('''
CREATE TABLE IF NOT EXISTS session_index (
id INTEGER PRIMARY KEY AUTOINCREMENT,
item_type TEXT,
entry_id TEXT,
description TEXT,
sheet TEXT,
status TEXT,
notes TEXT
)
''')
# Create cross-reference table
cursor.execute('''
CREATE TABLE IF NOT EXISTS cross_reference (
id INTEGER PRIMARY KEY AUTOINCREMENT,
entry_id TEXT,
parent_op TEXT,
dp_ref TEXT,
FOREIGN KEY (entry_id) REFERENCES child_schema (entry_id),
FOREIGN KEY (parent_op) REFERENCES umd_operations (op_id)
)
''')
conn.commit()
print("Created all tables")
def import_sheet_data(conn, cursor, wb, sheet_name, table_name):
"""Import data from an Excel sheet to SQLite table."""
ws = wb[sheet_name]
rows = list(ws.iter_rows(values_only=True))
if not rows:
print(f" No data in {sheet_name}")
return 0
# Find header row based on sheet-specific patterns
header_row = None
data_start = 0
# Special handling for each sheet based on debug output
if sheet_name == "DP_REGISTER":
# Row 0: title, Row 1: description, Row 2: headers
if len(rows) > 2:
header_row = rows[2]
data_start = 3
elif sheet_name == "PHONETIC_REVERSAL":
# Row 0: title, Row 1: description, Row 2: zone header, Row 3: headers
if len(rows) > 3:
header_row = rows[3]
data_start = 4
elif sheet_name == "UMD_OPERATIONS":
# Row 0: title, Row 1: description, Row 2: headers
if len(rows) > 2:
header_row = rows[2]
data_start = 3
elif sheet_name == "CHILD_SCHEMA":
# Row 0: title, Row 1: description, Row 2: headers
if len(rows) > 2:
header_row = rows[2]
data_start = 3
elif sheet_name == "ATT_TERMS":
# Row 0: title, Row 1: description, Row 2: headers
if len(rows) > 2:
header_row = rows[2]
data_start = 3
elif sheet_name == "SESSION_INDEX":
# Row 0: title, Row 1: description, Row 2: headers
if len(rows) > 2:
header_row = rows[2]
data_start = 3
else:
# Generic fallback: find row with typical column names
for i, row in enumerate(rows):
if row and any(isinstance(cell, str) and ('ID' in cell or 'NAME' in cell or 'CODE' in cell) for cell in row):
header_row = row
data_start = i + 1
break
if header_row is None:
print(f" Could not find headers in {sheet_name}")
return 0
# Clean column names
col_names = []
for cell in header_row:
col_name = clean_column_name(cell)
# Handle edge case where title row got mistaken as header
if col_name and 'uslap' in col_name and any(x in col_name for x in ['title', 'header', 'master']):
# This looks like a title, not a column name
col_names.append(clean_column_name(str(header_row.index(cell))))
else:
col_names.append(col_name)
# Create placeholders for SQL
placeholders = ', '.join(['?' for _ in col_names])
col_list = ', '.join(col_names)
# Prepare insert statement
insert_sql = f"INSERT OR REPLACE INTO {table_name} ({col_list}) VALUES ({placeholders})"
# Insert data rows
count = 0
for i in range(data_start, len(rows)):
row = rows[i]
# Skip empty rows or rows where first cell is None
if not row or row[0] is None:
continue
# Ensure row has same length as columns
row_data = list(row)
while len(row_data) < len(col_names):
row_data.append(None)
row_data = row_data[:len(col_names)]
try:
cursor.execute(insert_sql, row_data)
count += 1
except Exception as e:
print(f" Error inserting row {i} in {sheet_name}: {e}")
print(f" Row data: {row_data}")
conn.commit()
print(f" Imported {count} rows into {table_name}")
return count
def build_cross_reference(conn, cursor):
"""Build cross-reference table from CHILD_SCHEMA data."""
cursor.execute("DELETE FROM cross_reference")
# Get all child schema entries
cursor.execute("SELECT entry_id, parent_op, dp_codes FROM child_schema")
rows = cursor.fetchall()
count = 0
for entry_id, parent_op_str, dp_codes_str in rows:
if not entry_id or entry_id == 'ENTRY_ID':
continue
# Extract individual parent operations (e.g., "UMD-RL1 + UMD-ST1" -> ["UMD-RL1", "UMD-ST1"])
parent_ops = extract_parent_ops(parent_op_str)
# Extract individual DP codes
dp_codes = extract_dp_codes(dp_codes_str)
# Insert one row per parent_op per dp_code
for parent_op in parent_ops:
# Verify parent_op exists in umd_operations
cursor.execute("SELECT op_id FROM umd_operations WHERE op_id = ?", (parent_op,))
if not cursor.fetchone():
print(f" Warning: Parent operation {parent_op} not found in umd_operations for entry {entry_id}")
continue
for dp_code in dp_codes:
cursor.execute(
"INSERT INTO cross_reference (entry_id, parent_op, dp_ref) VALUES (?, ?, ?)",
(entry_id, parent_op, dp_code)
)
count += 1
conn.commit()
print(f" Built {count} cross-reference entries")
return count
def main():
excel_path = "USLaP_Final_Data_Consolidated_Master_v2.xlsx"
db_path = "uslap_database.db"
print(f"Opening Excel file: {excel_path}")
wb = openpyxl.load_workbook(excel_path, read_only=True, data_only=True)
print(f"Creating SQLite database: {db_path}")
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Enable foreign keys
cursor.execute("PRAGMA foreign_keys = ON")
# Create tables
create_tables(conn, cursor)
# Import data from each primary sheet
primary_sheets = [
("UMD_OPERATIONS", "umd_operations"),
("CHILD_SCHEMA", "child_schema"),
("DP_REGISTER", "dp_register"),
("ATT_TERMS", "att_terms"),
("PHONETIC_REVERSAL", "phonetic_reversal"),
("SESSION_INDEX", "session_index")
]
total_rows = 0
for excel_sheet, db_table in primary_sheets:
if excel_sheet in wb.sheetnames:
print(f"\nImporting {excel_sheet} -> {db_table}")
count = import_sheet_data(conn, cursor, wb, excel_sheet, db_table)
total_rows += count
else:
print(f"\nSheet {excel_sheet} not found in Excel file")
# Build cross-reference table
print("\nBuilding cross-reference table...")
cross_ref_count = build_cross_reference(conn, cursor)
# Create indexes for faster searching
print("\nCreating indexes...")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_child_schema_entry_id ON child_schema(entry_id)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_child_schema_parent_op ON child_schema(parent_op)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_cross_ref_entry_id ON cross_reference(entry_id)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_cross_ref_dp_ref ON cross_reference(dp_ref)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_umd_operations_op_id ON umd_operations(op_id)")
cursor.execute("CREATE INDEX IF NOT EXISTS idx_dp_register_dp_code ON dp_register(dp_code)")
conn.commit()
# Print statistics
print(f"\n=== Import Summary ===")
print(f"Total rows imported: {total_rows}")
print(f"Cross-reference entries: {cross_ref_count}")
# Verify data counts
for table in ["umd_operations", "child_schema", "dp_register", "att_terms", "phonetic_reversal", "session_index", "cross_reference"]:
cursor.execute(f"SELECT COUNT(*) FROM {table}")
count = cursor.fetchone()[0]
print(f"{table}: {count} rows")
wb.close()
conn.close()
print(f"\nDatabase created successfully: {db_path}")
if __name__ == "__main__":
main() |