Spaces:
Sleeping
Sleeping
Upload environment.py
Browse files- server/environment.py +120 -0
server/environment.py
CHANGED
|
@@ -322,10 +322,33 @@ class SQLQualityEnvironment:
|
|
| 322 |
# Find duplicate keys
|
| 323 |
cursor = conn.execute(f'PRAGMA table_info("{target_table}")')
|
| 324 |
columns = [row[1] for row in cursor.fetchall()]
|
|
|
|
| 325 |
|
| 326 |
if columns:
|
| 327 |
# Try to find duplicates on first column
|
| 328 |
key_col = columns[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
cursor = conn.execute(f'''
|
| 330 |
SELECT "{key_col}", COUNT(*) as cnt
|
| 331 |
FROM "{target_table}"
|
|
@@ -337,6 +360,103 @@ class SQLQualityEnvironment:
|
|
| 337 |
if result:
|
| 338 |
ground_truth["duplicate_key"] = result[0]
|
| 339 |
ground_truth["duplicate_count"] = result[1]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 340 |
|
| 341 |
except Exception as e:
|
| 342 |
# Ground truth analysis failed, but we can still run the task
|
|
|
|
| 322 |
# Find duplicate keys
|
| 323 |
cursor = conn.execute(f'PRAGMA table_info("{target_table}")')
|
| 324 |
columns = [row[1] for row in cursor.fetchall()]
|
| 325 |
+
ground_truth["all_columns"] = columns
|
| 326 |
|
| 327 |
if columns:
|
| 328 |
# Try to find duplicates on first column
|
| 329 |
key_col = columns[0]
|
| 330 |
+
ground_truth["column"] = key_col
|
| 331 |
+
ground_truth["offending_key"] = key_col
|
| 332 |
+
|
| 333 |
+
# Count total duplicates
|
| 334 |
+
cursor = conn.execute(f'''
|
| 335 |
+
SELECT COUNT(*) FROM (
|
| 336 |
+
SELECT "{key_col}"
|
| 337 |
+
FROM "{target_table}"
|
| 338 |
+
GROUP BY "{key_col}"
|
| 339 |
+
HAVING COUNT(*) > 1
|
| 340 |
+
)
|
| 341 |
+
''')
|
| 342 |
+
dup_groups = cursor.fetchone()[0]
|
| 343 |
+
|
| 344 |
+
# Count expected rows after dedup (unique keys)
|
| 345 |
+
cursor = conn.execute(f'''
|
| 346 |
+
SELECT COUNT(DISTINCT "{key_col}") FROM "{target_table}"
|
| 347 |
+
''')
|
| 348 |
+
expected_clean = cursor.fetchone()[0]
|
| 349 |
+
ground_truth["expected_clean_count"] = expected_clean
|
| 350 |
+
|
| 351 |
+
# Get first duplicate example
|
| 352 |
cursor = conn.execute(f'''
|
| 353 |
SELECT "{key_col}", COUNT(*) as cnt
|
| 354 |
FROM "{target_table}"
|
|
|
|
| 360 |
if result:
|
| 361 |
ground_truth["duplicate_key"] = result[0]
|
| 362 |
ground_truth["duplicate_count"] = result[1]
|
| 363 |
+
ground_truth["duplicate_groups"] = dup_groups
|
| 364 |
+
|
| 365 |
+
elif task_id == "type_repair" and target_table:
|
| 366 |
+
# Find columns that might have type issues (TEXT containing numbers)
|
| 367 |
+
cursor = conn.execute(f'PRAGMA table_info("{target_table}")')
|
| 368 |
+
columns = [row[1] for row in cursor.fetchall()]
|
| 369 |
+
ground_truth["all_columns"] = columns
|
| 370 |
+
|
| 371 |
+
# Find columns with numeric-like text values
|
| 372 |
+
for col in columns:
|
| 373 |
+
try:
|
| 374 |
+
# Check if column contains numeric-looking values stored as TEXT
|
| 375 |
+
cursor = conn.execute(f'''
|
| 376 |
+
SELECT "{col}" FROM "{target_table}"
|
| 377 |
+
WHERE "{col}" IS NOT NULL
|
| 378 |
+
LIMIT 100
|
| 379 |
+
''')
|
| 380 |
+
values = [row[0] for row in cursor.fetchall()]
|
| 381 |
+
|
| 382 |
+
# Check if majority look like numbers but stored as text
|
| 383 |
+
numeric_like = 0
|
| 384 |
+
for v in values:
|
| 385 |
+
if v and str(v).strip():
|
| 386 |
+
try:
|
| 387 |
+
float(str(v).replace(',', '').replace('$', ''))
|
| 388 |
+
numeric_like += 1
|
| 389 |
+
except (ValueError, TypeError):
|
| 390 |
+
pass
|
| 391 |
+
|
| 392 |
+
if numeric_like > len(values) * 0.5:
|
| 393 |
+
ground_truth["column"] = col
|
| 394 |
+
ground_truth["target_column"] = col
|
| 395 |
+
ground_truth["issue_type"] = "TEXT_STORED_NUMERIC"
|
| 396 |
+
break
|
| 397 |
+
except Exception:
|
| 398 |
+
continue
|
| 399 |
+
|
| 400 |
+
# If no type issue found, default to first column
|
| 401 |
+
if "column" not in ground_truth and columns:
|
| 402 |
+
ground_truth["column"] = columns[0]
|
| 403 |
+
ground_truth["target_column"] = columns[0]
|
| 404 |
+
|
| 405 |
+
elif task_id == "fk_integrity" and target_table:
|
| 406 |
+
# For FK integrity, we need to simulate orphan records
|
| 407 |
+
# Since uploaded data may not have parent/child tables,
|
| 408 |
+
# we'll create a virtual parent table based on existing data
|
| 409 |
+
cursor = conn.execute(f'PRAGMA table_info("{target_table}")')
|
| 410 |
+
columns = [row[1] for row in cursor.fetchall()]
|
| 411 |
+
ground_truth["all_columns"] = columns
|
| 412 |
+
|
| 413 |
+
# Look for columns that might be foreign keys (ending in _id)
|
| 414 |
+
fk_candidates = [c for c in columns if c.lower().endswith('_id') and c.lower() != 'id']
|
| 415 |
+
|
| 416 |
+
if fk_candidates:
|
| 417 |
+
fk_col = fk_candidates[0]
|
| 418 |
+
ground_truth["column"] = fk_col
|
| 419 |
+
|
| 420 |
+
# Find values that would be orphans (using modulo to simulate missing parents)
|
| 421 |
+
cursor = conn.execute(f'''
|
| 422 |
+
SELECT DISTINCT "{fk_col}" FROM "{target_table}"
|
| 423 |
+
WHERE "{fk_col}" IS NOT NULL
|
| 424 |
+
''')
|
| 425 |
+
all_ids = [row[0] for row in cursor.fetchall()]
|
| 426 |
+
|
| 427 |
+
if all_ids:
|
| 428 |
+
# Mark some IDs as invalid to simulate orphans
|
| 429 |
+
import random
|
| 430 |
+
random.seed(42) # Deterministic for testing
|
| 431 |
+
valid_ids = all_ids[:max(1, len(all_ids) * 2 // 3)]
|
| 432 |
+
invalid_ids = [v for v in all_ids if v not in valid_ids]
|
| 433 |
+
|
| 434 |
+
ground_truth["parent_table"] = f"{target_table}_parent"
|
| 435 |
+
ground_truth["parent_key"] = fk_col.replace('_id', '')
|
| 436 |
+
ground_truth["valid_invoice_ids"] = valid_ids
|
| 437 |
+
ground_truth["expected_clean_count"] = len([v for v in all_ids if v in valid_ids])
|
| 438 |
+
ground_truth["orphan_count"] = len(invalid_ids)
|
| 439 |
+
|
| 440 |
+
elif task_id == "schema_drift" and target_table:
|
| 441 |
+
# Schema drift compares two versions of same table
|
| 442 |
+
# For uploaded data, we'll treat it as canonical and create expected schema
|
| 443 |
+
cursor = conn.execute(f'PRAGMA table_info("{target_table}")')
|
| 444 |
+
columns_info = cursor.fetchall()
|
| 445 |
+
columns = [row[1] for row in columns_info]
|
| 446 |
+
|
| 447 |
+
ground_truth["table"] = target_table
|
| 448 |
+
ground_truth["canonical_table"] = target_table
|
| 449 |
+
ground_truth["expected_columns"] = columns
|
| 450 |
+
|
| 451 |
+
# Get row count
|
| 452 |
+
cursor = conn.execute(f'SELECT COUNT(*) FROM "{target_table}"')
|
| 453 |
+
ground_truth["expected_row_count"] = cursor.fetchone()[0]
|
| 454 |
+
|
| 455 |
+
# Get distinct IDs if available
|
| 456 |
+
if 'id' in [c.lower() for c in columns]:
|
| 457 |
+
id_col = next(c for c in columns if c.lower() == 'id')
|
| 458 |
+
cursor = conn.execute(f'SELECT COUNT(DISTINCT "{id_col}") FROM "{target_table}"')
|
| 459 |
+
ground_truth["expected_distinct_ids"] = cursor.fetchone()[0]
|
| 460 |
|
| 461 |
except Exception as e:
|
| 462 |
# Ground truth analysis failed, but we can still run the task
|