1 line
1.2 MiB
Plaintext
1 line
1.2 MiB
Plaintext
{"repo": "2020dfff/rbac-text2sql-benchmark", "files": {"notebooks/bird_column_level_rbac.ipynb": {"54d344296bbc44b61df84eebf4dd55021ecc3198": {"t": 1785745476.6476693, "n": 162988, "h": "a254ef89eada166d", "c": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"id\": \"c4bddd25\",\n \"metadata\": {},\n \"source\": [\n \"## 0. Import and Setup\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"93292a4c\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Setup and imports\\n\",\n \"import sys\\n\",\n \"import json\\n\",\n \"import sqlite3\\n\",\n \"from pathlib import Path\\n\",\n \"from pprint import pprint\\n\",\n \"\\n\",\n \"# Add project root to path\\n\",\n \"project_root = Path.cwd().parent\\n\",\n \"sys.path.insert(0, str(project_root))\\n\",\n \"\\n\",\n \"print(f\\\"Project root: {project_root}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 2,\n \"id\": \"bce399e4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Available API keys:\\n\",\n \" OPENAI_API_KEY: ✓\\n\",\n \" DEEPSEEK_API_KEY: ✓\\n\",\n \" DEEPINFRA_API_KEY: ✓\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Load environment and initialize Oracle\\n\",\n \"from dotenv import load_dotenv\\n\",\n \"import os\\n\",\n \"\\n\",\n \"load_dotenv(project_root / '.env')\\n\",\n \"\\n\",\n \"# Check available API keys\\n\",\n \"api_keys = {\\n\",\n \" 'OPENAI_API_KEY': bool(os.getenv('OPENAI_API_KEY')),\\n\",\n \" 'DEEPSEEK_API_KEY': bool(os.getenv('DEEPSEEK_API_KEY')),\\n\",\n \" 'DEEPINFRA_API_KEY': bool(os.getenv('DEEPINFRA_API_KEY')),\\n\",\n \"}\\n\",\n \"print(\\\"Available API keys:\\\")\\n\",\n \"for k, v in api_keys.items():\\n\",\n \" print(f\\\" {k}: {'✓' if v else '✗'}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"a4d69b7c\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Initialize LLM Oracle\\n\",\n \"from src.llm_oracle import Oracle\\n\",\n \"\\n\",\n \"# Choose model based on available keys (prefer DeepSeek for cost efficiency)\\n\",\n \"if os.getenv('DEEPSEEK_API_KEY'):\\n\",\n \" MODEL = \\\"deepseek-chat\\\"\\n\",\n \" API_KEY = os.getenv('DEEPSEEK_API_KEY')\\n\",\n \"elif os.getenv('OPENAI_API_KEY'):\\n\",\n \" MODEL = \\\"gpt-4o-mini\\\"\\n\",\n \" API_KEY = os.getenv('OPENAI_API_KEY')\\n\",\n \"else:\\n\",\n \" raise ValueError(\\\"No API key available!\\\")\\n\",\n \"\\n\",\n \"oracle = Oracle(model=MODEL, apikey=API_KEY)\\n\",\n \"print(f\\\"Using model: {MODEL}\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8809cf55\",\n \"metadata\": {},\n \"source\": [\n \"## 1. Load Sample Database Schema\\n\",\n \"\\n\",\n \"We'll use a BIRD database with interesting column structure for testing.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 4,\n \"id\": \"e9dcc61c\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Import shared policy utility functions\\n\",\n \"from src.role_evaluator.quality_metrics import RoleQualityEvaluator\\n\",\n \"\\n\",\n \"# Convenience aliases for the static methods\\n\",\n \"count_policy_columns = RoleQualityEvaluator.count_policy_columns\\n\",\n \"has_wildcard_policy = RoleQualityEvaluator.has_wildcard_policy\\n\",\n \"\\n\",\n \"def get_schema_from_sqlite(db_path: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Extract detailed schema from SQLite database.\\\"\\\"\\\"\\n\",\n \" conn = sqlite3.connect(db_path)\\n\",\n \" cursor = conn.cursor()\\n\",\n \" \\n\",\n \" # Get all tables\\n\",\n \" cursor.execute(\\\"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\\\")\\n\",\n \" tables = [row[0] for row in cursor.fetchall()]\\n\",\n \" \\n\",\n \" schema = {}\\n\",\n \" for table in tables:\\n\",\n \" cursor.execute(f\\\"PRAGMA table_info(`{table}`)\\\")\\n\",\n \" columns = cursor.fetchall()\\n\",\n \" \\n\",\n \" cursor.execute(f\\\"PRAGMA foreign_key_list(`{table}`)\\\")\\n\",\n \" fks = {row[3]: f\\\"{row[2]}.{row[4]}\\\" for row in cursor.fetchall()}\\n\",\n \" \\n\",\n \" schema[table] = {\\n\",\n \" 'columns': [\\n\",\n \" {\\n\",\n \" 'name': col[1],\\n\",\n \" 'type': col[2] or 'TEXT',\\n\",\n \" 'pk': col[5] == 1,\\n\",\n \" 'not_null': col[3] == 1,\\n\",\n \" 'fk': fks.get(col[1])\\n\",\n \" }\\n\",\n \" for col in columns\\n\",\n \" ]\\n\",\n \" }\\n\",\n \" \\n\",\n \" conn.close()\\n\",\n \" return schema\\n\",\n \"\\n\",\n \"def format_schema_for_prompt(schema: dict, db_name: str) -> str:\\n\",\n \" \\\"\\\"\\\"Format schema as text for LLM prompt.\\\"\\\"\\\"\\n\",\n \" lines = [f\\\"Database: {db_name}\\\\n\\\"]\\n\",\n \" \\n\",\n \" for table, info in schema.items():\\n\",\n \" lines.append(f\\\"Table: {table}\\\")\\n\",\n \" lines.append(\\\"Columns:\\\")\\n\",\n \" for col in info['columns']:\\n\",\n \" constraints = []\\n\",\n \" if col['pk']:\\n\",\n \" constraints.append(\\\"PRIMARY KEY\\\")\\n\",\n \" if col['not_null'] and not col['pk']:\\n\",\n \" constraints.append(\\\"NOT NULL\\\")\\n\",\n \" if col['fk']:\\n\",\n \" constraints.append(f\\\"FK -> {col['fk']}\\\")\\n\",\n \" \\n\",\n \" constraint_str = f\\\" [{', '.join(constraints)}]\\\" if constraints else \\\"\\\"\\n\",\n \" lines.append(f\\\" - {col['name']} ({col['type']}){constraint_str}\\\")\\n\",\n \" lines.append(\\\"\\\")\\n\",\n \" \\n\",\n \" return \\\"\\\\n\\\".join(lines)\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 5,\n \"id\": \"705d64b1\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Available BIRD databases (11):\\n\",\n \" - california_schools\\n\",\n \" - card_games\\n\",\n \" - codebase_community\\n\",\n \" - debit_card_specializing\\n\",\n \" - european_football_2\\n\",\n \" - financial\\n\",\n \" - formula_1\\n\",\n \" - student_club\\n\",\n \" - superhero\\n\",\n \" - thrombosis_prediction\\n\",\n \" ...\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Load a sample BIRD database - use the latest 2025 version\\n\",\n \"bird_db_dir = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases'\\n\",\n \"\\n\",\n \"# List available databases\\n\",\n \"available_dbs = [d.name for d in bird_db_dir.iterdir() if d.is_dir()]\\n\",\n \"print(f\\\"Available BIRD databases ({len(available_dbs)}):\\\")\\n\",\n \"for db in sorted(available_dbs)[:10]:\\n\",\n \" print(f\\\" - {db}\\\")\\n\",\n \"print(\\\" ...\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"f9e1a86a\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Select a database with moderate complexity\\n\",\n \"# 'california_schools' has tables with sensitive-looking columns\\n\",\n \"TEST_DB = \\\"california_schools\\\"\\n\",\n \"db_path = bird_db_dir / TEST_DB / f\\\"{TEST_DB}.sqlite\\\"\\n\",\n \"\\n\",\n \"print(f\\\"Loading schema from: {db_path}\\\")\\n\",\n \"schema = get_schema_from_sqlite(str(db_path))\\n\",\n \"\\n\",\n \"print(f\\\"\\\\nTables: {list(schema.keys())}\\\")\\n\",\n \"for table, info in schema.items():\\n\",\n \" print(f\\\"\\\\n{table}: {len(info['columns'])} columns\\\")\\n\",\n \" for col in info['columns'][:5]:\\n\",\n \" print(f\\\" - {col['name']} ({col['type']})\\\")\\n\",\n \" if len(info['columns']) > 5:\\n\",\n \" print(f\\\" ... and {len(info['columns']) - 5} more\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 7,\n \"id\": \"e5fb0114\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Database: california_schools\\n\",\n \"\\n\",\n \"Table: frpm\\n\",\n \"Columns:\\n\",\n \" - CDSCode (TEXT) [PRIMARY KEY, FK -> schools.CDSCode]\\n\",\n \" - Academic Year (TEXT)\\n\",\n \" - County Code (TEXT)\\n\",\n \" - District Code (INTEGER)\\n\",\n \" - School Code (TEXT)\\n\",\n \" - County Name (TEXT)\\n\",\n \" - District Name (TEXT)\\n\",\n \" - School Name (TEXT)\\n\",\n \" - District Type (TEXT)\\n\",\n \" - School Type (TEXT)\\n\",\n \" - Educational Option Type (TEXT)\\n\",\n \" - NSLP Provision Status (TEXT)\\n\",\n \" - Charter School (Y/N) (INTEGER)\\n\",\n \" - Charter School Number (TEXT)\\n\",\n \" - Charter Funding Type (TEXT)\\n\",\n \" - IRC (INTEGER)\\n\",\n \" - Low Grade (TEXT)\\n\",\n \" - High Grade (TEXT)\\n\",\n \" - Enrollment (K-12) (REAL)\\n\",\n \" - Free Meal Count (K-12) (REAL)\\n\",\n \" - Percent (%) Eligible Free (K-12) (REAL)\\n\",\n \" - FRPM Count (K-12) (REAL)\\n\",\n \" - Percent (%) Eligible FRPM (K-12) (REAL)\\n\",\n \" - Enrollment (Ages 5-17) (REAL)\\n\",\n \" - Free Meal Count (Ages 5-17) (REAL)\\n\",\n \" - Percent (%) Eligible Free (Ages 5-17) (REAL)\\n\",\n \" - FRPM Count (Ages 5-17) (REAL)\\n\",\n \" - Percent (%) Eligible FRPM (Ages 5-17) (REAL)\\n\",\n \" - 2013-14 CALPADS Fall 1 Certification Status (INTEGER)\\n\",\n \"\\n\",\n \"Table: satscores\\n\",\n \"Columns:\\n\",\n \" - cds (TEXT) [PRIMARY KEY, FK -> schools.CDSCode]\\n\",\n \" - rtype (TEXT) [NOT NULL]\\n\",\n \" - sname (TEXT)\\n\",\n \" - dname (TEXT)\\n\",\n \" - cname (TEXT)\\n\",\n \" - enroll12 (INTEGER) [NOT NULL]\\n\",\n \" - NumTstTakr (INTEGER) [NOT NULL]\\n\",\n \" - AvgScrRead (INTEGER)\\n\",\n \" - AvgScrMath (INTEGER)\\n\",\n \" - AvgScrWrite (INTEGER)\\n\",\n \" - NumGE1500 (INTEGER)\\n\",\n \"\\n\",\n \"Table: schools\\n\",\n \"Columns:\\n\",\n \" - CDSCode (TEXT) [PRIMARY KEY]\\n\",\n \" - NCESDist (TEXT)\\n\",\n \" - NCESSchool (TEXT)\\n\",\n \" - StatusType (TEXT) [NOT NULL]\\n\",\n \" - County (TEXT) [NOT NULL]\\n\",\n \" - District (TEXT) [NOT NULL]\\n\",\n \" - School (TEXT)\\n\",\n \" - Street (TEXT)\\n\",\n \" - StreetAbr (TEXT)\\n\",\n \" - City (TEXT)\\n\",\n \" - Zip (TEXT)\\n\",\n \" - State (TEXT)\\n\",\n \" - MailStreet (TEXT)\\n\",\n \" - MailStrAbr (TEXT)\\n\",\n \" - MailCity (TEXT)\\n\",\n \" - MailZip (TEXT)\\n\",\n \" - MailState (TEXT)\\n\",\n \" - Phone (TEXT)\\n\",\n \" - Ext (TEXT)\\n\",\n \" - Website (TEXT)\\n\",\n \" - OpenDate (DATE)\\n\",\n \" - ClosedDate (DATE)\\n\",\n \" - Charter (INTEGER)\\n\",\n \" - CharterNum (TEXT)\\n\",\n \" - FundingType (TEXT)\\n\",\n \" - DOC (TEXT) [NOT NULL]\\n\",\n \" - DOCType (TEXT) [NOT NULL]\\n\",\n \" - SOC (TEXT)\\n\",\n \" - SOCType (TEXT)\\n\",\n \" - EdOpsCode (TEXT)\\n\",\n \" \\n\",\n \"\\n\",\n \"... (total 2401 characters)\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Format schema for prompt\\n\",\n \"schema_text = format_schema_for_prompt(schema, TEST_DB)\\n\",\n \"print(schema_text[:2000])\\n\",\n \"print(f\\\"\\\\n... (total {len(schema_text)} characters)\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8e904509\",\n \"metadata\": {},\n \"source\": [\n \"## 2. Load Prompts from External Modules\\n\",\n \"\\n\",\n \"Prompts are now defined in `configs/column_level_prompts.py`.\\n\",\n \"- **JSON format** is used (more structured, requires max_tokens=4000)\\n\",\n \"- Parser auto-detects format and handles both JSON and Plain Text\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 8,\n \"id\": \"1a425645\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"✓ Loaded from external modules:\\n\",\n \" - COLUMN_LEVEL_SYSTEM_PROMPT (2734 chars)\\n\",\n \" - COLUMN_LEVEL_USER_PROMPT_TEMPLATE\\n\",\n \" - DEFAULT_MAX_TOKENS = 4000\\n\",\n \" - parse_column_level_roles (auto-detects JSON/Text)\\n\",\n \" - validate_roles_against_schema\\n\",\n \"\\n\",\n \"--- System Prompt Preview ---\\n\",\n \"You are a database security expert specializing in fine-grained Role-Based Access Control (RBAC).\\n\",\n \"Your job is to read a relational database schema and propose a COHERENT set of roles with COLUMN-LEVEL permissions.\\n\",\n \"\\n\",\n \"Key goals:\\n\",\n \"- Minimize role count while covering real-world workflows (decide number of roles according to real world scenario).\\n\",\n \"- Each role must be clearly related to others (same scenario, complementary duties).\\n\",\n \"- Enforce least-privilege at the COLUMN level (specify which columns eac\\n\",\n \"...\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Reload modules to get latest changes\\n\",\n \"import importlib\\n\",\n \"import configs.column_level_prompts\\n\",\n \"import src.role_parser.column_level_parser\\n\",\n \"importlib.reload(configs.column_level_prompts)\\n\",\n \"importlib.reload(src.role_parser.column_level_parser)\\n\",\n \"\\n\",\n \"# Import prompts and parser from external modules\\n\",\n \"from configs.column_level_prompts import (\\n\",\n \" COLUMN_LEVEL_SYSTEM_PROMPT, \\n\",\n \" COLUMN_LEVEL_USER_PROMPT_TEMPLATE,\\n\",\n \" DEFAULT_MAX_TOKENS\\n\",\n \")\\n\",\n \"from src.role_parser import parse_column_level_roles, validate_roles_against_schema\\n\",\n \"\\n\",\n \"print(\\\"✓ Loaded from external modules:\\\")\\n\",\n \"print(f\\\" - COLUMN_LEVEL_SYSTEM_PROMPT ({len(COLUMN_LEVEL_SYSTEM_PROMPT)} chars)\\\")\\n\",\n \"print(f\\\" - COLUMN_LEVEL_USER_PROMPT_TEMPLATE\\\")\\n\",\n \"print(f\\\" - DEFAULT_MAX_TOKENS = {DEFAULT_MAX_TOKENS}\\\")\\n\",\n \"print(f\\\" - parse_column_level_roles (auto-detects JSON/Text)\\\")\\n\",\n \"print(f\\\" - validate_roles_against_schema\\\")\\n\",\n \"\\n\",\n \"print(\\\"\\\\n--- System Prompt Preview ---\\\")\\n\",\n \"print(COLUMN_LEVEL_SYSTEM_PROMPT[:500])\\n\",\n \"print(\\\"...\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"0acacb6e\",\n \"metadata\": {},\n \"source\": [\n \"## [Archived] Single-Sample Experiments\\n\",\n \"\\n\",\n \"The following code block contains single sample test code for debugging prompt and parser.\\n\",\n \"**Commented out. For production, skip to Section 8 for batch processing.**\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 9,\n \"id\": \"e9700ae1\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"⏭️ This cell is commented out. Skip to Section 8 for batch processing.\\n\"\n ]\n }\n ],\n \"source\": [\n \"# ============================================================\\n\",\n \"# [ARCHIVED] Single sample experiment code - commented out, skip this cell\\n\",\n \"# ============================================================\\n\",\n \"# The following code is for testing and debugging a single database\\n\",\n \"# For production, skip to Section 8 for batch processing\\n\",\n \"\\n\",\n \"'''\\n\",\n \"# --- 3. Generate Column-Level Roles (Single DB) ---\\n\",\n \"print(\\\"Generating column-level roles...\\\")\\n\",\n \"user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=schema_text)\\n\",\n \"response = oracle.query(\\n\",\n \" prompt_sys=COLUMN_LEVEL_SYSTEM_PROMPT,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3, top_p=0.9,\\n\",\n \" max_completion_tokens=DEFAULT_MAX_TOKENS\\n\",\n \")\\n\",\n \"llm_response = response['answer']\\n\",\n \"print(f\\\"Response length: {len(llm_response)} chars\\\")\\n\",\n \"\\n\",\n \"# --- 4. Parse and Validate ---\\n\",\n \"from src.role_parser.column_level_parser import parse_column_level_roles\\n\",\n \"parsed_roles = parse_column_level_roles(llm_response)\\n\",\n \"print(f\\\"Parsed {len(parsed_roles)} roles\\\")\\n\",\n \"\\n\",\n \"validation = validate_roles_against_schema(parsed_roles, str(db_path))\\n\",\n \"print(f\\\"Valid: {validation['valid_roles']}/{validation['total_roles']}\\\")\\n\",\n \"\\n\",\n \"# --- 5. Show Results ---\\n\",\n \"for role in parsed_roles:\\n\",\n \" print(f\\\" - {role['role']}: {len(role.get('columns', {}))} tables\\\")\\n\",\n \"\\n\",\n \"# --- 6. Compare with Table-Level ---\\n\",\n \"from configs.prompts import SYSTEM_PROMPT as TABLE_LEVEL_PROMPT, USER_PROMPT_TEMPLATE\\n\",\n \"table_level_response = oracle.query(\\n\",\n \" prompt_sys=TABLE_LEVEL_PROMPT,\\n\",\n \" prompt_user=USER_PROMPT_TEMPLATE.format(schema_content=schema_text),\\n\",\n \" temp=0.3, top_p=0.9\\n\",\n \")\\n\",\n \"print(\\\"Table-Level Response:\\\", table_level_response['answer'][:500])\\n\",\n \"\\n\",\n \"# --- 7. Save Single DB Result ---\\n\",\n \"def convert_to_assignment_format(parsed_roles, db_name):\\n\",\n \" return {\\\"assignments\\\": {db_name: [\\n\",\n \" {\\\"role\\\": r[\\\"role\\\"], \\\"description\\\": r.get(\\\"description\\\", \\\"\\\"), \\\"policy\\\": r.get(\\\"columns\\\", {})}\\n\",\n \" for r in parsed_roles\\n\",\n \" ]}}\\n\",\n \"\\n\",\n \"column_level_assignments = convert_to_assignment_format(parsed_roles, TEST_DB)\\n\",\n \"output_file = project_root / 'outputs' / 'column_level_role_assignments.json'\\n\",\n \"with open(output_file, 'w') as f:\\n\",\n \" json.dump(column_level_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \"print(f\\\"Saved to: {output_file}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ This cell is commented out. Skip to Section 8 for batch processing.\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8bc8cb95\",\n \"metadata\": {},\n \"source\": [\n \"## 3. Batch Process All BIRD Databases\\n\",\n \"\\n\",\n \"Generate column-level role assignments for all BIRD databases with parallel processing.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 10,\n \"id\": \"fe3f2e81\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Configuration:\\n\",\n \" Workers: 40\\n\",\n \" Databases: 11\\n\",\n \" Max tokens: 4000\\n\",\n \" Model: deepseek-chat\\n\",\n \"\\n\",\n \"Databases to process:\\n\",\n \" - superhero\\n\",\n \" - thrombosis_prediction\\n\",\n \" - student_club\\n\",\n \" - toxicology\\n\",\n \" - california_schools\\n\",\n \" - debit_card_specializing\\n\",\n \" - card_games\\n\",\n \" - codebase_community\\n\",\n \" - formula_1\\n\",\n \" - european_football_2\\n\",\n \" - financial\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Batch processing configuration\\n\",\n \"from concurrent.futures import ThreadPoolExecutor, as_completed\\n\",\n \"from tqdm.notebook import tqdm\\n\",\n \"\\n\",\n \"# Configuration\\n\",\n \"WORKERS = 40\\n\",\n \"SAMPLES = 100 # 0 for all databases\\n\",\n \"MAX_TOKENS = DEFAULT_MAX_TOKENS\\n\",\n \"\\n\",\n \"# Get all BIRD databases\\n\",\n \"all_dbs = [d.name for d in bird_db_dir.iterdir() if d.is_dir()]\\n\",\n \"if SAMPLES > 0:\\n\",\n \" all_dbs = all_dbs[:SAMPLES]\\n\",\n \"\\n\",\n \"print(f\\\"Configuration:\\\")\\n\",\n \"print(f\\\" Workers: {WORKERS}\\\")\\n\",\n \"print(f\\\" Databases: {len(all_dbs)}\\\")\\n\",\n \"print(f\\\" Max tokens: {MAX_TOKENS}\\\")\\n\",\n \"print(f\\\" Model: {MODEL}\\\")\\n\",\n \"print(f\\\"\\\\nDatabases to process:\\\")\\n\",\n \"for db in all_dbs:\\n\",\n \" print(f\\\" - {db}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 11,\n \"id\": \"7b5edd94\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"✓ Helper functions defined\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Helper functions for batch processing\\n\",\n \"\\n\",\n \"def process_single_database(db_name: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Process a single database and return role assignments.\\\"\\\"\\\"\\n\",\n \" db_path = bird_db_dir / db_name / f\\\"{db_name}.sqlite\\\"\\n\",\n \" \\n\",\n \" if not db_path.exists():\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": f\\\"Database not found\\\"}\\n\",\n \" \\n\",\n \" try:\\n\",\n \" # Extract schema\\n\",\n \" db_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" db_schema_text = format_schema_for_prompt(db_schema, db_name)\\n\",\n \" \\n\",\n \" # Generate roles via LLM\\n\",\n \" user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=db_schema_text)\\n\",\n \" \\n\",\n \" response = oracle.query(\\n\",\n \" prompt_sys=COLUMN_LEVEL_SYSTEM_PROMPT,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3,\\n\",\n \" top_p=0.9,\\n\",\n \" max_completion_tokens=MAX_TOKENS\\n\",\n \" )\\n\",\n \" \\n\",\n \" llm_response = response['answer']\\n\",\n \" usage = response.get('usage', {})\\n\",\n \" \\n\",\n \" # Parse response\\n\",\n \" roles = parse_column_level_roles(llm_response)\\n\",\n \" \\n\",\n \" if not roles:\\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"parse_error\\\",\\n\",\n \" \\\"error\\\": \\\"Failed to parse LLM response\\\",\\n\",\n \" \\\"raw_response\\\": llm_response[:300]\\n\",\n \" }\\n\",\n \" \\n\",\n \" # Validate against schema\\n\",\n \" validation = validate_roles_against_schema(roles, str(db_path))\\n\",\n \" \\n\",\n \" # Convert to target format\\n\",\n \" assignments = []\\n\",\n \" for role in roles:\\n\",\n \" assignments.append({\\n\",\n \" \\\"role\\\": role[\\\"role\\\"],\\n\",\n \" \\\"description\\\": role.get(\\\"description\\\", \\\"\\\"),\\n\",\n \" \\\"policy\\\": role.get(\\\"columns\\\", {})\\n\",\n \" })\\n\",\n \" \\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"success\\\",\\n\",\n \" \\\"roles_count\\\": len(assignments),\\n\",\n \" \\\"valid_roles\\\": validation['valid_roles'],\\n\",\n \" \\\"all_valid\\\": validation['all_valid'],\\n\",\n \" \\\"assignments\\\": assignments,\\n\",\n \" \\\"usage\\\": usage\\n\",\n \" }\\n\",\n \" \\n\",\n \" except Exception as e:\\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"error\\\",\\n\",\n \" \\\"error\\\": str(e)\\n\",\n \" }\\n\",\n \"\\n\",\n \"print(\\\"✓ Helper functions defined\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 12,\n \"id\": \"61e61795\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Starting batch processing with 40 workers...\\n\",\n \"============================================================\\n\",\n \"[1/11] ✓ toxicology: 4 roles (4 valid)\\n\",\n \"[2/11] ✓ debit_card_specializing: 4 roles (4 valid)\\n\",\n \"[3/11] ✓ thrombosis_prediction: 4 roles (4 valid)\\n\",\n \"[4/11] ✓ student_club: 5 roles (5 valid)\\n\",\n \"[5/11] ✓ superhero: 5 roles (5 valid)\\n\",\n \"[6/11] ✓ financial: 5 roles (5 valid)\\n\",\n \"[7/11] ✓ card_games: 5 roles (5 valid)\\n\",\n \"[8/11] ✓ codebase_community: 4 roles (4 valid)\\n\",\n \"[9/11] ✓ california_schools: 5 roles (5 valid)\\n\",\n \"[10/11] ✓ formula_1: 5 roles (5 valid)\\n\",\n \"[11/11] ✓ european_football_2: 5 roles (5 valid)\\n\",\n \"============================================================\\n\",\n \"Completed: 11/11 success, 0 errors\\n\",\n \"Total tokens: 25,679\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Execute batch processing with parallel workers\\n\",\n \"print(f\\\"Starting batch processing with {WORKERS} workers...\\\")\\n\",\n \"print(\\\"=\\\" * 60)\\n\",\n \"\\n\",\n \"results = {\\n\",\n \" \\\"metadata\\\": {\\n\",\n \" \\\"model\\\": MODEL,\\n\",\n \" \\\"max_tokens\\\": MAX_TOKENS,\\n\",\n \" \\\"total_databases\\\": len(all_dbs)\\n\",\n \" },\\n\",\n \" \\\"assignments\\\": {},\\n\",\n \" \\\"errors\\\": []\\n\",\n \"}\\n\",\n \"\\n\",\n \"success_count = 0\\n\",\n \"error_count = 0\\n\",\n \"total_tokens = {\\\"prompt\\\": 0, \\\"completion\\\": 0}\\n\",\n \"\\n\",\n \"with ThreadPoolExecutor(max_workers=WORKERS) as executor:\\n\",\n \" futures = {executor.submit(process_single_database, db): db for db in all_dbs}\\n\",\n \" \\n\",\n \" for i, future in enumerate(as_completed(futures), 1):\\n\",\n \" db_name = futures[future]\\n\",\n \" try:\\n\",\n \" result = future.result()\\n\",\n \" \\n\",\n \" if result[\\\"status\\\"] == \\\"success\\\":\\n\",\n \" results[\\\"assignments\\\"][result[\\\"database\\\"]] = result[\\\"assignments\\\"]\\n\",\n \" success_count += 1\\n\",\n \" \\n\",\n \" usage = result.get(\\\"usage\\\", {})\\n\",\n \" total_tokens[\\\"prompt\\\"] += usage.get(\\\"prompt_tokens\\\", 0)\\n\",\n \" total_tokens[\\\"completion\\\"] += usage.get(\\\"completion_tokens\\\", 0)\\n\",\n \" \\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✓ {db_name}: {result['roles_count']} roles ({result['valid_roles']} valid)\\\")\\n\",\n \" else:\\n\",\n \" results[\\\"errors\\\"].append(result)\\n\",\n \" error_count += 1\\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✗ {db_name}: {result.get('error', result['status'])}\\\")\\n\",\n \" \\n\",\n \" except Exception as e:\\n\",\n \" error_count += 1\\n\",\n \" results[\\\"errors\\\"].append({\\\"database\\\": db_name, \\\"error\\\": str(e)})\\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✗ {db_name}: {e}\\\")\\n\",\n \"\\n\",\n \"results[\\\"metadata\\\"][\\\"success_count\\\"] = success_count\\n\",\n \"results[\\\"metadata\\\"][\\\"error_count\\\"] = error_count\\n\",\n \"results[\\\"metadata\\\"][\\\"total_tokens\\\"] = total_tokens\\n\",\n \"\\n\",\n \"print(\\\"=\\\" * 60)\\n\",\n \"print(f\\\"Completed: {success_count}/{len(all_dbs)} success, {error_count} errors\\\")\\n\",\n \"print(f\\\"Total tokens: {total_tokens['prompt'] + total_tokens['completion']:,}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"39d38aa3\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Save batch results to file\\n\",\n \"output_file = project_root / 'outputs' / 'column_level_role_assignments_bird.json'\\n\",\n \"\\n\",\n \"with open(output_file, 'w', encoding='utf-8') as f:\\n\",\n \" json.dump(results, f, indent=2, ensure_ascii=False)\\n\",\n \"\\n\",\n \"print(f\\\"✓ Results saved to: {output_file}\\\")\\n\",\n \"print(f\\\"\\\\nFile structure:\\\")\\n\",\n \"print(f\\\" - metadata: model, tokens, counts\\\")\\n\",\n \"print(f\\\" - assignments: {len(results['assignments'])} databases\\\")\\n\",\n \"print(f\\\" - errors: {len(results['errors'])} entries\\\")\\n\",\n \"\\n\",\n \"# Show sample\\n\",\n \"if results['assignments']:\\n\",\n \" sample_db = list(results['assignments'].keys())[0]\\n\",\n \" print(f\\\"\\\\nSample ({sample_db}):\\\")\\n\",\n \" print(json.dumps(results['assignments'][sample_db][:1], indent=2, ensure_ascii=False)[:500])\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"9595c46d\",\n \"metadata\": {},\n \"source\": [\n \"## 4. Generate Column-Level RBAC Dataset\\n\",\n \"\\n\",\n \"Based on the generated role policies, combined with BIRD dataset queries, generate the final RBAC training/evaluation dataset.\\n\",\n \"\\n\",\n \"Dataset format (consistent with table-level):\\n\",\n \"- `db_id`: Database name\\n\",\n \"- `instruction`: Full instruction with schema + evidence + role policy\\n\",\n \"- `role`: Role name\\n\",\n \"- `policy`: Column-level permission policy {table: [columns]}\\n\",\n \"- `input`: User question\\n\",\n \"- `output`: SQL or denial message\\n\",\n \"- `difficulty`: Query difficulty level\\n\",\n \"- `metadata`: Contains gold_sql, permission, query_columns, missing_columns, etc.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"405783fa\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# 4.1 Import generator and test dependencies\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator, format_policy_text\\n\",\n \"from src.processors.column_extractor import extract_query_columns\\n\",\n \"from src.processors.column_permission_checker import check_column_permission\\n\",\n \"import glob\\n\",\n \"\\n\",\n \"# Configure paths - use 2025 version (dev_20251106) for latest BIRD data\\n\",\n \"ROLE_ASSIGNMENTS_PATH = project_root / 'outputs' / 'column_level_role_assignments_bird.json'\\n\",\n \"BIRD_DATA_PATH = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev.json'\\n\",\n \"BIRD_DB_ROOT = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases'\\n\",\n \"\\n\",\n \"print(\\\"=== Dataset Generation Config ===\\\")\\n\",\n \"print(f\\\"Role assignments: {ROLE_ASSIGNMENTS_PATH}\\\")\\n\",\n \"print(f\\\"BIRD data file: {BIRD_DATA_PATH}\\\")\\n\",\n \"print(f\\\"BIRD database dir: {BIRD_DB_ROOT}\\\")\\n\",\n \"\\n\",\n \"# Verify file existence\\n\",\n \"# assert ROLE_ASSIGNMENTS_PATH.exists(), f\\\"Role assignments file not found: {ROLE_ASSIGNMENTS_PATH}\\\"\\n\",\n \"assert BIRD_DATA_PATH.exists(), f\\\"BIRD data file not found: {BIRD_DATA_PATH}\\\"\\n\",\n \"assert BIRD_DB_ROOT.exists(), f\\\"BIRD database dir not found: {BIRD_DB_ROOT}\\\"\\n\",\n \"print(\\\"\\\\n✓ All file paths are valid\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"4b80dc65\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== SQL Column Extraction Test ===\\n\",\n \"Tables: {'song', 'singer'}\\n\",\n \"Columns: {'song': {'singer_id', 'title'}, 'singer': {'id', 'name', 'country', 'age'}}\\n\",\n \"Has *: False\\n\",\n \"\\n\",\n \"=== Permission Check Test (Allow) ===\\n\",\n \"Allowed: True\\n\",\n \"Reason: All required columns are permitted\\n\",\n \"\\n\",\n \"=== Permission Check Test (Deny) ===\\n\",\n \"Allowed: False\\n\",\n \"Reason: Missing column permissions: song: singer_id; singer: age, country, id\\n\",\n \"Missing: {'song': {'singer_id'}, 'singer': {'id', 'country', 'age'}}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.2 Test SQL column extraction and permission checking\\n\",\n \"# Verified working, archived to simplify notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"test_sql = \\\"\\\"\\\"\\n\",\n \"SELECT singer.name, singer.age, song.title\\n\",\n \"FROM singer\\n\",\n \"JOIN song ON singer.id = song.singer_id\\n\",\n \"WHERE singer.country = 'USA'\\n\",\n \"\\\"\\\"\\\"\\n\",\n \"\\n\",\n \"print(\\\"=== SQL Column Extraction Test ===\\\")\\n\",\n \"info = extract_query_columns(test_sql)\\n\",\n \"print(f\\\"Tables: {info.tables}\\\")\\n\",\n \"print(f\\\"Columns: {info.columns}\\\")\\n\",\n \"print(f\\\"Has *: {info.has_star}\\\")\\n\",\n \"\\n\",\n \"# Test permission checking\\n\",\n \"policy_allow = {\\n\",\n \" 'singer': ['name', 'age', 'country', 'id'],\\n\",\n \" 'song': ['title', 'singer_id']\\n\",\n \"}\\n\",\n \"policy_deny = {\\n\",\n \" 'singer': ['name'], # Missing 'age', 'country', 'id'\\n\",\n \" 'song': ['title'] # Missing 'singer_id'\\n\",\n \"}\\n\",\n \"\\n\",\n \"print(\\\"\\\\n=== Permission Check Test (Allow) ===\\\")\\n\",\n \"result1 = check_column_permission(test_sql, policy_allow)\\n\",\n \"print(f\\\"Allowed: {result1.allowed}\\\")\\n\",\n \"print(f\\\"Reason: {result1.reason}\\\")\\n\",\n \"\\n\",\n \"print(\\\"\\\\n=== Permission Check Test (Deny) ===\\\")\\n\",\n \"result2 = check_column_permission(test_sql, policy_deny)\\n\",\n \"print(f\\\"Allowed: {result2.allowed}\\\")\\n\",\n \"print(f\\\"Reason: {result2.reason}\\\")\\n\",\n \"print(f\\\"Missing: {result2.missing_columns}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Column extraction/permission tests - verified\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 16,\n \"id\": \"0a018208\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Generator Initialized ===\\n\",\n \"Total BIRD queries: 1534\\n\",\n \"Databases with role assignments: 11\\n\",\n \"Databases with queries: 11\\n\",\n \"\\n\",\n \"Queries per database:\\n\",\n \" california_schools: 89\\n\",\n \" card_games: 191\\n\",\n \" codebase_community: 186\\n\",\n \" debit_card_specializing: 64\\n\",\n \" european_football_2: 129\\n\",\n \" financial: 106\\n\",\n \" formula_1: 174\\n\",\n \" student_club: 158\\n\",\n \" superhero: 129\\n\",\n \" thrombosis_prediction: 163\\n\",\n \" toxicology: 145\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 4.3 Initialize generator\\n\",\n \"generator = ColumnLevelRBACGenerator(\\n\",\n \" role_assignments_path=str(ROLE_ASSIGNMENTS_PATH),\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH),\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" project_root=str(project_root),\\n\",\n \" seed=42\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"=== Generator Initialized ===\\\")\\n\",\n \"print(f\\\"Total BIRD queries: {len(generator.bird_data)}\\\")\\n\",\n \"print(f\\\"Databases with role assignments: {len(generator.role_assignments.get('assignments', {}))}\\\")\\n\",\n \"print(f\\\"Databases with queries: {len(generator.queries_by_db)}\\\")\\n\",\n \"\\n\",\n \"# Show query counts per database\\n\",\n \"print(\\\"\\\\nQueries per database:\\\")\\n\",\n \"for db_id, queries in sorted(generator.queries_by_db.items()):\\n\",\n \" print(f\\\" {db_id}: {len(queries)}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"2a267246\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# [ARCHIVED] 4.4 Generate sample dataset (for format validation)\\n\",\n \"# Sample dataset used for debugging, production uses 4.5\\n\",\n \"\\n\",\n \"'''\\n\",\n \"print(\\\"=== Generating Sample Dataset ===\\\")\\n\",\n \"sample_stats = generator.generate_and_save(\\n\",\n \" output_path=str(project_root / 'outputs' / 'column_level_rbac_dataset_bird_sample.json'),\\n\",\n \" roles_per_db=1, # Use only 1 role per database\\n\",\n \" queries_per_role=3 # Use only 3 queries per role\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"\\\\\\\\nStatistics:\\\")\\n\",\n \"for k, v in sample_stats.items():\\n\",\n \" if k != 'by_database':\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Sample dataset generation - production uses 4.5\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"4851fde0\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Data Format Validation ===\\n\",\n \"Total entries: 33\\n\",\n \"\\n\",\n \"--- Allowed Entry Example ---\\n\",\n \"db_id: toxicology\\n\",\n \"role: MolecularResearcher\\n\",\n \"policy: {\\\"molecule\\\": [\\\"molecule_id\\\", \\\"label\\\"], \\\"atom\\\": [\\\"atom_id\\\", \\\"molecule_id\\\", \\\"element\\\"], \\\"bond\\\": [\\\"bond...\\n\",\n \"input: What are the atom IDs of the bond TR000_2_5?...\\n\",\n \"output: SELECT T.atom_id FROM connected AS T WHERE T.bond_id = 'TR000_2_5'...\\n\",\n \"difficulty: simple\\n\",\n \"instruction length: 1887 chars\\n\",\n \"\\n\",\n \"--- Denied Entry Example ---\\n\",\n \"db_id: thrombosis_prediction\\n\",\n \"role: ClinicalResearcher\\n\",\n \"input: How old was the patient who had the highest hemoglobin count on the date of labo...\\n\",\n \"output: Sorry, I cannot answer.\\n\",\n \"missing_columns: {'patient': ['Diagnosis']}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.5-4.6 Validate generated data format & Show instruction example\\n\",\n \"# Data format verified, archived to simplify notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"sample_file = project_root / 'outputs' / 'column_level_rbac_dataset_bird_sample.json'\\n\",\n \"with open(sample_file, 'r') as f:\\n\",\n \" sample_data = json.load(f)\\n\",\n \"\\n\",\n \"print(\\\"=== Data Format Validation ===\\\")\\n\",\n \"print(f\\\"Total entries: {len(sample_data)}\\\")\\n\",\n \"\\n\",\n \"allowed_entry = next((e for e in sample_data if e['metadata']['permission'] == 'allowed'), None)\\n\",\n \"denied_entry = next((e for e in sample_data if e['metadata']['permission'] == 'denied'), None)\\n\",\n \"\\n\",\n \"if allowed_entry:\\n\",\n \" print(\\\"\\\\\\\\n--- Allowed Entry Example ---\\\")\\n\",\n \" print(f\\\"db_id: {allowed_entry['db_id']}\\\")\\n\",\n \" print(f\\\"role: {allowed_entry['role']}\\\")\\n\",\n \" print(f\\\"policy: {json.dumps(allowed_entry['policy'])[:100]}...\\\")\\n\",\n \" print(f\\\"input: {allowed_entry['input'][:80]}...\\\")\\n\",\n \" print(f\\\"output: {allowed_entry['output'][:80]}...\\\")\\n\",\n \"\\n\",\n \"if denied_entry:\\n\",\n \" print(\\\"\\\\\\\\n--- Denied Entry Example ---\\\")\\n\",\n \" print(f\\\"db_id: {denied_entry['db_id']}\\\")\\n\",\n \" print(f\\\"role: {denied_entry['role']}\\\")\\n\",\n \" print(f\\\"output: {denied_entry['output']}\\\")\\n\",\n \" print(f\\\"missing_columns: {denied_entry['metadata']['missing_columns']}\\\")\\n\",\n \"\\n\",\n \"# Show full instruction\\n\",\n \"print(\\\"\\\\\\\\n=== Full Instruction Example ===\\\")\\n\",\n \"if allowed_entry:\\n\",\n \" print(allowed_entry['instruction'][:2500])\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Data format validation - verified\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"c05e8787\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# 4.5 Generate Full Column-Level RBAC Dataset\\n\",\n \"from datetime import datetime\\n\",\n \"\\n\",\n \"# Reload the modules to ensure we use the latest CTE fix\\n\",\n \"import importlib\\n\",\n \"import src.processors.column_extractor\\n\",\n \"import src.processors.column_permission_checker\\n\",\n \"import src.processors.column_level_rbac_generator\\n\",\n \"\\n\",\n \"importlib.reload(src.processors.column_extractor)\\n\",\n \"importlib.reload(src.processors.column_permission_checker)\\n\",\n \"importlib.reload(src.processors.column_level_rbac_generator)\\n\",\n \"\\n\",\n \"# Re-import with fresh code\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator\\n\",\n \"\\n\",\n \"# Re-create generator with fresh modules\\n\",\n \"generator = ColumnLevelRBACGenerator(\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" role_assignments_path=str(ROLE_ASSIGNMENTS_PATH),\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH)\\n\",\n \")\\n\",\n \"\\n\",\n \"# Output with date suffix\\n\",\n \"today = datetime.now().strftime('%Y%m%d')\\n\",\n \"output_path = project_root / 'outputs' / f'column_level_rbac_dataset_bird_{today}.json'\\n\",\n \"\\n\",\n \"print(\\\"=== Generating Full Column-Level RBAC Dataset ===\\\")\\n\",\n \"print(f\\\"Output: {output_path}\\\")\\n\",\n \"\\n\",\n \"full_stats = generator.generate_and_save(\\n\",\n \" output_path=str(output_path),\\n\",\n \" roles_per_db=None, # Use all roles (number varies per db based on LLM generation)\\n\",\n \" queries_per_role=None # Use all queries\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"\\\\nFull dataset statistics:\\\")\\n\",\n \"for k, v in full_stats.items():\\n\",\n \" if k == 'by_database':\\n\",\n \" print(f\\\"\\\\nPer-database distribution:\\\")\\n\",\n \" for db_id, counts in v.items():\\n\",\n \" print(f\\\" {db_id}: allowed={counts['allowed']}, denied={counts['denied']}\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" {k}: {v}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"2142ac6e\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Comparison with Table-Level Data Format ===\\n\",\n \"\\n\",\n \"--- Table-Level Entry Fields ---\\n\",\n \"['db_id', 'difficulty', 'input', 'instruction', 'metadata', 'output', 'role', 'tables']\\n\",\n \"\\n\",\n \"--- Column-Level Entry Fields ---\\n\",\n \"['db_id', 'difficulty', 'input', 'instruction', 'metadata', 'output', 'policy', 'role']\\n\",\n \"\\n\",\n \"Common fields: {'output', 'role', 'metadata', 'difficulty', 'db_id', 'input', 'instruction'}\\n\",\n \"Table-Level only: {'tables'}\\n\",\n \"Column-Level only: {'policy'}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.6 Compare with Table-Level format\\n\",\n \"# Format comparison completed, archived to simplify notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"print(\\\"=== Comparison with Table-Level Data Format ===\\\")\\n\",\n \"\\n\",\n \"table_level_file = project_root / 'data' / 'selected' / 'bird' / 'bird_dev_with_role_rbac.json'\\n\",\n \"if table_level_file.exists():\\n\",\n \" with open(table_level_file, 'r') as f:\\n\",\n \" table_level_data = json.load(f)\\n\",\n \" \\n\",\n \" print(\\\"\\\\\\\\n--- Table-Level Entry Fields ---\\\")\\n\",\n \" print(sorted(table_level_data[0].keys()))\\n\",\n \" \\n\",\n \" print(\\\"\\\\\\\\n--- Column-Level Entry Fields ---\\\") \\n\",\n \" print(sorted(sample_data[0].keys()))\\n\",\n \" \\n\",\n \" tl_keys = set(table_level_data[0].keys())\\n\",\n \" cl_keys = set(sample_data[0].keys())\\n\",\n \" \\n\",\n \" print(f\\\"\\\\\\\\nCommon fields: {tl_keys & cl_keys}\\\")\\n\",\n \" print(f\\\"Table-Level only: {tl_keys - cl_keys}\\\")\\n\",\n \" print(f\\\"Column-Level only: {cl_keys - tl_keys}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Table-level comparison - format verified compatible\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"7392ebc1\",\n \"metadata\": {},\n \"source\": [\n \"## 5. Role Generation Quality Assessment\\n\",\n \"\\n\",\n \"Automated quality checks for generated role files and RBAC datasets.\\n\",\n \"\\n\",\n \"### Quality Metrics\\n\",\n \"\\n\",\n \"| Metric | Healthy Range | Issue Description |\\n\",\n \"|--------|---------------|-------------------|\\n\",\n \"| **Deny Rate** | 5% - 90% | <5% too permissive, >90% too restrictive |\\n\",\n \"| **All-Allow Queries** | <50% | High value indicates poor role differentiation |\\n\",\n \"| **Role-Schema Similarity** | >0.6 | <0.6 indicates poor semantic alignment |\\n\",\n \"| **Policy Overlap** | <70% | >70% indicates redundant roles |\\n\",\n \"| **Coverage Diversity** | std >0.1 | Low std indicates roles have similar permissions |\\n\",\n \"\\n\",\n \"### Evaluation Pipeline\\n\",\n \"1. **Deny Rate Analysis**: Check permission distribution balance\\n\",\n \"2. **Semantic Quality**: Embedding-based role-schema alignment\\n\",\n \"3. **Policy Overlap**: Jaccard similarity to detect redundant roles\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"71f8f60f\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# 5.1 Setup Quality Evaluator (force reload all modules)\\n\",\n \"import importlib\\n\",\n \"import sys\\n\",\n \"from pathlib import Path\\n\",\n \"import glob\\n\",\n \"\\n\",\n \"# Force reload the entire module tree\\n\",\n \"if 'src.role_evaluator.quality_metrics' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator.quality_metrics']\\n\",\n \"if 'src.role_evaluator.quality_visualizer' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator.quality_visualizer']\\n\",\n \"if 'src.role_evaluator' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator']\\n\",\n \"\\n\",\n \"from src.role_evaluator import RoleQualityEvaluator, RoleQualityVisualizer, print_quality_report\\n\",\n \"\\n\",\n \"# Configure paths\\n\",\n \"project_root = Path.cwd().parent if Path.cwd().name == 'notebooks' else Path.cwd()\\n\",\n \"\\n\",\n \"# Find most recent dataset and role assignments files\\n\",\n \"role_files = sorted(glob.glob(str(project_root / 'outputs' / 'column_level_role_assignments_bird_*.json')))\\n\",\n \"ROLE_ASSIGNMENTS_PATH = role_files[-1] if role_files else str(project_root / 'outputs' / 'column_level_role_assignments_bird.json')\\n\",\n \"\\n\",\n \"# Check for v2 role assignments (after regeneration)\\n\",\n \"role_files_v2 = sorted([f for f in glob.glob(str(project_root / 'outputs' / 'bird_column_level_rbac_assignments_*.json'))\\n\",\n \" if 'v2' in f])\\n\",\n \"if role_files_v2:\\n\",\n \" ROLE_ASSIGNMENTS_PATH = role_files_v2[-1]\\n\",\n \"\\n\",\n \"dataset_files = sorted([f for f in glob.glob(str(project_root / 'outputs' / 'column_level_rbac_dataset_bird_*.json')) \\n\",\n \" if 'metadata' not in f])\\n\",\n \"if not dataset_files:\\n\",\n \" raise FileNotFoundError(\\\"No RBAC dataset found in outputs/\\\")\\n\",\n \"DATASET_PATH = dataset_files[-1]\\n\",\n \"\\n\",\n \"SCHEMA_DIR = str(project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases')\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# CONFIGURABLE QUALITY THRESHOLDS\\n\",\n \"# Adjust based on dataset characteristics\\n\",\n \"# BIRD has larger DBs with more columns, so thresholds differ from Spider\\n\",\n \"# ============================================================\\n\",\n \"QUALITY_THRESHOLDS = {\\n\",\n \" \\\"deny_rate_min\\\": 5.0, # Warn if deny rate < this (roles too permissive)\\n\",\n \" \\\"coverage_max\\\": 0.85, # Warn if avg coverage > this (roles too broad)\\n\",\n \" \\\"coverage_std_min\\\": 0.065, # Warn if coverage std < this (low diversity)\\n\",\n \" \\\"max_overlap\\\": 0.80, # Warn if max overlap > this (redundant roles)\\n\",\n \"}\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EMBEDDING MODEL CONFIGURATION\\n\",\n \"# Backend auto-detected based on model name, or set explicitly\\n\",\n \"# ============================================================\\n\",\n \"# OpenAI models (requires OPENAI_API_KEY):\\n\",\n \"# - \\\"text-embedding-3-small\\\" (default, cost-effective)\\n\",\n \"# - \\\"text-embedding-3-large\\\" (higher accuracy)\\n\",\n \"# - \\\"text-embedding-ada-002\\\" (legacy)\\n\",\n \"# \\n\",\n \"# BAAI/fastembed models (local, no API key needed):\\n\",\n \"# - \\\"BAAI/bge-small-en-v1.5\\\" (fast, lightweight)\\n\",\n \"# - \\\"BAAI/bge-base-en-v1.5\\\" (balanced)\\n\",\n \"# - \\\"BAAI/bge-large-en-v1.5\\\" (best accuracy)\\n\",\n \"# ============================================================\\n\",\n \"# EMBED_MODEL = \\\"BAAI/bge-small-en-v1.5\\\"\\n\",\n \"EMBED_MODEL = \\\"text-embedding-3-small\\\"\\n\",\n \"EMBED_BACKEND = None # None = auto-detect, or \\\"openai\\\" / \\\"fastembed\\\"\\n\",\n \"\\n\",\n \"print(\\\"=== Quality Thresholds (Configurable for BIRD) ===\\\")\\n\",\n \"for k, v in QUALITY_THRESHOLDS.items():\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"print(f\\\" embed_model: {EMBED_MODEL}\\\")\\n\",\n \"print(f\\\" embed_backend: {EMBED_BACKEND or 'auto'}\\\")\\n\",\n \"\\n\",\n \"print(f\\\"\\\\nRole Assignments: {Path(ROLE_ASSIGNMENTS_PATH).name if ROLE_ASSIGNMENTS_PATH else 'Not found'}\\\")\\n\",\n \"print(f\\\"Dataset: {Path(DATASET_PATH).name if DATASET_PATH else 'Not found'}\\\")\\n\",\n \"print(f\\\"Schema Dir: {SCHEMA_DIR}\\\")\\n\",\n \"\\n\",\n \"if ROLE_ASSIGNMENTS_PATH and DATASET_PATH:\\n\",\n \" evaluator = RoleQualityEvaluator(\\n\",\n \" role_assignments_path=ROLE_ASSIGNMENTS_PATH,\\n\",\n \" dataset_path=DATASET_PATH,\\n\",\n \" schema_dir=SCHEMA_DIR,\\n\",\n \" embed_model=EMBED_MODEL,\\n\",\n \" embed_backend=EMBED_BACKEND,\\n\",\n \" thresholds=QUALITY_THRESHOLDS\\n\",\n \" )\\n\",\n \" print(\\\"\\\\n✓ Quality evaluator initialized with custom thresholds\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"\\\\n Missing files - generate roles and dataset first\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 74,\n \"id\": \"8135e2e3\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[embedding_utils] Using OpenAI embeddings (text-embedding-3-small) for 11 texts\\n\",\n \"[embedding_utils] Using OpenAI embeddings (text-embedding-3-small) for 47 texts\\n\",\n \"Overall: 11 databases, 53 roles, 7252 entries\\n\",\n \"Pass Rate: 100.0% (11/11 pass)\\n\",\n \"\\n\",\n \"Using Thresholds: deny_rate_min=5.0%, max_overlap=80%, coverage_std_min=0.065\\n\",\n \"\\n\",\n \"=== Deny Rate Analysis ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Deny%</th>\\n\",\n \" <th>AllAllow%</th>\\n\",\n \" <th>AllDeny</th>\\n\",\n \" <th>Roles</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>73.5%</td>\\n\",\n \" <td>1.1%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>46.9%</td>\\n\",\n \" <td>2.1%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>37.8%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>54.7%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>57.1%</td>\\n\",\n \" <td>1.6%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>70.6%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>44.7%</td>\\n\",\n \" <td>8.7%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>49.9%</td>\\n\",\n \" <td>1.3%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>49.1%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>33.7%</td>\\n\",\n \" <td>2.5%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>47.4%</td>\\n\",\n \" <td>11.7%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Deny% AllAllow% AllDeny Roles Status\\n\",\n \"0 california_schools 73.5% 1.1% 0 5 OK\\n\",\n \"1 card_games 46.9% 2.1% 0 5 OK\\n\",\n \"2 codebase_community 37.8% 0.0% 0 5 OK\\n\",\n \"3 debit_card_specializing 54.7% 0.0% 0 6 OK\\n\",\n \"4 european_football_2 57.1% 1.6% 0 5 OK\\n\",\n \"5 financial 70.6% 0.0% 0 5 OK\\n\",\n \"6 formula_1 44.7% 8.7% 0 4 OK\\n\",\n \"7 student_club 49.9% 1.3% 0 5 OK\\n\",\n \"8 superhero 49.1% 0.0% 0 5 OK\\n\",\n \"9 thrombosis_prediction 33.7% 2.5% 0 4 OK\\n\",\n \"10 toxicology 47.4% 11.7% 0 4 OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Semantic Quality (Similarity: role-schema alignment, Coverage: column access, Diversity: coverage std) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Similarity</th>\\n\",\n \" <th>Coverage</th>\\n\",\n \" <th>Diversity</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>0.6902</td>\\n\",\n \" <td>34.8%</td>\\n\",\n \" <td>0.0996</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>0.6843</td>\\n\",\n \" <td>50.0%</td>\\n\",\n \" <td>0.2965</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>0.6332</td>\\n\",\n \" <td>60.6%</td>\\n\",\n \" <td>0.2888</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>0.6264</td>\\n\",\n \" <td>63.8%</td>\\n\",\n \" <td>0.1299</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>0.6799</td>\\n\",\n \" <td>31.3%</td>\\n\",\n \" <td>0.0984</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>0.6410</td>\\n\",\n \" <td>46.8%</td>\\n\",\n \" <td>0.1288</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>0.6599</td>\\n\",\n \" <td>44.0%</td>\\n\",\n \" <td>0.1480</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>0.6780</td>\\n\",\n \" <td>50.5%</td>\\n\",\n \" <td>0.1701</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>0.6835</td>\\n\",\n \" <td>61.3%</td>\\n\",\n \" <td>0.1895</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>0.6607</td>\\n\",\n \" <td>69.3%</td>\\n\",\n \" <td>0.3798</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>0.6698</td>\\n\",\n \" <td>69.7%</td>\\n\",\n \" <td>0.1134</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Similarity Coverage Diversity Status\\n\",\n \"0 california_schools 0.6902 34.8% 0.0996 OK\\n\",\n \"1 card_games 0.6843 50.0% 0.2965 OK\\n\",\n \"2 codebase_community 0.6332 60.6% 0.2888 OK\\n\",\n \"3 debit_card_specializing 0.6264 63.8% 0.1299 OK\\n\",\n \"4 european_football_2 0.6799 31.3% 0.0984 OK\\n\",\n \"5 financial 0.6410 46.8% 0.1288 OK\\n\",\n \"6 formula_1 0.6599 44.0% 0.1480 OK\\n\",\n \"7 student_club 0.6780 50.5% 0.1701 OK\\n\",\n \"8 superhero 0.6835 61.3% 0.1895 OK\\n\",\n \"9 thrombosis_prediction 0.6607 69.3% 0.3798 OK\\n\",\n \"10 toxicology 0.6698 69.7% 0.1134 OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Policy Overlap Analysis ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>AvgOverlap</th>\\n\",\n \" <th>MaxOverlap</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>30.7%</td>\\n\",\n \" <td>57.1%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>11.9%</td>\\n\",\n \" <td>23.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>28.0%</td>\\n\",\n \" <td>57.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>47.0%</td>\\n\",\n \" <td>65.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>14.4%</td>\\n\",\n \" <td>23.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>21.5%</td>\\n\",\n \" <td>37.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>29.3%</td>\\n\",\n \" <td>32.9%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>30.2%</td>\\n\",\n \" <td>45.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>33.0%</td>\\n\",\n \" <td>52.6%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>18.1%</td>\\n\",\n \" <td>38.5%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>53.7%</td>\\n\",\n \" <td>66.7%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database AvgOverlap MaxOverlap Status\\n\",\n \"0 california_schools 30.7% 57.1% OK\\n\",\n \"1 card_games 11.9% 23.8% OK\\n\",\n \"2 codebase_community 28.0% 57.8% OK\\n\",\n \"3 debit_card_specializing 47.0% 65.0% OK\\n\",\n \"4 european_football_2 14.4% 23.8% OK\\n\",\n \"5 financial 21.5% 37.0% OK\\n\",\n \"6 formula_1 29.3% 32.9% OK\\n\",\n \"7 student_club 30.2% 45.0% OK\\n\",\n \"8 superhero 33.0% 52.6% OK\\n\",\n \"9 thrombosis_prediction 18.1% 38.5% OK\\n\",\n \"10 toxicology 53.7% 66.7% OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n }\n ],\n \"source\": [\n \"# 5.2 Run Quality Evaluations and Display as DataFrame\\n\",\n \"import pandas as pd\\n\",\n \"import importlib\\n\",\n \"\\n\",\n \"# Force reload modules to use latest code\\n\",\n \"import src.role_evaluator.quality_metrics\\n\",\n \"import src.role_evaluator.quality_visualizer\\n\",\n \"importlib.reload(src.role_evaluator.quality_metrics)\\n\",\n \"importlib.reload(src.role_evaluator.quality_visualizer)\\n\",\n \"\\n\",\n \"from src.role_evaluator.quality_metrics import print_quality_report\\n\",\n \"\\n\",\n \"# Set display options for full content (no truncation)\\n\",\n \"pd.set_option('display.max_colwidth', None)\\n\",\n \"pd.set_option('display.max_rows', None)\\n\",\n \"pd.set_option('display.width', None)\\n\",\n \"\\n\",\n \"if 'evaluator' in dir():\\n\",\n \" report = evaluator.evaluate_all()\\n\",\n \" \\n\",\n \" # Summary\\n\",\n \" print(f\\\"Overall: {report.total_databases} databases, {report.total_roles} roles, {report.total_entries} entries\\\")\\n\",\n \" print(f\\\"Pass Rate: {report.pass_rate:.1%} ({report.total_databases - len(report.databases_with_issues)}/{report.total_databases} pass)\\\")\\n\",\n \" print(f\\\"\\\\nUsing Thresholds: deny_rate_min={QUALITY_THRESHOLDS['deny_rate_min']}%, \\\"\\n\",\n \" f\\\"max_overlap={QUALITY_THRESHOLDS['max_overlap']:.0%}, \\\"\\n\",\n \" f\\\"coverage_std_min={QUALITY_THRESHOLDS['coverage_std_min']}\\\\n\\\")\\n\",\n \" \\n\",\n \" # Deny Rate DataFrame - Full display\\n\",\n \" if report.deny_rate_metrics:\\n\",\n \" deny_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"Deny%\\\": f\\\"{m.deny_rate:.1f}%\\\",\\n\",\n \" \\\"AllAllow%\\\": f\\\"{m.all_allow_pct:.1f}%\\\",\\n\",\n \" \\\"AllDeny\\\": m.all_deny_queries,\\n\",\n \" \\\"Roles\\\": len(m.roles),\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.deny_rate_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"=== Deny Rate Analysis ===\\\")\\n\",\n \" display(deny_df)\\n\",\n \"\\n\",\n \" # Semantic Quality DataFrame \\n\",\n \" if report.semantic_metrics:\\n\",\n \" sem_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"Similarity\\\": f\\\"{m.avg_similarity:.4f}\\\",\\n\",\n \" \\\"Coverage\\\": f\\\"{m.avg_coverage:.1%}\\\",\\n\",\n \" \\\"Diversity\\\": f\\\"{m.coverage_std:.4f}\\\",\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.semantic_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Semantic Quality (Similarity: role-schema alignment, Coverage: column access, Diversity: coverage std) ===\\\")\\n\",\n \" display(sem_df)\\n\",\n \"\\n\",\n \" # Policy Overlap DataFrame\\n\",\n \" if report.overlap_metrics:\\n\",\n \" ovl_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"AvgOverlap\\\": f\\\"{m.avg_overlap:.1%}\\\",\\n\",\n \" \\\"MaxOverlap\\\": f\\\"{m.max_overlap:.1%}\\\",\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.overlap_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Policy Overlap Analysis ===\\\")\\n\",\n \" display(ovl_df)\\n\",\n \"else:\\n\",\n \" print(\\\" Evaluator not initialized\\\")\\n\",\n \"\\n\",\n \"pd.reset_option('display.max_colwidth')\\n\",\n \"pd.reset_option('display.max_rows')\\n\",\n \"pd.reset_option('display.width')\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 75,\n \"id\": \"89f843e7\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# # 5.3 Quality Dashboard Visualization\\n\",\n \"# import matplotlib.pyplot as plt\\n\",\n \"# from src.role_evaluator.quality_visualizer import RoleQualityVisualizer\\n\",\n \"\\n\",\n \"# visualizer = RoleQualityVisualizer(evaluator=evaluator, report=report)\\n\",\n \"# fig = visualizer.plot_quality_dashboard()\\n\",\n \"# plt.show()\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"86eb26de\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# 5.4 Semantic Alignment Visualization (Column-Level RBAC)\\n\",\n \"# Circle size = column coverage, Red triangles = SystemManager\\n\",\n \"import random\\n\",\n \"import math\\n\",\n \"import matplotlib.pyplot as plt\\n\",\n \"from matplotlib.colors import PowerNorm\\n\",\n \"from matplotlib import colormaps as cm\\n\",\n \"from collections import defaultdict\\n\",\n \"from matplotlib.lines import Line2D\\n\",\n \"import numpy as np\\n\",\n \"\\n\",\n \"sys.path.insert(0, str(project_root / \\\"plot\\\"))\\n\",\n \"from embedding_utils import cosine_similarity\\n\",\n \"\\n\",\n \"# Prepare records (recompute)\\n\",\n \"role_records = evaluator._prepare_role_records()\\n\",\n \"evaluator._compute_embeddings()\\n\",\n \"\\n\",\n \"# Build vis_records with is_system_manager\\n\",\n \"vis_records = []\\n\",\n \"for spec in role_records:\\n\",\n \" schema_emb = evaluator._schema_emb_dict.get(spec[\\\"schema_text\\\"])\\n\",\n \" role_emb = evaluator._role_emb_dict.get(spec[\\\"role_text\\\"])\\n\",\n \" \\n\",\n \" if schema_emb is not None and role_emb is not None:\\n\",\n \" raw_sim = cosine_similarity(role_emb, schema_emb)\\n\",\n \" similarity = max(0.0, min(1.0, (raw_sim + 1.0) / 2.0))\\n\",\n \" else:\\n\",\n \" similarity = 0.5\\n\",\n \" \\n\",\n \" total_cols = spec.get(\\\"total_columns\\\", 1)\\n\",\n \" accessible_cols = spec.get(\\\"accessible_columns\\\", 0)\\n\",\n \" col_coverage = accessible_cols / total_cols if total_cols > 0 else 0\\n\",\n \" \\n\",\n \" vis_records.append({\\n\",\n \" \\\"db_id\\\": spec[\\\"db_id\\\"],\\n\",\n \" \\\"role\\\": spec[\\\"role\\\"],\\n\",\n \" \\\"similarity\\\": similarity,\\n\",\n \" \\\"is_system_manager\\\": spec.get(\\\"is_system_manager\\\", False),\\n\",\n \" \\\"column_coverage\\\": col_coverage,\\n\",\n \" })\\n\",\n \"\\n\",\n \"sm_count = sum(1 for r in vis_records if r[\\\"is_system_manager\\\"])\\n\",\n \"print(f\\\"Total roles: {len(vis_records)}, SystemManager: {sm_count}\\\")\\n\",\n \"\\n\",\n \"# Plot function\\n\",\n \"def plot_semantic(records, output_path, title, dbs_per_panel=12):\\n\",\n \" db_to_entries = defaultdict(list)\\n\",\n \" sims = []\\n\",\n \" for rec in records:\\n\",\n \" sims.append(rec[\\\"similarity\\\"])\\n\",\n \" size = 90.0 + rec[\\\"column_coverage\\\"] * 220.0\\n\",\n \" db_to_entries[rec[\\\"db_id\\\"]].append({\\n\",\n \" \\\"sim\\\": rec[\\\"similarity\\\"], \\\"size\\\": size,\\n\",\n \" \\\"is_sm\\\": rec[\\\"is_system_manager\\\"],\\n\",\n \" })\\n\",\n \" \\n\",\n \" db_ids = sorted(db_to_entries.keys())\\n\",\n \" panels = [db_ids[i:i+dbs_per_panel] for i in range(0, len(db_ids), dbs_per_panel)]\\n\",\n \" \\n\",\n \" min_s, max_s = min(sims), max(sims)\\n\",\n \" if math.isclose(min_s, max_s): max_s = min_s + 1e-6\\n\",\n \" span = max_s - min_s\\n\",\n \" norm = PowerNorm(gamma=0.55, vmin=min_s, vmax=max_s)\\n\",\n \" cmap = cm[\\\"viridis\\\"]\\n\",\n \" \\n\",\n \" fig = plt.figure(figsize=(11, sum(0.35*len(p)+0.6 for p in panels)+2))\\n\",\n \" gs = fig.add_gridspec(len(panels), 1, height_ratios=[0.35*len(p)+0.6 for p in panels], hspace=0.18)\\n\",\n \" axes = []\\n\",\n \" \\n\",\n \" random.seed(42)\\n\",\n \" sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)\\n\",\n \" sm.set_array([])\\n\",\n \" \\n\",\n \" for pi, chunk in enumerate(panels):\\n\",\n \" ax = fig.add_subplot(gs[pi, 0])\\n\",\n \" axes.append(ax)\\n\",\n \" for ri, db in enumerate(chunk):\\n\",\n \" y = float(ri)\\n\",\n \" ax.axhline(y, color=\\\"#d0d0d0\\\", linestyle=\\\"--\\\", lw=0.9, zorder=0)\\n\",\n \" for e in db_to_entries[db]:\\n\",\n \" x = (e[\\\"sim\\\"] - min_s) / span + random.uniform(-0.012, 0.012)\\n\",\n \" if e[\\\"is_sm\\\"]:\\n\",\n \" ax.scatter(x, y, s=250, c=\\\"#d62728\\\", alpha=0.95,\\n\",\n \" edgecolors=\\\"#8c1c15\\\", lw=1.0, marker=\\\"^\\\", zorder=4)\\n\",\n \" else:\\n\",\n \" ax.scatter(x, y + random.uniform(-0.16, 0.16), s=e[\\\"size\\\"]*2.5,\\n\",\n \" c=[e[\\\"sim\\\"]], cmap=cmap, norm=norm, alpha=0.82,\\n\",\n \" edgecolors=\\\"#333\\\", lw=0.45, marker=\\\"o\\\")\\n\",\n \" ax.set_yticks(range(len(chunk)))\\n\",\n \" ax.set_yticklabels(chunk, fontsize=10, weight='bold')\\n\",\n \" ax.set_xlim(-0.05, 1.05)\\n\",\n \" ax.set_ylim(-0.6, len(chunk)-0.4)\\n\",\n \" ax.set_xticks(np.linspace(0, 1, 5))\\n\",\n \" ax.set_xticklabels([f\\\"{min_s + p*span:.2f}\\\" for p in np.linspace(0,1,5)], fontsize=9)\\n\",\n \" ax.grid(axis=\\\"x\\\", ls=\\\":\\\", lw=0.5, alpha=0.45)\\n\",\n \" \\n\",\n \" axes[-1].set_xlabel(\\\"Semantic similarity\\\", fontsize=12, weight='bold')\\n\",\n \" fig.suptitle(f\\\"{title}: Role-Schema Alignment\\\", fontsize=14, y=0.98, weight='bold')\\n\",\n \" cbar = fig.colorbar(sm, ax=axes, fraction=0.03, pad=0.015)\\n\",\n \" cbar.set_label(\\\"Similarity\\\", fontsize=10)\\n\",\n \" \\n\",\n \" legend = [\\n\",\n \" Line2D([0],[0], marker='^', color='w', markerfacecolor='#d62728',\\n\",\n \" markeredgecolor='#8c1c15', markersize=12, label='SystemManager'),\\n\",\n \" Line2D([0],[0], marker='o', color='w', markerfacecolor='#21918c',\\n\",\n \" markeredgecolor='#333', markersize=8, label='Other (size=col coverage)'),\\n\",\n \" ]\\n\",\n \" fig.legend(handles=legend, loc='upper right', fontsize=9)\\n\",\n \" plt.tight_layout(rect=[0,0,0.95,0.95])\\n\",\n \" fig.savefig(output_path, dpi=150, bbox_inches='tight')\\n\",\n \" plt.close(fig)\\n\",\n \" print(f\\\"Saved: {output_path}\\\")\\n\",\n \"\\n\",\n \"output_path = project_root / \\\"plot\\\" / \\\"column_level_rbac_semantic.png\\\"\\n\",\n \"plot_semantic(vis_records, output_path, \\\"Column-Level RBAC (BIRD)\\\")\\n\",\n \"from IPython.display import Image, display\\n\",\n \"display(Image(filename=str(output_path)))\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 13,\n \"id\": \"2dd807b4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Databases Requiring Review (1/11) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Deny%</th>\\n\",\n \" <th>AllAllow%</th>\\n\",\n \" <th>Coverage</th>\\n\",\n \" <th>CovStd</th>\\n\",\n \" <th>MaxOverlap</th>\\n\",\n \" <th>Issues</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>63.1%</td>\\n\",\n \" <td>5.6%</td>\\n\",\n \" <td>50.8%</td>\\n\",\n \" <td>0.059</td>\\n\",\n \" <td>72.3%</td>\\n\",\n \" <td>all-deny queries found (1) - SystemManager coverage broken; Low role diversity</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Deny% AllAllow% Coverage CovStd MaxOverlap \\\\\\n\",\n \"0 california_schools 63.1% 5.6% 50.8% 0.059 72.3% \\n\",\n \"\\n\",\n \" Issues \\n\",\n \"0 all-deny queries found (1) - SystemManager coverage broken; Low role diversity \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"Issue Thresholds:\\n\",\n \" - Deny% < 5%: roles too permissive\\n\",\n \" - Coverage > 85%: most roles have near-full access\\n\",\n \" - CovStd < 0.065: roles have similar coverage (low diversity)\\n\",\n \" - MaxOverlap >80%: redundant roles with similar policies\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 5.5 Export Problematic Databases as DataFrame\\n\",\n \"if report.databases_with_issues:\\n\",\n \" issues_data = []\\n\",\n \" for db_id in report.databases_with_issues:\\n\",\n \" row = {\\\"Database\\\": db_id}\\n\",\n \" \\n\",\n \" if db_id in report.deny_rate_metrics:\\n\",\n \" m = report.deny_rate_metrics[db_id]\\n\",\n \" row[\\\"Deny%\\\"] = f\\\"{m.deny_rate:.1f}%\\\"\\n\",\n \" row[\\\"AllAllow%\\\"] = f\\\"{m.all_allow_pct:.1f}%\\\"\\n\",\n \" \\n\",\n \" if db_id in report.semantic_metrics:\\n\",\n \" m = report.semantic_metrics[db_id]\\n\",\n \" row[\\\"Coverage\\\"] = f\\\"{m.avg_coverage:.1%}\\\" # Source of \\\"Most roles too permissive\\\" (>90%)\\n\",\n \" row[\\\"CovStd\\\"] = f\\\"{m.coverage_std:.3f}\\\" # Source of \\\"Low role diversity\\\" (<0.1)\\n\",\n \" \\n\",\n \" if db_id in report.overlap_metrics:\\n\",\n \" m = report.overlap_metrics[db_id]\\n\",\n \" row[\\\"MaxOverlap\\\"] = f\\\"{m.max_overlap:.1%}\\\" # Source of \\\"High max overlap\\\" (>70%)\\n\",\n \" \\n\",\n \" row[\\\"Issues\\\"] = \\\"; \\\".join(report.all_issues.get(db_id, []))\\n\",\n \" issues_data.append(row)\\n\",\n \" \\n\",\n \" issues_df = pd.DataFrame(issues_data)\\n\",\n \" print(f\\\"=== Databases Requiring Review ({len(issues_df)}/{report.total_databases}) ===\\\")\\n\",\n \" \\n\",\n \" # Display with full column width for Issues column\\n\",\n \" pd.set_option('display.max_colwidth', None)\\n\",\n \" pd.set_option('display.max_rows', None)\\n\",\n \" display(issues_df)\\n\",\n \" pd.reset_option('display.max_colwidth')\\n\",\n \" pd.reset_option('display.max_rows')\\n\",\n \" \\n\",\n \" print(\\\"\\\\nIssue Thresholds:\\\")\\n\",\n \" print(\\\" - Deny% < 5%: roles too permissive\\\")\\n\",\n \" print(\\\" - Coverage > 85%: most roles have near-full access\\\")\\n\",\n \" print(\\\" - CovStd < 0.065: roles have similar coverage (low diversity)\\\")\\n\",\n \" print(\\\" - MaxOverlap >80%: redundant roles with similar policies\\\")\\n\",\n \"\\n\",\n \"else:\\n\",\n \" print(\\\"[OK] All databases pass quality checks!\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"7586c455\",\n \"metadata\": {},\n \"source\": [\n \"## 6. Regenerate Roles for Problematic Databases\\n\",\n \"\\n\",\n \"Human-in-the-loop workflow for fixing quality issues:\\n\",\n \"\\n\",\n \"| Step | Cell | Description |\\n\",\n \"|------|------|-------------|\\n\",\n \"| 1 | 6.1 | Select database, view schema and current roles |\\n\",\n \"| 2 | 6.2 | Write regeneration hints (edit manually) |\\n\",\n \"| 3 | 6.3 | Execute regeneration with LLM |\\n\",\n \"| 4 | 6.4 | Preview and compare OLD vs NEW |\\n\",\n \"| 5 | 6.4.1 | Select & combine roles from OLD and NEW |\\n\",\n \"| 6 | 6.5 | Apply changes if satisfied |\\n\",\n \"| 7 | 6.6 | Regenerate RBAC Dataset with updated roles |\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 32,\n \"id\": \"70f25858\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"Database: formula_1\\n\",\n \"======================================================================\\n\",\n \"\\n\",\n \"=== Schema (13 tables, 94 columns) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Table</th>\\n\",\n \" <th>Column</th>\\n\",\n \" <th>Type</th>\\n\",\n \" <th>PK</th>\\n\",\n \" <th>FK</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>circuitId</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td>Yes</td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>circuitRef</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>name</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>location</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>country</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>...</th>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>89</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLap</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>90</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>rank</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>91</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLapTime</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>92</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLapSpeed</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>93</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>statusId</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td>status.statusId</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"<p>94 rows × 5 columns</p>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Table Column Type PK FK\\n\",\n \"0 circuits circuitId INTEGER Yes \\n\",\n \"1 circuits circuitRef TEXT \\n\",\n \"2 circuits name TEXT \\n\",\n \"3 circuits location TEXT \\n\",\n \"4 circuits country TEXT \\n\",\n \".. ... ... ... ... ...\\n\",\n \"89 results fastestLap INTEGER \\n\",\n \"90 results rank INTEGER \\n\",\n \"91 results fastestLapTime TEXT \\n\",\n \"92 results fastestLapSpeed TEXT \\n\",\n \"93 results statusId INTEGER status.statusId\\n\",\n \"\\n\",\n \"[94 rows x 5 columns]\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Current Roles (5) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Columns</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap times, pit stops, and qualifying results</td>\\n\",\n \" <td>9</td>\\n\",\n \" <td>65 (*)</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date, time]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>StandingsManager</td>\\n\",\n \" <td>Manages championship standings and constructor/driver points calculations</td>\\n\",\n \" <td>7</td>\\n\",\n \" <td>43 (*)</td>\\n\",\n \" <td>constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; results: [resultId, raceId, driverId, constructorId, position, positionOrder, points]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>MediaCoordinator</td>\\n\",\n \" <td>Provides public information for media and fan engagement</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>33 (*)</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, constructorRef, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [*]; races: [raceId, year, round, circuitId, name, date, time, url]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal details including date of birth</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>32 (*)</td>\\n\",\n \" <td>drivers: [*]; races: [raceId, year, round, name, date]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, statusId]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintenance and data integrity</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>94 (*)</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*]</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Role \\\\\\n\",\n \"0 RaceAnalyst \\n\",\n \"1 StandingsManager \\n\",\n \"2 MediaCoordinator \\n\",\n \"3 DriverManager \\n\",\n \"4 SystemManager \\n\",\n \"\\n\",\n \" Description \\\\\\n\",\n \"0 Analyzes race performance data including lap times, pit stops, and qualifying results \\n\",\n \"1 Manages championship standings and constructor/driver points calculations \\n\",\n \"2 Provides public information for media and fan engagement \\n\",\n \"3 Manages driver information and personal details including date of birth \\n\",\n \"4 Full administrative access for system maintenance and data integrity \\n\",\n \"\\n\",\n \" Tables Columns \\\\\\n\",\n \"0 9 65 (*) \\n\",\n \"1 7 43 (*) \\n\",\n \"2 6 33 (*) \\n\",\n \"3 4 32 (*) \\n\",\n \"4 13 94 (*) \\n\",\n \"\\n\",\n \" Policy \\n\",\n \"0 circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date, time]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]; status: [statusId, status] \\n\",\n \"1 constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; results: [resultId, raceId, driverId, constructorId, position, positionOrder, points] \\n\",\n \"2 circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, constructorRef, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [*]; races: [raceId, year, round, circuitId, name, date, time, url]; status: [statusId, status] \\n\",\n \"3 drivers: [*]; races: [raceId, year, round, name, date]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, statusId]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins] \\n\",\n \"4 circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*] \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Role x Table Matrix (column counts, * expanded) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<style type=\\\"text/css\\\">\\n\",\n \"#T_3c946_row0_col0, #T_3c946_row0_col1, #T_3c946_row0_col2, #T_3c946_row0_col3, #T_3c946_row0_col6, #T_3c946_row0_col7, #T_3c946_row0_col8, #T_3c946_row0_col11, #T_3c946_row0_col12, #T_3c946_row1_col1, #T_3c946_row1_col2, #T_3c946_row1_col4, #T_3c946_row1_col6, #T_3c946_row1_col7, #T_3c946_row1_col8, #T_3c946_row1_col10, #T_3c946_row2_col1, #T_3c946_row2_col2, #T_3c946_row2_col4, #T_3c946_row2_col11, #T_3c946_row3_col0, #T_3c946_row3_col6, #T_3c946_row3_col7, #T_3c946_row3_col8, #T_3c946_row3_col11, #T_3c946_row3_col12 {\\n\",\n \" background-color: #f7fbff;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col4, #T_3c946_row2_col5, #T_3c946_row2_col7, #T_3c946_row2_col9, #T_3c946_row3_col2, #T_3c946_row3_col4, #T_3c946_row3_col5, #T_3c946_row3_col10, #T_3c946_row4_col2, #T_3c946_row4_col4, #T_3c946_row4_col7 {\\n\",\n \" background-color: #99c7e0;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col5, #T_3c946_row2_col8, #T_3c946_row4_col0, #T_3c946_row4_col5, #T_3c946_row4_col8 {\\n\",\n \" background-color: #6aaed6;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col9, #T_3c946_row1_col3, #T_3c946_row3_col1, #T_3c946_row4_col1, #T_3c946_row4_col3 {\\n\",\n \" background-color: #bdd7ec;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col10 {\\n\",\n \" background-color: #4695c8;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row1_col0, #T_3c946_row1_col5, #T_3c946_row1_col9, #T_3c946_row4_col9 {\\n\",\n \" background-color: #82bbdb;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row1_col11, #T_3c946_row1_col12, #T_3c946_row2_col12, #T_3c946_row4_col11, #T_3c946_row4_col12 {\\n\",\n \" background-color: #e1edf8;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col0, #T_3c946_row2_col6, #T_3c946_row3_col9, #T_3c946_row4_col6 {\\n\",\n \" background-color: #abd0e6;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col3, #T_3c946_row3_col3 {\\n\",\n \" background-color: #ccdff1;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col10 {\\n\",\n \" background-color: #083e81;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row4_col10 {\\n\",\n \" background-color: #08306b;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"</style>\\n\",\n \"<table id=\\\"T_3c946\\\">\\n\",\n \" <thead>\\n\",\n \" <tr>\\n\",\n \" <th class=\\\"index_name level0\\\" >Table</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col0\\\" class=\\\"col_heading level0 col0\\\" >circuits</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col1\\\" class=\\\"col_heading level0 col1\\\" >constructorResults</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col2\\\" class=\\\"col_heading level0 col2\\\" >constructorStandings</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col3\\\" class=\\\"col_heading level0 col3\\\" >constructors</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col4\\\" class=\\\"col_heading level0 col4\\\" >driverStandings</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col5\\\" class=\\\"col_heading level0 col5\\\" >drivers</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col6\\\" class=\\\"col_heading level0 col6\\\" >lapTimes</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col7\\\" class=\\\"col_heading level0 col7\\\" >pitStops</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col8\\\" class=\\\"col_heading level0 col8\\\" >qualifying</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col9\\\" class=\\\"col_heading level0 col9\\\" >races</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col10\\\" class=\\\"col_heading level0 col10\\\" >results</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col11\\\" class=\\\"col_heading level0 col11\\\" >seasons</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col12\\\" class=\\\"col_heading level0 col12\\\" >status</th>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th class=\\\"index_name level0\\\" >Role</th>\\n\",\n \" <th class=\\\"blank col0\\\" > </th>\\n\",\n \" <th class=\\\"blank col1\\\" > </th>\\n\",\n \" <th class=\\\"blank col2\\\" > </th>\\n\",\n \" <th class=\\\"blank col3\\\" > </th>\\n\",\n \" <th class=\\\"blank col4\\\" > </th>\\n\",\n \" <th class=\\\"blank col5\\\" > </th>\\n\",\n \" <th class=\\\"blank col6\\\" > </th>\\n\",\n \" <th class=\\\"blank col7\\\" > </th>\\n\",\n \" <th class=\\\"blank col8\\\" > </th>\\n\",\n \" <th class=\\\"blank col9\\\" > </th>\\n\",\n \" <th class=\\\"blank col10\\\" > </th>\\n\",\n \" <th class=\\\"blank col11\\\" > </th>\\n\",\n \" <th class=\\\"blank col12\\\" > </th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row0\\\" class=\\\"row_heading level0 row0\\\" >DriverManager</th>\\n\",\n \" <td id=\\\"T_3c946_row0_col0\\\" class=\\\"data row0 col0\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col1\\\" class=\\\"data row0 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col2\\\" class=\\\"data row0 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col3\\\" class=\\\"data row0 col3\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col4\\\" class=\\\"data row0 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col5\\\" class=\\\"data row0 col5\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col6\\\" class=\\\"data row0 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col7\\\" class=\\\"data row0 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col8\\\" class=\\\"data row0 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col9\\\" class=\\\"data row0 col9\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col10\\\" class=\\\"data row0 col10\\\" >11.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col11\\\" class=\\\"data row0 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col12\\\" class=\\\"data row0 col12\\\" >0.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row1\\\" class=\\\"row_heading level0 row1\\\" >MediaCoordinator</th>\\n\",\n \" <td id=\\\"T_3c946_row1_col0\\\" class=\\\"data row1 col0\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col1\\\" class=\\\"data row1 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col2\\\" class=\\\"data row1 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col3\\\" class=\\\"data row1 col3\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col4\\\" class=\\\"data row1 col4\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col5\\\" class=\\\"data row1 col5\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col6\\\" class=\\\"data row1 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col7\\\" class=\\\"data row1 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col8\\\" class=\\\"data row1 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col9\\\" class=\\\"data row1 col9\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col10\\\" class=\\\"data row1 col10\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col11\\\" class=\\\"data row1 col11\\\" >2.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col12\\\" class=\\\"data row1 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row2\\\" class=\\\"row_heading level0 row2\\\" >RaceAnalyst</th>\\n\",\n \" <td id=\\\"T_3c946_row2_col0\\\" class=\\\"data row2 col0\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col1\\\" class=\\\"data row2 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col2\\\" class=\\\"data row2 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col3\\\" class=\\\"data row2 col3\\\" >4.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col4\\\" class=\\\"data row2 col4\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col5\\\" class=\\\"data row2 col5\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col6\\\" class=\\\"data row2 col6\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col7\\\" class=\\\"data row2 col7\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col8\\\" class=\\\"data row2 col8\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col9\\\" class=\\\"data row2 col9\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col10\\\" class=\\\"data row2 col10\\\" >17.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col11\\\" class=\\\"data row2 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col12\\\" class=\\\"data row2 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row3\\\" class=\\\"row_heading level0 row3\\\" >StandingsManager</th>\\n\",\n \" <td id=\\\"T_3c946_row3_col0\\\" class=\\\"data row3 col0\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col1\\\" class=\\\"data row3 col1\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col2\\\" class=\\\"data row3 col2\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col3\\\" class=\\\"data row3 col3\\\" >4.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col4\\\" class=\\\"data row3 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col5\\\" class=\\\"data row3 col5\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col6\\\" class=\\\"data row3 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col7\\\" class=\\\"data row3 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col8\\\" class=\\\"data row3 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col9\\\" class=\\\"data row3 col9\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col10\\\" class=\\\"data row3 col10\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col11\\\" class=\\\"data row3 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col12\\\" class=\\\"data row3 col12\\\" >0.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row4\\\" class=\\\"row_heading level0 row4\\\" >SystemManager</th>\\n\",\n \" <td id=\\\"T_3c946_row4_col0\\\" class=\\\"data row4 col0\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col1\\\" class=\\\"data row4 col1\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col2\\\" class=\\\"data row4 col2\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col3\\\" class=\\\"data row4 col3\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col4\\\" class=\\\"data row4 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col5\\\" class=\\\"data row4 col5\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col6\\\" class=\\\"data row4 col6\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col7\\\" class=\\\"data row4 col7\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col8\\\" class=\\\"data row4 col8\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col9\\\" class=\\\"data row4 col9\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col10\\\" class=\\\"data row4 col10\\\" >18.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col11\\\" class=\\\"data row4 col11\\\" >2.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col12\\\" class=\\\"data row4 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\"\n ],\n \"text/plain\": [\n \"<pandas.io.formats.style.Styler at 0x7f5045bdae00>\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"[SUMMARY] 1 database(s) selected: ['formula_1']\\n\",\n \"[NEXT] Edit hints in Cell 6.2, then run Cell 6.3\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.1 Select Database(s) & View Current State\\n\",\n \"# Supports single db (str) or multiple dbs (list)\\n\",\n \"\\n\",\n \"TARGET_DBS = [\\\"formula_1\\\"] # Single: \\\"toxicology\\\" or Multiple: [\\\"toxicology\\\", \\\"california_schools\\\"]\\n\",\n \"\\n\",\n \"# Normalize to list\\n\",\n \"if isinstance(TARGET_DBS, str):\\n\",\n \" TARGET_DBS = [TARGET_DBS]\\n\",\n \"\\n\",\n \"# Load current role assignments\\n\",\n \"with open(ROLE_ASSIGNMENTS_PATH, 'r') as f:\\n\",\n \" all_assignments = json.load(f)\\n\",\n \"\\n\",\n \"available_dbs = set(all_assignments.get('assignments', {}).keys())\\n\",\n \"valid_target_dbs = [db for db in TARGET_DBS if db in available_dbs]\\n\",\n \"invalid_dbs = [db for db in TARGET_DBS if db not in available_dbs]\\n\",\n \"\\n\",\n \"if invalid_dbs:\\n\",\n \" print(f\\\"[WARN] Databases not found: {invalid_dbs}\\\")\\n\",\n \" print(f\\\"Available: {sorted(available_dbs)}\\\\n\\\")\\n\",\n \"\\n\",\n \"# Store schemas for later use\\n\",\n \"target_schemas = {}\\n\",\n \"\\n\",\n \"for TARGET_DB in valid_target_dbs:\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"Database: {TARGET_DB}\\\")\\n\",\n \" print(f\\\"{'='*70}\\\")\\n\",\n \" \\n\",\n \" # --- Schema Summary ---\\n\",\n \" db_path = bird_db_dir / TARGET_DB / f\\\"{TARGET_DB}.sqlite\\\"\\n\",\n \" if db_path.exists():\\n\",\n \" target_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" target_schemas[TARGET_DB] = target_schema\\n\",\n \" \\n\",\n \" schema_rows = []\\n\",\n \" for table, info in target_schema.items():\\n\",\n \" for col in info['columns']:\\n\",\n \" schema_rows.append({\\n\",\n \" \\\"Table\\\": table,\\n\",\n \" \\\"Column\\\": col['name'],\\n\",\n \" \\\"Type\\\": col['type'],\\n\",\n \" \\\"PK\\\": \\\"Yes\\\" if col['pk'] else \\\"\\\",\\n\",\n \" \\\"FK\\\": col.get('fk', '') or \\\"\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" schema_df = pd.DataFrame(schema_rows)\\n\",\n \" print(f\\\"\\\\n=== Schema ({len(target_schema)} tables, {len(schema_df)} columns) ===\\\")\\n\",\n \" display(schema_df)\\n\",\n \" else:\\n\",\n \" print(f\\\"[ERROR] Database not found: {db_path}\\\")\\n\",\n \" target_schemas[TARGET_DB] = None\\n\",\n \" continue\\n\",\n \"\\n\",\n \" # --- Current Roles ---\\n\",\n \" if TARGET_DB in all_assignments.get('assignments', {}):\\n\",\n \" current_roles = all_assignments['assignments'][TARGET_DB]\\n\",\n \" \\n\",\n \" # Set display options for full content\\n\",\n \" pd.set_option('display.max_colwidth', None)\\n\",\n \" \\n\",\n \" roles_rows = []\\n\",\n \" for role_info in current_roles:\\n\",\n \" policy = role_info.get('policy', {})\\n\",\n \" total_access = count_policy_columns(policy, target_schema)\\n\",\n \" has_wildcard = has_wildcard_policy(policy)\\n\",\n \" cols_display = f\\\"{total_access}\\\" + (\\\" (*)\\\" if has_wildcard else \\\"\\\")\\n\",\n \" # Full policy string\\n\",\n \" policy_str = \\\"; \\\".join([\\n\",\n \" f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" \\n\",\n \" for t, c in policy.items()\\n\",\n \" ])\\n\",\n \" roles_rows.append({\\n\",\n \" \\\"Role\\\": role_info['role'],\\n\",\n \" \\\"Description\\\": role_info.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Columns\\\": cols_display,\\n\",\n \" \\\"Policy\\\": policy_str\\n\",\n \" })\\n\",\n \" \\n\",\n \" roles_df = pd.DataFrame(roles_rows)\\n\",\n \" print(f\\\"\\\\n=== Current Roles ({len(current_roles)}) ===\\\")\\n\",\n \" display(roles_df)\\n\",\n \" \\n\",\n \" pd.reset_option('display.max_colwidth')\\n\",\n \" \\n\",\n \" # Policy detail matrix\\n\",\n \" policy_rows = []\\n\",\n \" for role_info in current_roles:\\n\",\n \" policy = role_info.get('policy', {})\\n\",\n \" for table, cols in policy.items():\\n\",\n \" num_cols = count_policy_columns({table: cols}, target_schema)\\n\",\n \" policy_rows.append({\\n\",\n \" \\\"Role\\\": role_info['role'],\\n\",\n \" \\\"Table\\\": table,\\n\",\n \" \\\"NumCols\\\": num_cols\\n\",\n \" })\\n\",\n \" \\n\",\n \" if policy_rows:\\n\",\n \" policy_df = pd.DataFrame(policy_rows)\\n\",\n \" pivot = policy_df.pivot_table(index='Role', columns='Table', values='NumCols', fill_value=0)\\n\",\n \" print(\\\"\\\\n=== Role x Table Matrix (column counts, * expanded) ===\\\")\\n\",\n \" display(pivot.style.background_gradient(cmap='Blues', axis=None))\\n\",\n \" \\n\",\n \" # Quality issues\\n\",\n \" if 'report' in dir() and TARGET_DB in report.all_issues:\\n\",\n \" print(f\\\"\\\\n=== Quality Issues ===\\\")\\n\",\n \" issues = report.all_issues[TARGET_DB]\\n\",\n \" if TARGET_DB in report.deny_rate_metrics:\\n\",\n \" m = report.deny_rate_metrics[TARGET_DB]\\n\",\n \" issues_df = pd.DataFrame([{\\n\",\n \" \\\"Deny Rate\\\": f\\\"{m.deny_rate:.1f}%\\\",\\n\",\n \" \\\"All-Allow %\\\": f\\\"{m.all_allow_pct:.1f}%\\\",\\n\",\n \" \\\"Issues\\\": \\\"; \\\".join(issues)\\n\",\n \" }])\\n\",\n \" display(issues_df)\\n\",\n \" else:\\n\",\n \" print(f\\\"[ERROR] No roles found for {TARGET_DB}\\\")\\n\",\n \"\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"[SUMMARY] {len(valid_target_dbs)} database(s) selected: {valid_target_dbs}\\\")\\n\",\n \" print(\\\"[NEXT] Edit hints in Cell 6.2, then run Cell 6.3\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[INFO] No databases selected. Edit TARGET_DBS above and re-run.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 34,\n \"id\": \"f8416bc4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Applying shared hint to 1 database(s) ===\\n\",\n \"\\n\",\n \" ✓ formula_1: 13 tables, 94 columns\\n\",\n \"\\n\",\n \"[NEXT] Run Cell 6.3 to regenerate all 1 database(s)\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.2 Write Regeneration Hints (Human-in-the-Loop)\\n\",\n \"# Write ONE shared hint for all selected databases (they have similar issues)\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EDIT HERE: Shared problem description for all selected DBs\\n\",\n \"# Supports {db}, {num_tables}, {num_cols} placeholders\\n\",\n \"# ============================================================\\n\",\n \"# PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"# [PROBLEM]\\n\",\n \"# Low deny rate - almost all queries are allowed by all roles (roles too permissive)\\n\",\n \"# Low diversity - all roles have similar access patterns (coverage_std too low)\\n\",\n \"\\n\",\n \"\\n\",\n \"# [ROOT CAUSE]\\n\",\n \"# - Most roles can access most/all columns, so very few queries get denied\\n\",\n \"# - This defeats the purpose of RBAC - no meaningful access control\\n\",\n \"\\n\",\n \"# [DIRECTION]\\n\",\n \"# - Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"# - Create restrictive roles that only access specific subsets of data\\n\",\n \"# - Each role should have a clear, limited scope\\n\",\n \"# - Some roles should be restricted to fewer tables\\n\",\n \"# - Do not frequently giving non-admin roles \\\"*\\\" (all columns) access\\n\",\n \"# - Create clear functional boundaries between roles\\n\",\n \"# \\\"\\\"\\\"\\n\",\n \"\\n\",\n \"# All-denied cases - even SystemManager role cannot access some columns\\n\",\n \"# - Ensure SystemManager role has full access to all tables and columns\\n\",\n \"\\n\",\n \"\\n\",\n \"PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"[PROBLEM]\\n\",\n \"Low semantic alignment - roles do not align well with schema\\n\",\n \"All-denied cases - even SystemManager role cannot access some columns\\n\",\n \"\\n\",\n \"[ROOT CAUSE]\\n\",\n \"- This defeats the purpose of RBAC - no meaningful access control\\n\",\n \"\\n\",\n \"[DIRECTION]\\n\",\n \"- Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"- Ensure SystemManager role has full access to all tables and columns\\n\",\n \"- Review other roles to ensure they align with specific functional areas\\n\",\n \"- Each role should have a clear, limited scope\\n\",\n \"- Create meaningful semantic role names and descriptions that reflect their access patterns\\n\",\n \"\\\"\\\"\\\"\\n\",\n \"\\n\",\n \"\\n\",\n \"# PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"# [PROBLEM]\\n\",\n \"# Low diversity - all roles have similar access patterns (coverage_std too low)\\n\",\n \"\\n\",\n \"# [ROOT CAUSE]\\n\",\n \"# - This defeats the purpose of RBAC - no meaningful access control differentiation\\n\",\n \"\\n\",\n \"# [DIRECTION]\\n\",\n \"# - Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"# - Create restrictive roles that only access specific subsets of data\\n\",\n \"# - Each role should have a clear, limited scope\\n\",\n \"# - Some roles should be restricted to fewer tables\\n\",\n \"# - Do not frequently giving non-admin roles \\\"*\\\" (all columns) access\\n\",\n \"# - Create clear functional boundaries between roles\\n\",\n \"# \\\"\\\"\\\"\\n\",\n \"\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# Auto-apply to all selected databases (no need to edit below)\\n\",\n \"# ============================================================\\n\",\n \"REGENERATION_HINTS = {}\\n\",\n \"\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"=== Applying shared hint to {len(valid_target_dbs)} database(s) ===\\\\n\\\")\\n\",\n \" for db in valid_target_dbs:\\n\",\n \" schema = target_schemas.get(db, {})\\n\",\n \" num_tables = len(schema)\\n\",\n \" num_cols = sum(len(t['columns']) for t in schema.values())\\n\",\n \" \\n\",\n \" # Fill placeholders\\n\",\n \" REGENERATION_HINTS[db] = PROBLEM_TO_FIX.format(\\n\",\n \" db=db, num_tables=num_tables, num_cols=num_cols\\n\",\n \" )\\n\",\n \" print(f\\\" ✓ {db}: {num_tables} tables, {num_cols} columns\\\")\\n\",\n \"\\n\",\n \" print(f\\\"\\\\n[NEXT] Run Cell 6.3 to regenerate all {len(valid_target_dbs)} database(s)\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[INFO] No databases selected. Run Cell 6.1 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 68,\n \"id\": \"91109a7c\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Regenerating roles for 1 database(s)...\\n\",\n \"============================================================\\n\",\n \"\\n\",\n \"[formula_1] Generating...\\n\",\n \" ✓ 5 roles (5 valid)\\n\",\n \"\\n\",\n \"============================================================\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Status</th>\\n\",\n \" <th>Roles</th>\\n\",\n \" <th>Valid</th>\\n\",\n \" <th>Tokens</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>success</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>3058</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Status Roles Valid Tokens\\n\",\n \"0 formula_1 success 5 5 3058\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"[OK] All databases regenerated. Run Cell 6.4 to preview.\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.3 Execute Regeneration with Hints (supports multiple databases)\\n\",\n \"\\n\",\n \"def regenerate_single_database(db_name: str, hint: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Regenerate roles for a single database with guidance hints.\\\"\\\"\\\"\\n\",\n \" db_path = bird_db_dir / db_name / f\\\"{db_name}.sqlite\\\"\\n\",\n \" \\n\",\n \" if not db_path.exists():\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": \\\"Database not found\\\"}\\n\",\n \" \\n\",\n \" try:\\n\",\n \" db_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" db_schema_text = format_schema_for_prompt(db_schema, db_name)\\n\",\n \" \\n\",\n \" enhanced_system_prompt = f\\\"{COLUMN_LEVEL_SYSTEM_PROMPT}\\\\n\\\\n[OPTIMIZATION GUIDANCE]\\\\n{hint}\\\"\\n\",\n \" user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=db_schema_text)\\n\",\n \" \\n\",\n \" response = oracle.query(\\n\",\n \" prompt_sys=enhanced_system_prompt,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3,\\n\",\n \" top_p=0.9,\\n\",\n \" max_completion_tokens=DEFAULT_MAX_TOKENS\\n\",\n \" )\\n\",\n \" \\n\",\n \" llm_response = response['answer']\\n\",\n \" usage = response.get('usage', {})\\n\",\n \" roles = parse_column_level_roles(llm_response)\\n\",\n \" \\n\",\n \" if not roles:\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"parse_error\\\", \\n\",\n \" \\\"error\\\": \\\"Failed to parse LLM response\\\", \\\"raw_response\\\": llm_response[:500]}\\n\",\n \" \\n\",\n \" validation = validate_roles_against_schema(roles, str(db_path))\\n\",\n \" assignments = [{\\\"role\\\": r[\\\"role\\\"], \\\"description\\\": r.get(\\\"description\\\", \\\"\\\"), \\n\",\n \" \\\"policy\\\": r.get(\\\"columns\\\", {})} for r in roles]\\n\",\n \" \\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"success\\\", \\\"roles_count\\\": len(assignments),\\n\",\n \" \\\"valid_roles\\\": validation['valid_roles'], \\\"all_valid\\\": validation['all_valid'],\\n\",\n \" \\\"assignments\\\": assignments, \\\"usage\\\": usage, \\\"raw_response\\\": llm_response}\\n\",\n \" except Exception as e:\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": str(e)}\\n\",\n \"\\n\",\n \"# Execute for all target databases\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"Regenerating roles for {len(valid_target_dbs)} database(s)...\\\")\\n\",\n \" print(\\\"=\\\"*60)\\n\",\n \"\\n\",\n \" regenerated_results = {} # Store all results\\n\",\n \" all_success = True\\n\",\n \"\\n\",\n \" for db_name in valid_target_dbs:\\n\",\n \" hint = REGENERATION_HINTS.get(db_name, PROBLEM_TO_FIX)\\n\",\n \" print(f\\\"\\\\n[{db_name}] Generating...\\\")\\n\",\n \" \\n\",\n \" result = regenerate_single_database(db_name, hint)\\n\",\n \" regenerated_results[db_name] = result\\n\",\n \" \\n\",\n \" if result[\\\"status\\\"] == \\\"success\\\":\\n\",\n \" print(f\\\" ✓ {result['roles_count']} roles ({result['valid_roles']} valid)\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" ✗ {result.get('error', result['status'])}\\\")\\n\",\n \" all_success = False\\n\",\n \"\\n\",\n \" # Summary table\\n\",\n \" print(\\\"\\\\n\\\" + \\\"=\\\"*60)\\n\",\n \" result_df = pd.DataFrame([{\\n\",\n \" \\\"Database\\\": r[\\\"database\\\"],\\n\",\n \" \\\"Status\\\": r[\\\"status\\\"],\\n\",\n \" \\\"Roles\\\": r.get(\\\"roles_count\\\", \\\"N/A\\\"),\\n\",\n \" \\\"Valid\\\": r.get(\\\"valid_roles\\\", \\\"N/A\\\"),\\n\",\n \" \\\"Tokens\\\": r.get(\\\"usage\\\", {}).get(\\\"total_tokens\\\", \\\"N/A\\\")\\n\",\n \" } for r in regenerated_results.values()])\\n\",\n \" display(result_df)\\n\",\n \"\\n\",\n \" if all_success:\\n\",\n \" print(\\\"\\\\n[OK] All databases regenerated. Run Cell 6.4 to preview.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"\\\\n[WARN] Some databases failed. Check errors above.\\\")\\n\",\n \"else:\\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"[INFO] No databases selected. Run Cell 6.1 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 69,\n \"id\": \"a2b0ba98\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"Database: formula_1\\n\",\n \"======================================================================\\n\",\n \"\\n\",\n \"=== NEW Roles (5) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Cols</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintenance and data management</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>94 (*)</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap times, pit stops, and qualifying results</td>\\n\",\n \" <td>8</td>\\n\",\n \" <td>61</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; lapTimes: [raceId, driverId, lap, position, time, milliseconds]; pitStops: [raceId, driverId, stop, lap, time, duration, milliseconds]; qualifying: [qualifyId, raceId, driverId, constructorId, number, position, q1, q2, q3]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>StandingsManager</td>\\n\",\n \" <td>Manages and monitors championship standings for drivers and constructors</td>\\n\",\n \" <td>7</td>\\n\",\n \" <td>42</td>\\n\",\n \" <td>constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, name, date]; constructorResults: [constructorResultsId, raceId, constructorId, points, status]; constructorStandings: [constructorStandingsId, raceId, constructorId, points, position, positionText, wins]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, points, position, positionText, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>PublicRelations</td>\\n\",\n \" <td>Accesses public information about circuits, drivers, constructors, and race schedules</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>31</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [year, url]; races: [raceId, year, round, circuitId, name, date, url]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal details including date of birth</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>32</td>\\n\",\n \" <td>drivers: [driverId, driverRef, number, code, forename, surname, dob, nationality]; races: [raceId, year, round, name, date]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionText, positionOrder, points, laps, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Role \\\\\\n\",\n \"0 SystemManager \\n\",\n \"1 RaceAnalyst \\n\",\n \"2 StandingsManager \\n\",\n \"3 PublicRelations \\n\",\n \"4 DriverManager \\n\",\n \"\\n\",\n \" Description \\\\\\n\",\n \"0 Full administrative access for system maintenance and data management \\n\",\n \"1 Analyzes race performance data including lap times, pit stops, and qualifying results \\n\",\n \"2 Manages and monitors championship standings for drivers and constructors \\n\",\n \"3 Accesses public information about circuits, drivers, constructors, and race schedules \\n\",\n \"4 Manages driver information and personal details including date of birth \\n\",\n \"\\n\",\n \" Tables Cols \\\\\\n\",\n \"0 13 94 (*) \\n\",\n \"1 8 61 \\n\",\n \"2 7 42 \\n\",\n \"3 6 31 \\n\",\n \"4 4 32 \\n\",\n \"\\n\",\n \" Policy \\n\",\n \"0 circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*] \\n\",\n \"1 circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; lapTimes: [raceId, driverId, lap, position, time, milliseconds]; pitStops: [raceId, driverId, stop, lap, time, duration, milliseconds]; qualifying: [qualifyId, raceId, driverId, constructorId, number, position, q1, q2, q3]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId] \\n\",\n \"2 constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, name, date]; constructorResults: [constructorResultsId, raceId, constructorId, points, status]; constructorStandings: [constructorStandingsId, raceId, constructorId, points, position, positionText, wins]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, points, position, positionText, statusId] \\n\",\n \"3 circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [year, url]; races: [raceId, year, round, circuitId, name, date, url]; status: [statusId, status] \\n\",\n \"4 drivers: [driverId, driverRef, number, code, forename, surname, dob, nationality]; races: [raceId, year, round, name, date]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionText, positionOrder, points, laps, statusId] \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Quality Comparison ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Metric</th>\\n\",\n \" <th>OLD</th>\\n\",\n \" <th>NEW</th>\\n\",\n \" <th>Threshold</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>Roles</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>NaN</td>\\n\",\n \" <td>NaN</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>Avg Coverage</td>\\n\",\n \" <td>65.5%</td>\\n\",\n \" <td>55.3%</td>\\n\",\n \" <td><85%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>Coverage Std</td>\\n\",\n \" <td>0.183</td>\\n\",\n \" <td>0.251</td>\\n\",\n \" <td>>0.065</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>Max Overlap</td>\\n\",\n \" <td>87.3%</td>\\n\",\n \" <td>57.4%</td>\\n\",\n \" <td><80%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Metric OLD NEW Threshold Status\\n\",\n \"0 Roles 5 5 NaN NaN\\n\",\n \"1 Avg Coverage 65.5% 55.3% <85% OK\\n\",\n \"2 Coverage Std 0.183 0.251 >0.065 OK\\n\",\n \"3 Max Overlap 87.3% 57.4% <80% OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[OK] formula_1: All quality checks passed\\n\",\n \"\\n\",\n \"======================================================================\\n\",\n \"[DECISION] Satisfied? Run Cell 6.5 to apply. Otherwise edit Cell 6.2 and re-run 6.3.\\n\",\n \"[TIP] Use Cell 6.4.1 to select specific roles from OLD/NEW\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.4 Preview Regenerated Roles (Compare OLD vs NEW + Quality Check)\\n\",\n \"# Supports multiple databases\\n\",\n \"import numpy as np\\n\",\n \"from src.role_evaluator.quality_metrics import RoleQualityEvaluator\\n\",\n \"\\n\",\n \"pd.set_option('display.max_colwidth', None)\\n\",\n \"pd.set_option('display.max_rows', None)\\n\",\n \"pd.set_option('display.width', None)\\n\",\n \"\\n\",\n \"if 'regenerated_results' in dir() and regenerated_results:\\n\",\n \" for db_name, regen_result in regenerated_results.items():\\n\",\n \" if regen_result[\\\"status\\\"] != \\\"success\\\":\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"[SKIP] {db_name}: {regen_result.get('error', 'failed')}\\\")\\n\",\n \" continue\\n\",\n \" \\n\",\n \" regenerated_assignments = regen_result[\\\"assignments\\\"]\\n\",\n \" target_schema = target_schemas.get(db_name)\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"Database: {db_name}\\\")\\n\",\n \" print(f\\\"{'='*70}\\\")\\n\",\n \" \\n\",\n \" # --- NEW Role Summary ---\\n\",\n \" new_summary = []\\n\",\n \" for r in regenerated_assignments:\\n\",\n \" policy = r.get('policy', {})\\n\",\n \" total_cols = count_policy_columns(policy, target_schema)\\n\",\n \" has_wc = has_wildcard_policy(policy)\\n\",\n \" cols_display = f\\\"{total_cols}\\\" + (\\\" (*)\\\" if has_wc else \\\"\\\")\\n\",\n \" policy_str = \\\"; \\\".join([\\n\",\n \" f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" \\n\",\n \" for t, c in policy.items()\\n\",\n \" ])\\n\",\n \" new_summary.append({\\n\",\n \" \\\"Role\\\": r['role'],\\n\",\n \" \\\"Description\\\": r.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Cols\\\": cols_display,\\n\",\n \" \\\"Policy\\\": policy_str\\n\",\n \" })\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n=== NEW Roles ({len(regenerated_assignments)}) ===\\\")\\n\",\n \" display(pd.DataFrame(new_summary))\\n\",\n \" \\n\",\n \" # --- Quality Metrics ---\\n\",\n \" if target_schema:\\n\",\n \" total_schema_cols = sum(len(t['columns']) for t in target_schema.values())\\n\",\n \" else:\\n\",\n \" total_schema_cols = 10\\n\",\n \" \\n\",\n \" # NEW metrics\\n\",\n \" new_coverages = [count_policy_columns(r.get('policy', {}), target_schema) / total_schema_cols \\n\",\n \" for r in regenerated_assignments]\\n\",\n \" new_avg_cov = np.mean(new_coverages)\\n\",\n \" new_cov_std = np.std(new_coverages)\\n\",\n \" \\n\",\n \" new_max_overlap = 0.0\\n\",\n \" non_admin_new = [r for r in regenerated_assignments if 'admin' not in r['role'].lower()]\\n\",\n \" for i, r1 in enumerate(non_admin_new):\\n\",\n \" set1 = RoleQualityEvaluator.policy_to_column_set(r1.get('policy', {}))\\n\",\n \" for r2 in non_admin_new[i+1:]:\\n\",\n \" set2 = RoleQualityEvaluator.policy_to_column_set(r2.get('policy', {}))\\n\",\n \" new_max_overlap = max(new_max_overlap, RoleQualityEvaluator.jaccard_similarity(set1, set2))\\n\",\n \" \\n\",\n \" # OLD metrics\\n\",\n \" old_roles = all_assignments.get('assignments', {}).get(db_name, [])\\n\",\n \" if old_roles:\\n\",\n \" old_coverages = [count_policy_columns(r.get('policy', {}), target_schema) / total_schema_cols \\n\",\n \" for r in old_roles]\\n\",\n \" old_avg_cov = np.mean(old_coverages)\\n\",\n \" old_cov_std = np.std(old_coverages)\\n\",\n \" \\n\",\n \" old_max_overlap = 0.0\\n\",\n \" non_admin_old = [r for r in old_roles if 'admin' not in r['role'].lower()]\\n\",\n \" for i, r1 in enumerate(non_admin_old):\\n\",\n \" set1 = RoleQualityEvaluator.policy_to_column_set(r1.get('policy', {}))\\n\",\n \" for r2 in non_admin_old[i+1:]:\\n\",\n \" set2 = RoleQualityEvaluator.policy_to_column_set(r2.get('policy', {}))\\n\",\n \" old_max_overlap = max(old_max_overlap, RoleQualityEvaluator.jaccard_similarity(set1, set2))\\n\",\n \" else:\\n\",\n \" old_avg_cov, old_cov_std, old_max_overlap = 0, 0, 0\\n\",\n \" \\n\",\n \" # Comparison table (using QUALITY_THRESHOLDS from Cell 5.1)\\n\",\n \" def status(metric, val):\\n\",\n \" if metric == \\\"Coverage\\\": \\n\",\n \" return \\\"WARN\\\" if val > QUALITY_THRESHOLDS.get(\\\"coverage_max\\\", 0.90) else \\\"OK\\\"\\n\",\n \" if metric == \\\"CovStd\\\": \\n\",\n \" return \\\"WARN\\\" if val < QUALITY_THRESHOLDS.get(\\\"coverage_std_min\\\", 0.08) else \\\"OK\\\"\\n\",\n \" if metric == \\\"MaxOverlap\\\": \\n\",\n \" return \\\"WARN\\\" if val > QUALITY_THRESHOLDS.get(\\\"max_overlap\\\", 0.80) else \\\"OK\\\"\\n\",\n \" return \\\"OK\\\"\\n\",\n \" \\n\",\n \" quality_df = pd.DataFrame([\\n\",\n \" {\\\"Metric\\\": \\\"Roles\\\", \\\"OLD\\\": len(old_roles), \\\"NEW\\\": len(regenerated_assignments)},\\n\",\n \" {\\\"Metric\\\": \\\"Avg Coverage\\\", \\\"OLD\\\": f\\\"{old_avg_cov:.1%}\\\", \\\"NEW\\\": f\\\"{new_avg_cov:.1%}\\\", \\n\",\n \" \\\"Threshold\\\": f\\\"<{QUALITY_THRESHOLDS.get('coverage_max', 0.90):.0%}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"Coverage\\\", new_avg_cov)},\\n\",\n \" {\\\"Metric\\\": \\\"Coverage Std\\\", \\\"OLD\\\": f\\\"{old_cov_std:.3f}\\\", \\\"NEW\\\": f\\\"{new_cov_std:.3f}\\\",\\n\",\n \" \\\"Threshold\\\": f\\\">{QUALITY_THRESHOLDS.get('coverage_std_min', 0.08)}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"CovStd\\\", new_cov_std)},\\n\",\n \" {\\\"Metric\\\": \\\"Max Overlap\\\", \\\"OLD\\\": f\\\"{old_max_overlap:.1%}\\\", \\\"NEW\\\": f\\\"{new_max_overlap:.1%}\\\",\\n\",\n \" \\\"Threshold\\\": f\\\"<{QUALITY_THRESHOLDS.get('max_overlap', 0.80):.0%}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"MaxOverlap\\\", new_max_overlap)},\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Quality Comparison ===\\\")\\n\",\n \" display(quality_df)\\n\",\n \" \\n\",\n \" warn_count = quality_df['Status'].eq('WARN').sum() if 'Status' in quality_df.columns else 0\\n\",\n \" if warn_count == 0:\\n\",\n \" print(f\\\"[OK] {db_name}: All quality checks passed\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\"[WARN] {db_name}: {warn_count} issue(s)\\\")\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(\\\"[DECISION] Satisfied? Run Cell 6.5 to apply. Otherwise edit Cell 6.2 and re-run 6.3.\\\")\\n\",\n \" print(\\\"[TIP] Use Cell 6.4.1 to select specific roles from OLD/NEW\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[WARN] No regenerated results. Run Cell 6.3 first.\\\")\\n\",\n \"\\n\",\n \"pd.reset_option('display.max_colwidth')\\n\",\n \"pd.reset_option('display.max_rows')\\n\",\n \"pd.reset_option('display.width')\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 70,\n \"id\": \"f003a014\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Role Selection Summary ===\\n\",\n \"\\n\",\n \"============================================================\\n\",\n \"Database: formula_1\\n\",\n \" From OLD (0): (none)\\n\",\n \" From NEW (4): ['SystemManager', 'RaceAnalyst', 'PublicRelations', 'DriverManager']\\n\",\n \" Combined: 4 roles\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Source</th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintena...</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap t...</td>\\n\",\n \" <td>8</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country,...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>PublicRelations</td>\\n\",\n \" <td>Accesses public information about circuits, dr...</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country,...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal detail...</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>drivers: [driverId, driverRef, number, code, f...</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Source Role Description \\\\\\n\",\n \"0 NEW SystemManager Full administrative access for system maintena... \\n\",\n \"1 NEW RaceAnalyst Analyzes race performance data including lap t... \\n\",\n \"2 NEW PublicRelations Accesses public information about circuits, dr... \\n\",\n \"3 NEW DriverManager Manages driver information and personal detail... \\n\",\n \"\\n\",\n \" Tables Policy \\n\",\n \"0 13 circuits: [*]; constructors: [*]; drivers: [*]... \\n\",\n \"1 8 circuits: [circuitId, name, location, country,... \\n\",\n \"2 6 circuits: [circuitId, name, location, country,... \\n\",\n \"3 4 drivers: [driverId, driverRef, number, code, f... \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"============================================================\\n\",\n \"[NEXT] Run Cell 6.5 to apply combined role sets\\n\",\n \"[TIP] Edit ROLE_SELECTION above to customize, then re-run this cell\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.4.1 Select & Combine Roles from OLD and NEW\\n\",\n \"# Human expert picks best roles from both versions to create final role set\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EDIT HERE: Specify which roles to keep from each version\\n\",\n \"# Format: {\\\"db_name\\\": {\\\"old\\\": [role_names], \\\"new\\\": [role_names]}}\\n\",\n \"# Leave empty [] to skip a version, or use [\\\"*\\\"] to keep all from that version\\n\",\n \"# ============================================================\\n\",\n \"ROLE_SELECTION = {\\n\",\n \" \\\"formula_1\\\": {\\n\",\n \" \\\"old\\\": [], # e.g., [\\\"DataAnalyst\\\", \\\"Researcher\\\"]\\n\",\n \" \\\"new\\\": [\\\"SystemManager\\\", \\\"RaceAnalyst\\\", \\\"PublicRelations\\\", \\\"DriverManager\\\"], # keep all new roles\\n\",\n \" },\\n\",\n \" # \\\"financial\\\": {\\n\",\n \" # \\\"old\\\": [],\\n\",\n \" # \\\"new\\\": [\\\"*\\\"],\\n\",\n \" # },\\n\",\n \"}\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# Process selections and create combined role sets\\n\",\n \"# ============================================================\\n\",\n \"combined_results = {}\\n\",\n \"\\n\",\n \"if 'regenerated_results' in dir() and regenerated_results:\\n\",\n \" print(\\\"=== Role Selection Summary ===\\\\n\\\")\\n\",\n \" \\n\",\n \" for db_name in valid_target_dbs:\\n\",\n \" selection = ROLE_SELECTION.get(db_name, {\\\"old\\\": [], \\\"new\\\": [\\\"*\\\"]})\\n\",\n \" old_keep = selection.get(\\\"old\\\", [])\\n\",\n \" new_keep = selection.get(\\\"new\\\", [\\\"*\\\"])\\n\",\n \" \\n\",\n \" # Get OLD roles\\n\",\n \" old_roles = all_assignments.get('assignments', {}).get(db_name, [])\\n\",\n \" old_role_dict = {r['role']: r for r in old_roles}\\n\",\n \" \\n\",\n \" # Get NEW roles\\n\",\n \" regen_result = regenerated_results.get(db_name, {})\\n\",\n \" if regen_result.get(\\\"status\\\") != \\\"success\\\":\\n\",\n \" print(f\\\"[SKIP] {db_name}: regeneration failed\\\")\\n\",\n \" continue\\n\",\n \" new_roles = regen_result[\\\"assignments\\\"]\\n\",\n \" new_role_dict = {r['role']: r for r in new_roles}\\n\",\n \" \\n\",\n \" # Build combined role set\\n\",\n \" combined_roles = []\\n\",\n \" selected_from_old = []\\n\",\n \" selected_from_new = []\\n\",\n \" \\n\",\n \" # Select from OLD\\n\",\n \" if old_keep == [\\\"*\\\"]:\\n\",\n \" combined_roles.extend(old_roles)\\n\",\n \" selected_from_old = [r['role'] for r in old_roles]\\n\",\n \" else:\\n\",\n \" for role_name in old_keep:\\n\",\n \" if role_name in old_role_dict:\\n\",\n \" combined_roles.append(old_role_dict[role_name])\\n\",\n \" selected_from_old.append(role_name)\\n\",\n \" else:\\n\",\n \" print(f\\\" [WARN] {db_name}: OLD role '{role_name}' not found\\\")\\n\",\n \" \\n\",\n \" # Select from NEW\\n\",\n \" if new_keep == [\\\"*\\\"]:\\n\",\n \" combined_roles.extend(new_roles)\\n\",\n \" selected_from_new = [r['role'] for r in new_roles]\\n\",\n \" else:\\n\",\n \" for role_name in new_keep:\\n\",\n \" if role_name in new_role_dict:\\n\",\n \" combined_roles.append(new_role_dict[role_name])\\n\",\n \" selected_from_new.append(role_name)\\n\",\n \" else:\\n\",\n \" print(f\\\" [WARN] {db_name}: NEW role '{role_name}' not found\\\")\\n\",\n \" \\n\",\n \" combined_results[db_name] = combined_roles\\n\",\n \" \\n\",\n \" # Display summary\\n\",\n \" print(f\\\"{'='*60}\\\")\\n\",\n \" print(f\\\"Database: {db_name}\\\")\\n\",\n \" print(f\\\" From OLD ({len(selected_from_old)}): {selected_from_old if selected_from_old else '(none)'}\\\")\\n\",\n \" print(f\\\" From NEW ({len(selected_from_new)}): {selected_from_new if selected_from_new else '(none)'}\\\")\\n\",\n \" print(f\\\" Combined: {len(combined_roles)} roles\\\")\\n\",\n \" \\n\",\n \" # Show combined roles table\\n\",\n \" target_schema = target_schemas.get(db_name)\\n\",\n \" combined_summary = []\\n\",\n \" for r in combined_roles:\\n\",\n \" policy = r.get('policy', {})\\n\",\n \" source = \\\"OLD\\\" if r['role'] in old_role_dict and r['role'] not in [nr['role'] for nr in new_roles] else \\\"NEW\\\"\\n\",\n \" if r['role'] in old_role_dict and r['role'] in [nr['role'] for nr in new_roles]:\\n\",\n \" source = \\\"NEW\\\" # Prefer NEW if same name exists in both\\n\",\n \" combined_summary.append({\\n\",\n \" \\\"Source\\\": source,\\n\",\n \" \\\"Role\\\": r['role'],\\n\",\n \" \\\"Description\\\": r.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Policy\\\": \\\"; \\\".join([f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" for t, c in policy.items()])\\n\",\n \" })\\n\",\n \" \\n\",\n \" display(pd.DataFrame(combined_summary))\\n\",\n \" print()\\n\",\n \" \\n\",\n \" print(\\\"=\\\"*60)\\n\",\n \" print(f\\\"[NEXT] Run Cell 6.5 to apply combined role sets\\\")\\n\",\n \" print(\\\"[TIP] Edit ROLE_SELECTION above to customize, then re-run this cell\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[WARN] No regenerated results. Run Cell 6.3 first.\\\")\\n\",\n \" print(\\\"\\\\n[HINT] Available roles to select from:\\\")\\n\",\n \" print(\\\" OLD: roles from current assignments\\\")\\n\",\n \" print(\\\" NEW: roles from regeneration\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 71,\n \"id\": \"6cb24c7d\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[MODE] Applying COMBINED role sets (from Cell 6.4.1 selection)\\n\",\n \"\\n\",\n \"=== Changes Applied ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Old Roles</th>\\n\",\n \" <th>New Roles</th>\\n\",\n \" <th>Source</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>Combined (OLD+NEW)</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Old Roles New Roles Source\\n\",\n \"0 formula_1 5 4 Combined (OLD+NEW)\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"Saved to: bird_column_level_rbac_assignments_v2_20251230.json\\n\",\n \"\\n\",\n \"[OK] State cleared. Select new database(s) to continue.\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.5 Apply Changes (Save Combined Role Sets)\\n\",\n \"# Uses combined_results from Cell 6.4.1 (selected from OLD + NEW)\\n\",\n \"from datetime import datetime\\n\",\n \"\\n\",\n \"APPLY_CHANGES = True # Set to True to confirm\\n\",\n \"\\n\",\n \"if APPLY_CHANGES:\\n\",\n \" # Prefer combined_results (from selection), fallback to regenerated_results\\n\",\n \" use_combined = 'combined_results' in dir() and combined_results\\n\",\n \" use_regenerated = 'regenerated_results' in dir() and regenerated_results\\n\",\n \" \\n\",\n \" if use_combined:\\n\",\n \" print(\\\"[MODE] Applying COMBINED role sets (from Cell 6.4.1 selection)\\\\n\\\")\\n\",\n \" applied = []\\n\",\n \" \\n\",\n \" for db_name, combined_roles in combined_results.items():\\n\",\n \" if not combined_roles:\\n\",\n \" continue\\n\",\n \" \\n\",\n \" old_count = len(all_assignments.get('assignments', {}).get(db_name, []))\\n\",\n \" all_assignments['assignments'][db_name] = combined_roles\\n\",\n \" applied.append({\\n\",\n \" \\\"Database\\\": db_name,\\n\",\n \" \\\"Old Roles\\\": old_count,\\n\",\n \" \\\"New Roles\\\": len(combined_roles),\\n\",\n \" \\\"Source\\\": \\\"Combined (OLD+NEW)\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" if applied:\\n\",\n \" OUTPUT_DIR = project_root / 'outputs'\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = OUTPUT_DIR / f'bird_column_level_rbac_assignments_v2_{today}.json'\\n\",\n \" with open(output_path, 'w') as f:\\n\",\n \" json.dump(all_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \" \\n\",\n \" print(\\\"=== Changes Applied ===\\\")\\n\",\n \" display(pd.DataFrame(applied))\\n\",\n \" print(f\\\"\\\\nSaved to: {output_path.name}\\\")\\n\",\n \" \\n\",\n \" # Clear state\\n\",\n \" combined_results = {}\\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"\\\\n[OK] State cleared. Select new database(s) to continue.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No combined role sets to apply. Run Cell 6.4.1 first.\\\")\\n\",\n \" \\n\",\n \" elif use_regenerated:\\n\",\n \" print(\\\"[MODE] Applying ALL NEW roles (no selection made)\\\\n\\\")\\n\",\n \" applied = []\\n\",\n \" \\n\",\n \" for db_name, regen_result in regenerated_results.items():\\n\",\n \" if regen_result[\\\"status\\\"] != \\\"success\\\":\\n\",\n \" continue\\n\",\n \" \\n\",\n \" old_count = len(all_assignments.get('assignments', {}).get(db_name, []))\\n\",\n \" all_assignments['assignments'][db_name] = regen_result[\\\"assignments\\\"]\\n\",\n \" applied.append({\\n\",\n \" \\\"Database\\\": db_name,\\n\",\n \" \\\"Old Roles\\\": old_count,\\n\",\n \" \\\"New Roles\\\": len(regen_result[\\\"assignments\\\"]),\\n\",\n \" \\\"Source\\\": \\\"All NEW\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" if applied:\\n\",\n \" OUTPUT_DIR = project_root / 'outputs'\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = OUTPUT_DIR / f'bird_column_level_rbac_assignments_v2_{today}.json'\\n\",\n \" with open(output_path, 'w') as f:\\n\",\n \" json.dump(all_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \" \\n\",\n \" print(\\\"=== Changes Applied ===\\\")\\n\",\n \" display(pd.DataFrame(applied))\\n\",\n \" print(f\\\"\\\\nSaved to: {output_path.name}\\\")\\n\",\n \" \\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"\\\\n[OK] State cleared. Select new database(s) to continue.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No successful regenerations to apply.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No results to apply. Run Cell 6.3 and optionally Cell 6.4.1 first.\\\")\\n\",\n \"else:\\n\",\n \" has_combined = 'combined_results' in dir() and combined_results\\n\",\n \" has_regenerated = 'regenerated_results' in dir() and regenerated_results\\n\",\n \" \\n\",\n \" if has_combined:\\n\",\n \" print(f\\\"[INFO] {len(combined_results)} database(s) with COMBINED role sets ready\\\")\\n\",\n \" print(\\\"[TIP] These are your custom selections from OLD + NEW\\\")\\n\",\n \" elif has_regenerated:\\n\",\n \" success_count = sum(1 for r in regenerated_results.values() if r[\\\"status\\\"] == \\\"success\\\")\\n\",\n \" print(f\\\"[INFO] {success_count} database(s) with ALL NEW roles ready\\\")\\n\",\n \" print(\\\"[TIP] Run Cell 6.4.1 to select specific roles from OLD/NEW\\\")\\n\",\n \" \\n\",\n \" print(\\\"\\\\n[INFO] APPLY_CHANGES is False. Set to True and re-run to apply.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"f1bd0bc2\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# 6.6 Regenerate RBAC Dataset with Updated Role Assignments\\n\",\n \"# Run this after Cell 6.5 to create the final dataset\\n\",\n \"\\n\",\n \"from datetime import datetime\\n\",\n \"import importlib\\n\",\n \"\\n\",\n \"# IMPORTANT: Reload ALL dependent modules to pick up CTE parsing fix\\n\",\n \"import src.processors.column_extractor\\n\",\n \"import src.processors.column_permission_checker\\n\",\n \"import src.processors.column_level_rbac_generator\\n\",\n \"\\n\",\n \"importlib.reload(src.processors.column_extractor)\\n\",\n \"importlib.reload(src.processors.column_permission_checker)\\n\",\n \"importlib.reload(src.processors.column_level_rbac_generator)\\n\",\n \"\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator\\n\",\n \"\\n\",\n \"# Find the updated role assignments file (v2 if exists, otherwise latest)\\n\",\n \"v2_files = sorted(glob.glob(str(project_root / 'outputs' / 'bird_column_level_rbac_assignments_v2_*.json')))\\n\",\n \"if v2_files:\\n\",\n \" UPDATED_ROLE_PATH = v2_files[-1]\\n\",\n \"else:\\n\",\n \" role_files = sorted(glob.glob(str(project_root / 'outputs' / 'column_level_role_assignments_bird_*.json')))\\n\",\n \" UPDATED_ROLE_PATH = role_files[-1] if role_files else None\\n\",\n \"\\n\",\n \"if UPDATED_ROLE_PATH:\\n\",\n \" print(f\\\"=== Regenerating RBAC Dataset ===\\\")\\n\",\n \" print(f\\\"Role assignments: {Path(UPDATED_ROLE_PATH).name}\\\")\\n\",\n \" print(f\\\"BIRD data: {BIRD_DATA_PATH}\\\")\\n\",\n \" \\n\",\n \" # Initialize generator with updated roles\\n\",\n \" generator = ColumnLevelRBACGenerator(\\n\",\n \" role_assignments_path=UPDATED_ROLE_PATH,\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH),\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" project_root=str(project_root),\\n\",\n \" seed=42\\n\",\n \" )\\n\",\n \" \\n\",\n \" # Generate dataset\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = project_root / 'outputs' / f'column_level_rbac_dataset_bird_{today}.json'\\n\",\n \" \\n\",\n \" full_stats = generator.generate_and_save(\\n\",\n \" output_path=str(output_path),\\n\",\n \" roles_per_db=None,\\n\",\n \" queries_per_role=None\\n\",\n \" )\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n✓ Dataset saved to: {output_path.name}\\\")\\n\",\n \" print(f\\\"\\\\n=== Dataset Statistics ===\\\")\\n\",\n \" for k, v in full_stats.items():\\n\",\n \" if k == 'by_database':\\n\",\n \" print(f\\\"\\\\nPer-database distribution:\\\")\\n\",\n \" for db_id, counts in sorted(v.items()):\\n\",\n \" print(f\\\" {db_id}: allowed={counts['allowed']}, denied={counts['denied']}\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[ERROR] No role assignments file found. Run Cell 6.5 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"a9345c97\",\n \"metadata\": {},\n \"source\": [\n \"## 7. Summary\\n\",\n \"\\n\",\n \"### Generated Files\\n\",\n \"- `outputs/column_level_role_assignments_bird_YYYYMMDD.json` - Role assignments with column-level policies\\n\",\n \"- `outputs/bird_column_level_rbac_assignments_v2_YYYYMMDD.json` - Updated role assignments after regeneration\\n\",\n \"- `outputs/column_level_rbac_dataset_bird_YYYYMMDD.json` - Column-Level RBAC dataset\\n\",\n \"\\n\",\n \"### Workflow\\n\",\n \"\\n\",\n \"| Section | Description |\\n\",\n \"|---------|-------------|\\n\",\n \"| **3** | Batch process all BIRD databases to generate initial roles |\\n\",\n \"| **4** | Generate RBAC dataset from role assignments |\\n\",\n \"| **5** | Quality assessment with configurable thresholds |\\n\",\n \"| **6.1** | Select problematic database(s) and view current state |\\n\",\n \"| **6.2** | Write regeneration hints with problem description |\\n\",\n \"| **6.3** | Execute LLM regeneration with hints |\\n\",\n \"| **6.4** | Preview OLD vs NEW roles with quality comparison |\\n\",\n \"| **6.4.1** | Select & combine best roles from OLD and NEW |\\n\",\n \"| **6.5** | Apply changes (save updated role assignments) |\\n\",\n \"| **6.6** | Regenerate RBAC dataset with updated roles |\\n\",\n \"\\n\",\n \"### Quality Assessment Module (Section 5)\\n\",\n \"\\n\",\n \"```python\\n\",\n \"from src.role_evaluator import RoleQualityEvaluator, RoleQualityVisualizer\\n\",\n \"\\n\",\n \"evaluator = RoleQualityEvaluator(\\n\",\n \" role_assignments_path=\\\"outputs/column_level_role_assignments_bird.json\\\",\\n\",\n \" dataset_path=\\\"outputs/column_level_rbac_dataset_bird.json\\\",\\n\",\n \" schema_dir=\\\"data/Bird/dev_20251106/dev_databases\\\",\\n\",\n \" embed_model=\\\"BAAI/bge-small-en-v1.5\\\" # or \\\"text-embedding-3-small\\\"\\n\",\n \")\\n\",\n \"\\n\",\n \"report = evaluator.evaluate_all()\\n\",\n \"visualizer = RoleQualityVisualizer(evaluator=evaluator, report=report)\\n\",\n \"visualizer.plot_quality_dashboard()\\n\",\n \"```\\n\",\n \"\\n\",\n \"### Quality Thresholds (Configurable)\\n\",\n \"\\n\",\n \"| Metric | Default | Description |\\n\",\n \"|--------|---------|-------------|\\n\",\n \"| `deny_rate_min` | 5% | Warn if deny rate below (roles too permissive) |\\n\",\n \"| `coverage_max` | 90% | Warn if avg coverage above (roles too broad) |\\n\",\n \"| `coverage_std_min` | 0.08 | Warn if coverage std below (low diversity) |\\n\",\n \"| `max_overlap` | 80% | Warn if max overlap above (redundant roles) |\\n\",\n \"\\n\",\n \"### External Modules\\n\",\n \"\\n\",\n \"1. **`src/role_evaluator/quality_metrics.py`** - Quality metrics computation\\n\",\n \" - `RoleQualityEvaluator`: Comprehensive evaluator\\n\",\n \" - `DenyRateMetrics`, `SemanticMetrics`, `OverlapMetrics`: Data classes\\n\",\n \" - `print_quality_report()`: Formatted report output\\n\",\n \"\\n\",\n \"2. **`src/role_evaluator/quality_visualizer.py`** - Visualization suite\\n\",\n \" - `plot_deny_rate_distribution()`: Permission distribution chart\\n\",\n \" - `plot_semantic_alignment()`: Semantic alignment scatter plot\\n\",\n \" - `plot_policy_overlap_bars()`: Policy overlap bar chart\\n\",\n \" - `plot_quality_dashboard()`: Comprehensive dashboard\\n\",\n \"\\n\",\n \"3. **`src/processors/column_level_rbac_generator.py`** - Dataset generation\\n\",\n \" - `ColumnLevelRBACGenerator`: Generate RBAC dataset from role assignments\"\n ]\n }\n ],\n \"metadata\": {\n \"kernelspec\": {\n \"display_name\": \"llm4db\",\n \"language\": \"python\",\n \"name\": \"python3\"\n },\n \"language_info\": {\n \"codemirror_mode\": {\n \"name\": \"ipython\",\n \"version\": 3\n },\n \"file_extension\": \".py\",\n \"mimetype\": \"text/x-python\",\n \"name\": \"python\",\n \"nbconvert_exporter\": \"python\",\n \"pygments_lexer\": \"ipython3\",\n \"version\": \"3.10.18\"\n }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 5\n}\n"}, "5cc57acf0416355bbf429707237d4389c7ecaebe": {"t": 1785753616.5686216, "n": 162988, "h": "a254ef89eada166d", "c": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"id\": \"c4bddd25\",\n \"metadata\": {},\n \"source\": [\n \"## 0. Import and Setup\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"93292a4c\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Setup and imports\\n\",\n \"import sys\\n\",\n \"import json\\n\",\n \"import sqlite3\\n\",\n \"from pathlib import Path\\n\",\n \"from pprint import pprint\\n\",\n \"\\n\",\n \"# Add project root to path\\n\",\n \"project_root = Path.cwd().parent\\n\",\n \"sys.path.insert(0, str(project_root))\\n\",\n \"\\n\",\n \"print(f\\\"Project root: {project_root}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 2,\n \"id\": \"bce399e4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Available API keys:\\n\",\n \" OPENAI_API_KEY: ✓\\n\",\n \" DEEPSEEK_API_KEY: ✓\\n\",\n \" DEEPINFRA_API_KEY: ✓\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Load environment and initialize Oracle\\n\",\n \"from dotenv import load_dotenv\\n\",\n \"import os\\n\",\n \"\\n\",\n \"load_dotenv(project_root / '.env')\\n\",\n \"\\n\",\n \"# Check available API keys\\n\",\n \"api_keys = {\\n\",\n \" 'OPENAI_API_KEY': bool(os.getenv('OPENAI_API_KEY')),\\n\",\n \" 'DEEPSEEK_API_KEY': bool(os.getenv('DEEPSEEK_API_KEY')),\\n\",\n \" 'DEEPINFRA_API_KEY': bool(os.getenv('DEEPINFRA_API_KEY')),\\n\",\n \"}\\n\",\n \"print(\\\"Available API keys:\\\")\\n\",\n \"for k, v in api_keys.items():\\n\",\n \" print(f\\\" {k}: {'✓' if v else '✗'}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"a4d69b7c\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Initialize LLM Oracle\\n\",\n \"from src.llm_oracle import Oracle\\n\",\n \"\\n\",\n \"# Choose model based on available keys (prefer DeepSeek for cost efficiency)\\n\",\n \"if os.getenv('DEEPSEEK_API_KEY'):\\n\",\n \" MODEL = \\\"deepseek-chat\\\"\\n\",\n \" API_KEY = os.getenv('DEEPSEEK_API_KEY')\\n\",\n \"elif os.getenv('OPENAI_API_KEY'):\\n\",\n \" MODEL = \\\"gpt-4o-mini\\\"\\n\",\n \" API_KEY = os.getenv('OPENAI_API_KEY')\\n\",\n \"else:\\n\",\n \" raise ValueError(\\\"No API key available!\\\")\\n\",\n \"\\n\",\n \"oracle = Oracle(model=MODEL, apikey=API_KEY)\\n\",\n \"print(f\\\"Using model: {MODEL}\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8809cf55\",\n \"metadata\": {},\n \"source\": [\n \"## 1. Load Sample Database Schema\\n\",\n \"\\n\",\n \"We'll use a BIRD database with interesting column structure for testing.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 4,\n \"id\": \"e9dcc61c\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Import shared policy utility functions\\n\",\n \"from src.role_evaluator.quality_metrics import RoleQualityEvaluator\\n\",\n \"\\n\",\n \"# Convenience aliases for the static methods\\n\",\n \"count_policy_columns = RoleQualityEvaluator.count_policy_columns\\n\",\n \"has_wildcard_policy = RoleQualityEvaluator.has_wildcard_policy\\n\",\n \"\\n\",\n \"def get_schema_from_sqlite(db_path: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Extract detailed schema from SQLite database.\\\"\\\"\\\"\\n\",\n \" conn = sqlite3.connect(db_path)\\n\",\n \" cursor = conn.cursor()\\n\",\n \" \\n\",\n \" # Get all tables\\n\",\n \" cursor.execute(\\\"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\\\")\\n\",\n \" tables = [row[0] for row in cursor.fetchall()]\\n\",\n \" \\n\",\n \" schema = {}\\n\",\n \" for table in tables:\\n\",\n \" cursor.execute(f\\\"PRAGMA table_info(`{table}`)\\\")\\n\",\n \" columns = cursor.fetchall()\\n\",\n \" \\n\",\n \" cursor.execute(f\\\"PRAGMA foreign_key_list(`{table}`)\\\")\\n\",\n \" fks = {row[3]: f\\\"{row[2]}.{row[4]}\\\" for row in cursor.fetchall()}\\n\",\n \" \\n\",\n \" schema[table] = {\\n\",\n \" 'columns': [\\n\",\n \" {\\n\",\n \" 'name': col[1],\\n\",\n \" 'type': col[2] or 'TEXT',\\n\",\n \" 'pk': col[5] == 1,\\n\",\n \" 'not_null': col[3] == 1,\\n\",\n \" 'fk': fks.get(col[1])\\n\",\n \" }\\n\",\n \" for col in columns\\n\",\n \" ]\\n\",\n \" }\\n\",\n \" \\n\",\n \" conn.close()\\n\",\n \" return schema\\n\",\n \"\\n\",\n \"def format_schema_for_prompt(schema: dict, db_name: str) -> str:\\n\",\n \" \\\"\\\"\\\"Format schema as text for LLM prompt.\\\"\\\"\\\"\\n\",\n \" lines = [f\\\"Database: {db_name}\\\\n\\\"]\\n\",\n \" \\n\",\n \" for table, info in schema.items():\\n\",\n \" lines.append(f\\\"Table: {table}\\\")\\n\",\n \" lines.append(\\\"Columns:\\\")\\n\",\n \" for col in info['columns']:\\n\",\n \" constraints = []\\n\",\n \" if col['pk']:\\n\",\n \" constraints.append(\\\"PRIMARY KEY\\\")\\n\",\n \" if col['not_null'] and not col['pk']:\\n\",\n \" constraints.append(\\\"NOT NULL\\\")\\n\",\n \" if col['fk']:\\n\",\n \" constraints.append(f\\\"FK -> {col['fk']}\\\")\\n\",\n \" \\n\",\n \" constraint_str = f\\\" [{', '.join(constraints)}]\\\" if constraints else \\\"\\\"\\n\",\n \" lines.append(f\\\" - {col['name']} ({col['type']}){constraint_str}\\\")\\n\",\n \" lines.append(\\\"\\\")\\n\",\n \" \\n\",\n \" return \\\"\\\\n\\\".join(lines)\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 5,\n \"id\": \"705d64b1\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Available BIRD databases (11):\\n\",\n \" - california_schools\\n\",\n \" - card_games\\n\",\n \" - codebase_community\\n\",\n \" - debit_card_specializing\\n\",\n \" - european_football_2\\n\",\n \" - financial\\n\",\n \" - formula_1\\n\",\n \" - student_club\\n\",\n \" - superhero\\n\",\n \" - thrombosis_prediction\\n\",\n \" ...\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Load a sample BIRD database - use the latest 2025 version\\n\",\n \"bird_db_dir = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases'\\n\",\n \"\\n\",\n \"# List available databases\\n\",\n \"available_dbs = [d.name for d in bird_db_dir.iterdir() if d.is_dir()]\\n\",\n \"print(f\\\"Available BIRD databases ({len(available_dbs)}):\\\")\\n\",\n \"for db in sorted(available_dbs)[:10]:\\n\",\n \" print(f\\\" - {db}\\\")\\n\",\n \"print(\\\" ...\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"f9e1a86a\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Select a database with moderate complexity\\n\",\n \"# 'california_schools' has tables with sensitive-looking columns\\n\",\n \"TEST_DB = \\\"california_schools\\\"\\n\",\n \"db_path = bird_db_dir / TEST_DB / f\\\"{TEST_DB}.sqlite\\\"\\n\",\n \"\\n\",\n \"print(f\\\"Loading schema from: {db_path}\\\")\\n\",\n \"schema = get_schema_from_sqlite(str(db_path))\\n\",\n \"\\n\",\n \"print(f\\\"\\\\nTables: {list(schema.keys())}\\\")\\n\",\n \"for table, info in schema.items():\\n\",\n \" print(f\\\"\\\\n{table}: {len(info['columns'])} columns\\\")\\n\",\n \" for col in info['columns'][:5]:\\n\",\n \" print(f\\\" - {col['name']} ({col['type']})\\\")\\n\",\n \" if len(info['columns']) > 5:\\n\",\n \" print(f\\\" ... and {len(info['columns']) - 5} more\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 7,\n \"id\": \"e5fb0114\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Database: california_schools\\n\",\n \"\\n\",\n \"Table: frpm\\n\",\n \"Columns:\\n\",\n \" - CDSCode (TEXT) [PRIMARY KEY, FK -> schools.CDSCode]\\n\",\n \" - Academic Year (TEXT)\\n\",\n \" - County Code (TEXT)\\n\",\n \" - District Code (INTEGER)\\n\",\n \" - School Code (TEXT)\\n\",\n \" - County Name (TEXT)\\n\",\n \" - District Name (TEXT)\\n\",\n \" - School Name (TEXT)\\n\",\n \" - District Type (TEXT)\\n\",\n \" - School Type (TEXT)\\n\",\n \" - Educational Option Type (TEXT)\\n\",\n \" - NSLP Provision Status (TEXT)\\n\",\n \" - Charter School (Y/N) (INTEGER)\\n\",\n \" - Charter School Number (TEXT)\\n\",\n \" - Charter Funding Type (TEXT)\\n\",\n \" - IRC (INTEGER)\\n\",\n \" - Low Grade (TEXT)\\n\",\n \" - High Grade (TEXT)\\n\",\n \" - Enrollment (K-12) (REAL)\\n\",\n \" - Free Meal Count (K-12) (REAL)\\n\",\n \" - Percent (%) Eligible Free (K-12) (REAL)\\n\",\n \" - FRPM Count (K-12) (REAL)\\n\",\n \" - Percent (%) Eligible FRPM (K-12) (REAL)\\n\",\n \" - Enrollment (Ages 5-17) (REAL)\\n\",\n \" - Free Meal Count (Ages 5-17) (REAL)\\n\",\n \" - Percent (%) Eligible Free (Ages 5-17) (REAL)\\n\",\n \" - FRPM Count (Ages 5-17) (REAL)\\n\",\n \" - Percent (%) Eligible FRPM (Ages 5-17) (REAL)\\n\",\n \" - 2013-14 CALPADS Fall 1 Certification Status (INTEGER)\\n\",\n \"\\n\",\n \"Table: satscores\\n\",\n \"Columns:\\n\",\n \" - cds (TEXT) [PRIMARY KEY, FK -> schools.CDSCode]\\n\",\n \" - rtype (TEXT) [NOT NULL]\\n\",\n \" - sname (TEXT)\\n\",\n \" - dname (TEXT)\\n\",\n \" - cname (TEXT)\\n\",\n \" - enroll12 (INTEGER) [NOT NULL]\\n\",\n \" - NumTstTakr (INTEGER) [NOT NULL]\\n\",\n \" - AvgScrRead (INTEGER)\\n\",\n \" - AvgScrMath (INTEGER)\\n\",\n \" - AvgScrWrite (INTEGER)\\n\",\n \" - NumGE1500 (INTEGER)\\n\",\n \"\\n\",\n \"Table: schools\\n\",\n \"Columns:\\n\",\n \" - CDSCode (TEXT) [PRIMARY KEY]\\n\",\n \" - NCESDist (TEXT)\\n\",\n \" - NCESSchool (TEXT)\\n\",\n \" - StatusType (TEXT) [NOT NULL]\\n\",\n \" - County (TEXT) [NOT NULL]\\n\",\n \" - District (TEXT) [NOT NULL]\\n\",\n \" - School (TEXT)\\n\",\n \" - Street (TEXT)\\n\",\n \" - StreetAbr (TEXT)\\n\",\n \" - City (TEXT)\\n\",\n \" - Zip (TEXT)\\n\",\n \" - State (TEXT)\\n\",\n \" - MailStreet (TEXT)\\n\",\n \" - MailStrAbr (TEXT)\\n\",\n \" - MailCity (TEXT)\\n\",\n \" - MailZip (TEXT)\\n\",\n \" - MailState (TEXT)\\n\",\n \" - Phone (TEXT)\\n\",\n \" - Ext (TEXT)\\n\",\n \" - Website (TEXT)\\n\",\n \" - OpenDate (DATE)\\n\",\n \" - ClosedDate (DATE)\\n\",\n \" - Charter (INTEGER)\\n\",\n \" - CharterNum (TEXT)\\n\",\n \" - FundingType (TEXT)\\n\",\n \" - DOC (TEXT) [NOT NULL]\\n\",\n \" - DOCType (TEXT) [NOT NULL]\\n\",\n \" - SOC (TEXT)\\n\",\n \" - SOCType (TEXT)\\n\",\n \" - EdOpsCode (TEXT)\\n\",\n \" \\n\",\n \"\\n\",\n \"... (total 2401 characters)\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Format schema for prompt\\n\",\n \"schema_text = format_schema_for_prompt(schema, TEST_DB)\\n\",\n \"print(schema_text[:2000])\\n\",\n \"print(f\\\"\\\\n... (total {len(schema_text)} characters)\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8e904509\",\n \"metadata\": {},\n \"source\": [\n \"## 2. Load Prompts from External Modules\\n\",\n \"\\n\",\n \"Prompts are now defined in `configs/column_level_prompts.py`.\\n\",\n \"- **JSON format** is used (more structured, requires max_tokens=4000)\\n\",\n \"- Parser auto-detects format and handles both JSON and Plain Text\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 8,\n \"id\": \"1a425645\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"✓ Loaded from external modules:\\n\",\n \" - COLUMN_LEVEL_SYSTEM_PROMPT (2734 chars)\\n\",\n \" - COLUMN_LEVEL_USER_PROMPT_TEMPLATE\\n\",\n \" - DEFAULT_MAX_TOKENS = 4000\\n\",\n \" - parse_column_level_roles (auto-detects JSON/Text)\\n\",\n \" - validate_roles_against_schema\\n\",\n \"\\n\",\n \"--- System Prompt Preview ---\\n\",\n \"You are a database security expert specializing in fine-grained Role-Based Access Control (RBAC).\\n\",\n \"Your job is to read a relational database schema and propose a COHERENT set of roles with COLUMN-LEVEL permissions.\\n\",\n \"\\n\",\n \"Key goals:\\n\",\n \"- Minimize role count while covering real-world workflows (decide number of roles according to real world scenario).\\n\",\n \"- Each role must be clearly related to others (same scenario, complementary duties).\\n\",\n \"- Enforce least-privilege at the COLUMN level (specify which columns eac\\n\",\n \"...\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Reload modules to get latest changes\\n\",\n \"import importlib\\n\",\n \"import configs.column_level_prompts\\n\",\n \"import src.role_parser.column_level_parser\\n\",\n \"importlib.reload(configs.column_level_prompts)\\n\",\n \"importlib.reload(src.role_parser.column_level_parser)\\n\",\n \"\\n\",\n \"# Import prompts and parser from external modules\\n\",\n \"from configs.column_level_prompts import (\\n\",\n \" COLUMN_LEVEL_SYSTEM_PROMPT, \\n\",\n \" COLUMN_LEVEL_USER_PROMPT_TEMPLATE,\\n\",\n \" DEFAULT_MAX_TOKENS\\n\",\n \")\\n\",\n \"from src.role_parser import parse_column_level_roles, validate_roles_against_schema\\n\",\n \"\\n\",\n \"print(\\\"✓ Loaded from external modules:\\\")\\n\",\n \"print(f\\\" - COLUMN_LEVEL_SYSTEM_PROMPT ({len(COLUMN_LEVEL_SYSTEM_PROMPT)} chars)\\\")\\n\",\n \"print(f\\\" - COLUMN_LEVEL_USER_PROMPT_TEMPLATE\\\")\\n\",\n \"print(f\\\" - DEFAULT_MAX_TOKENS = {DEFAULT_MAX_TOKENS}\\\")\\n\",\n \"print(f\\\" - parse_column_level_roles (auto-detects JSON/Text)\\\")\\n\",\n \"print(f\\\" - validate_roles_against_schema\\\")\\n\",\n \"\\n\",\n \"print(\\\"\\\\n--- System Prompt Preview ---\\\")\\n\",\n \"print(COLUMN_LEVEL_SYSTEM_PROMPT[:500])\\n\",\n \"print(\\\"...\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"0acacb6e\",\n \"metadata\": {},\n \"source\": [\n \"## [Archived] Single-Sample Experiments\\n\",\n \"\\n\",\n \"The following code block contains single sample test code for debugging prompt and parser.\\n\",\n \"**Commented out. For production, skip to Section 8 for batch processing.**\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 9,\n \"id\": \"e9700ae1\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"⏭️ This cell is commented out. Skip to Section 8 for batch processing.\\n\"\n ]\n }\n ],\n \"source\": [\n \"# ============================================================\\n\",\n \"# [ARCHIVED] Single sample experiment code - commented out, skip this cell\\n\",\n \"# ============================================================\\n\",\n \"# The following code is for testing and debugging a single database\\n\",\n \"# For production, skip to Section 8 for batch processing\\n\",\n \"\\n\",\n \"'''\\n\",\n \"# --- 3. Generate Column-Level Roles (Single DB) ---\\n\",\n \"print(\\\"Generating column-level roles...\\\")\\n\",\n \"user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=schema_text)\\n\",\n \"response = oracle.query(\\n\",\n \" prompt_sys=COLUMN_LEVEL_SYSTEM_PROMPT,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3, top_p=0.9,\\n\",\n \" max_completion_tokens=DEFAULT_MAX_TOKENS\\n\",\n \")\\n\",\n \"llm_response = response['answer']\\n\",\n \"print(f\\\"Response length: {len(llm_response)} chars\\\")\\n\",\n \"\\n\",\n \"# --- 4. Parse and Validate ---\\n\",\n \"from src.role_parser.column_level_parser import parse_column_level_roles\\n\",\n \"parsed_roles = parse_column_level_roles(llm_response)\\n\",\n \"print(f\\\"Parsed {len(parsed_roles)} roles\\\")\\n\",\n \"\\n\",\n \"validation = validate_roles_against_schema(parsed_roles, str(db_path))\\n\",\n \"print(f\\\"Valid: {validation['valid_roles']}/{validation['total_roles']}\\\")\\n\",\n \"\\n\",\n \"# --- 5. Show Results ---\\n\",\n \"for role in parsed_roles:\\n\",\n \" print(f\\\" - {role['role']}: {len(role.get('columns', {}))} tables\\\")\\n\",\n \"\\n\",\n \"# --- 6. Compare with Table-Level ---\\n\",\n \"from configs.prompts import SYSTEM_PROMPT as TABLE_LEVEL_PROMPT, USER_PROMPT_TEMPLATE\\n\",\n \"table_level_response = oracle.query(\\n\",\n \" prompt_sys=TABLE_LEVEL_PROMPT,\\n\",\n \" prompt_user=USER_PROMPT_TEMPLATE.format(schema_content=schema_text),\\n\",\n \" temp=0.3, top_p=0.9\\n\",\n \")\\n\",\n \"print(\\\"Table-Level Response:\\\", table_level_response['answer'][:500])\\n\",\n \"\\n\",\n \"# --- 7. Save Single DB Result ---\\n\",\n \"def convert_to_assignment_format(parsed_roles, db_name):\\n\",\n \" return {\\\"assignments\\\": {db_name: [\\n\",\n \" {\\\"role\\\": r[\\\"role\\\"], \\\"description\\\": r.get(\\\"description\\\", \\\"\\\"), \\\"policy\\\": r.get(\\\"columns\\\", {})}\\n\",\n \" for r in parsed_roles\\n\",\n \" ]}}\\n\",\n \"\\n\",\n \"column_level_assignments = convert_to_assignment_format(parsed_roles, TEST_DB)\\n\",\n \"output_file = project_root / 'outputs' / 'column_level_role_assignments.json'\\n\",\n \"with open(output_file, 'w') as f:\\n\",\n \" json.dump(column_level_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \"print(f\\\"Saved to: {output_file}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ This cell is commented out. Skip to Section 8 for batch processing.\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8bc8cb95\",\n \"metadata\": {},\n \"source\": [\n \"## 3. Batch Process All BIRD Databases\\n\",\n \"\\n\",\n \"Generate column-level role assignments for all BIRD databases with parallel processing.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 10,\n \"id\": \"fe3f2e81\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Configuration:\\n\",\n \" Workers: 40\\n\",\n \" Databases: 11\\n\",\n \" Max tokens: 4000\\n\",\n \" Model: deepseek-chat\\n\",\n \"\\n\",\n \"Databases to process:\\n\",\n \" - superhero\\n\",\n \" - thrombosis_prediction\\n\",\n \" - student_club\\n\",\n \" - toxicology\\n\",\n \" - california_schools\\n\",\n \" - debit_card_specializing\\n\",\n \" - card_games\\n\",\n \" - codebase_community\\n\",\n \" - formula_1\\n\",\n \" - european_football_2\\n\",\n \" - financial\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Batch processing configuration\\n\",\n \"from concurrent.futures import ThreadPoolExecutor, as_completed\\n\",\n \"from tqdm.notebook import tqdm\\n\",\n \"\\n\",\n \"# Configuration\\n\",\n \"WORKERS = 40\\n\",\n \"SAMPLES = 100 # 0 for all databases\\n\",\n \"MAX_TOKENS = DEFAULT_MAX_TOKENS\\n\",\n \"\\n\",\n \"# Get all BIRD databases\\n\",\n \"all_dbs = [d.name for d in bird_db_dir.iterdir() if d.is_dir()]\\n\",\n \"if SAMPLES > 0:\\n\",\n \" all_dbs = all_dbs[:SAMPLES]\\n\",\n \"\\n\",\n \"print(f\\\"Configuration:\\\")\\n\",\n \"print(f\\\" Workers: {WORKERS}\\\")\\n\",\n \"print(f\\\" Databases: {len(all_dbs)}\\\")\\n\",\n \"print(f\\\" Max tokens: {MAX_TOKENS}\\\")\\n\",\n \"print(f\\\" Model: {MODEL}\\\")\\n\",\n \"print(f\\\"\\\\nDatabases to process:\\\")\\n\",\n \"for db in all_dbs:\\n\",\n \" print(f\\\" - {db}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 11,\n \"id\": \"7b5edd94\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"✓ Helper functions defined\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Helper functions for batch processing\\n\",\n \"\\n\",\n \"def process_single_database(db_name: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Process a single database and return role assignments.\\\"\\\"\\\"\\n\",\n \" db_path = bird_db_dir / db_name / f\\\"{db_name}.sqlite\\\"\\n\",\n \" \\n\",\n \" if not db_path.exists():\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": f\\\"Database not found\\\"}\\n\",\n \" \\n\",\n \" try:\\n\",\n \" # Extract schema\\n\",\n \" db_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" db_schema_text = format_schema_for_prompt(db_schema, db_name)\\n\",\n \" \\n\",\n \" # Generate roles via LLM\\n\",\n \" user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=db_schema_text)\\n\",\n \" \\n\",\n \" response = oracle.query(\\n\",\n \" prompt_sys=COLUMN_LEVEL_SYSTEM_PROMPT,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3,\\n\",\n \" top_p=0.9,\\n\",\n \" max_completion_tokens=MAX_TOKENS\\n\",\n \" )\\n\",\n \" \\n\",\n \" llm_response = response['answer']\\n\",\n \" usage = response.get('usage', {})\\n\",\n \" \\n\",\n \" # Parse response\\n\",\n \" roles = parse_column_level_roles(llm_response)\\n\",\n \" \\n\",\n \" if not roles:\\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"parse_error\\\",\\n\",\n \" \\\"error\\\": \\\"Failed to parse LLM response\\\",\\n\",\n \" \\\"raw_response\\\": llm_response[:300]\\n\",\n \" }\\n\",\n \" \\n\",\n \" # Validate against schema\\n\",\n \" validation = validate_roles_against_schema(roles, str(db_path))\\n\",\n \" \\n\",\n \" # Convert to target format\\n\",\n \" assignments = []\\n\",\n \" for role in roles:\\n\",\n \" assignments.append({\\n\",\n \" \\\"role\\\": role[\\\"role\\\"],\\n\",\n \" \\\"description\\\": role.get(\\\"description\\\", \\\"\\\"),\\n\",\n \" \\\"policy\\\": role.get(\\\"columns\\\", {})\\n\",\n \" })\\n\",\n \" \\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"success\\\",\\n\",\n \" \\\"roles_count\\\": len(assignments),\\n\",\n \" \\\"valid_roles\\\": validation['valid_roles'],\\n\",\n \" \\\"all_valid\\\": validation['all_valid'],\\n\",\n \" \\\"assignments\\\": assignments,\\n\",\n \" \\\"usage\\\": usage\\n\",\n \" }\\n\",\n \" \\n\",\n \" except Exception as e:\\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"error\\\",\\n\",\n \" \\\"error\\\": str(e)\\n\",\n \" }\\n\",\n \"\\n\",\n \"print(\\\"✓ Helper functions defined\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 12,\n \"id\": \"61e61795\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Starting batch processing with 40 workers...\\n\",\n \"============================================================\\n\",\n \"[1/11] ✓ toxicology: 4 roles (4 valid)\\n\",\n \"[2/11] ✓ debit_card_specializing: 4 roles (4 valid)\\n\",\n \"[3/11] ✓ thrombosis_prediction: 4 roles (4 valid)\\n\",\n \"[4/11] ✓ student_club: 5 roles (5 valid)\\n\",\n \"[5/11] ✓ superhero: 5 roles (5 valid)\\n\",\n \"[6/11] ✓ financial: 5 roles (5 valid)\\n\",\n \"[7/11] ✓ card_games: 5 roles (5 valid)\\n\",\n \"[8/11] ✓ codebase_community: 4 roles (4 valid)\\n\",\n \"[9/11] ✓ california_schools: 5 roles (5 valid)\\n\",\n \"[10/11] ✓ formula_1: 5 roles (5 valid)\\n\",\n \"[11/11] ✓ european_football_2: 5 roles (5 valid)\\n\",\n \"============================================================\\n\",\n \"Completed: 11/11 success, 0 errors\\n\",\n \"Total tokens: 25,679\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Execute batch processing with parallel workers\\n\",\n \"print(f\\\"Starting batch processing with {WORKERS} workers...\\\")\\n\",\n \"print(\\\"=\\\" * 60)\\n\",\n \"\\n\",\n \"results = {\\n\",\n \" \\\"metadata\\\": {\\n\",\n \" \\\"model\\\": MODEL,\\n\",\n \" \\\"max_tokens\\\": MAX_TOKENS,\\n\",\n \" \\\"total_databases\\\": len(all_dbs)\\n\",\n \" },\\n\",\n \" \\\"assignments\\\": {},\\n\",\n \" \\\"errors\\\": []\\n\",\n \"}\\n\",\n \"\\n\",\n \"success_count = 0\\n\",\n \"error_count = 0\\n\",\n \"total_tokens = {\\\"prompt\\\": 0, \\\"completion\\\": 0}\\n\",\n \"\\n\",\n \"with ThreadPoolExecutor(max_workers=WORKERS) as executor:\\n\",\n \" futures = {executor.submit(process_single_database, db): db for db in all_dbs}\\n\",\n \" \\n\",\n \" for i, future in enumerate(as_completed(futures), 1):\\n\",\n \" db_name = futures[future]\\n\",\n \" try:\\n\",\n \" result = future.result()\\n\",\n \" \\n\",\n \" if result[\\\"status\\\"] == \\\"success\\\":\\n\",\n \" results[\\\"assignments\\\"][result[\\\"database\\\"]] = result[\\\"assignments\\\"]\\n\",\n \" success_count += 1\\n\",\n \" \\n\",\n \" usage = result.get(\\\"usage\\\", {})\\n\",\n \" total_tokens[\\\"prompt\\\"] += usage.get(\\\"prompt_tokens\\\", 0)\\n\",\n \" total_tokens[\\\"completion\\\"] += usage.get(\\\"completion_tokens\\\", 0)\\n\",\n \" \\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✓ {db_name}: {result['roles_count']} roles ({result['valid_roles']} valid)\\\")\\n\",\n \" else:\\n\",\n \" results[\\\"errors\\\"].append(result)\\n\",\n \" error_count += 1\\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✗ {db_name}: {result.get('error', result['status'])}\\\")\\n\",\n \" \\n\",\n \" except Exception as e:\\n\",\n \" error_count += 1\\n\",\n \" results[\\\"errors\\\"].append({\\\"database\\\": db_name, \\\"error\\\": str(e)})\\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✗ {db_name}: {e}\\\")\\n\",\n \"\\n\",\n \"results[\\\"metadata\\\"][\\\"success_count\\\"] = success_count\\n\",\n \"results[\\\"metadata\\\"][\\\"error_count\\\"] = error_count\\n\",\n \"results[\\\"metadata\\\"][\\\"total_tokens\\\"] = total_tokens\\n\",\n \"\\n\",\n \"print(\\\"=\\\" * 60)\\n\",\n \"print(f\\\"Completed: {success_count}/{len(all_dbs)} success, {error_count} errors\\\")\\n\",\n \"print(f\\\"Total tokens: {total_tokens['prompt'] + total_tokens['completion']:,}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"39d38aa3\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Save batch results to file\\n\",\n \"output_file = project_root / 'outputs' / 'column_level_role_assignments_bird.json'\\n\",\n \"\\n\",\n \"with open(output_file, 'w', encoding='utf-8') as f:\\n\",\n \" json.dump(results, f, indent=2, ensure_ascii=False)\\n\",\n \"\\n\",\n \"print(f\\\"✓ Results saved to: {output_file}\\\")\\n\",\n \"print(f\\\"\\\\nFile structure:\\\")\\n\",\n \"print(f\\\" - metadata: model, tokens, counts\\\")\\n\",\n \"print(f\\\" - assignments: {len(results['assignments'])} databases\\\")\\n\",\n \"print(f\\\" - errors: {len(results['errors'])} entries\\\")\\n\",\n \"\\n\",\n \"# Show sample\\n\",\n \"if results['assignments']:\\n\",\n \" sample_db = list(results['assignments'].keys())[0]\\n\",\n \" print(f\\\"\\\\nSample ({sample_db}):\\\")\\n\",\n \" print(json.dumps(results['assignments'][sample_db][:1], indent=2, ensure_ascii=False)[:500])\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"9595c46d\",\n \"metadata\": {},\n \"source\": [\n \"## 4. Generate Column-Level RBAC Dataset\\n\",\n \"\\n\",\n \"Based on the generated role policies, combined with BIRD dataset queries, generate the final RBAC training/evaluation dataset.\\n\",\n \"\\n\",\n \"Dataset format (consistent with table-level):\\n\",\n \"- `db_id`: Database name\\n\",\n \"- `instruction`: Full instruction with schema + evidence + role policy\\n\",\n \"- `role`: Role name\\n\",\n \"- `policy`: Column-level permission policy {table: [columns]}\\n\",\n \"- `input`: User question\\n\",\n \"- `output`: SQL or denial message\\n\",\n \"- `difficulty`: Query difficulty level\\n\",\n \"- `metadata`: Contains gold_sql, permission, query_columns, missing_columns, etc.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"405783fa\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# 4.1 Import generator and test dependencies\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator, format_policy_text\\n\",\n \"from src.processors.column_extractor import extract_query_columns\\n\",\n \"from src.processors.column_permission_checker import check_column_permission\\n\",\n \"import glob\\n\",\n \"\\n\",\n \"# Configure paths - use 2025 version (dev_20251106) for latest BIRD data\\n\",\n \"ROLE_ASSIGNMENTS_PATH = project_root / 'outputs' / 'column_level_role_assignments_bird.json'\\n\",\n \"BIRD_DATA_PATH = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev.json'\\n\",\n \"BIRD_DB_ROOT = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases'\\n\",\n \"\\n\",\n \"print(\\\"=== Dataset Generation Config ===\\\")\\n\",\n \"print(f\\\"Role assignments: {ROLE_ASSIGNMENTS_PATH}\\\")\\n\",\n \"print(f\\\"BIRD data file: {BIRD_DATA_PATH}\\\")\\n\",\n \"print(f\\\"BIRD database dir: {BIRD_DB_ROOT}\\\")\\n\",\n \"\\n\",\n \"# Verify file existence\\n\",\n \"# assert ROLE_ASSIGNMENTS_PATH.exists(), f\\\"Role assignments file not found: {ROLE_ASSIGNMENTS_PATH}\\\"\\n\",\n \"assert BIRD_DATA_PATH.exists(), f\\\"BIRD data file not found: {BIRD_DATA_PATH}\\\"\\n\",\n \"assert BIRD_DB_ROOT.exists(), f\\\"BIRD database dir not found: {BIRD_DB_ROOT}\\\"\\n\",\n \"print(\\\"\\\\n✓ All file paths are valid\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"4b80dc65\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== SQL Column Extraction Test ===\\n\",\n \"Tables: {'song', 'singer'}\\n\",\n \"Columns: {'song': {'singer_id', 'title'}, 'singer': {'id', 'name', 'country', 'age'}}\\n\",\n \"Has *: False\\n\",\n \"\\n\",\n \"=== Permission Check Test (Allow) ===\\n\",\n \"Allowed: True\\n\",\n \"Reason: All required columns are permitted\\n\",\n \"\\n\",\n \"=== Permission Check Test (Deny) ===\\n\",\n \"Allowed: False\\n\",\n \"Reason: Missing column permissions: song: singer_id; singer: age, country, id\\n\",\n \"Missing: {'song': {'singer_id'}, 'singer': {'id', 'country', 'age'}}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.2 Test SQL column extraction and permission checking\\n\",\n \"# Verified working, archived to simplify notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"test_sql = \\\"\\\"\\\"\\n\",\n \"SELECT singer.name, singer.age, song.title\\n\",\n \"FROM singer\\n\",\n \"JOIN song ON singer.id = song.singer_id\\n\",\n \"WHERE singer.country = 'USA'\\n\",\n \"\\\"\\\"\\\"\\n\",\n \"\\n\",\n \"print(\\\"=== SQL Column Extraction Test ===\\\")\\n\",\n \"info = extract_query_columns(test_sql)\\n\",\n \"print(f\\\"Tables: {info.tables}\\\")\\n\",\n \"print(f\\\"Columns: {info.columns}\\\")\\n\",\n \"print(f\\\"Has *: {info.has_star}\\\")\\n\",\n \"\\n\",\n \"# Test permission checking\\n\",\n \"policy_allow = {\\n\",\n \" 'singer': ['name', 'age', 'country', 'id'],\\n\",\n \" 'song': ['title', 'singer_id']\\n\",\n \"}\\n\",\n \"policy_deny = {\\n\",\n \" 'singer': ['name'], # Missing 'age', 'country', 'id'\\n\",\n \" 'song': ['title'] # Missing 'singer_id'\\n\",\n \"}\\n\",\n \"\\n\",\n \"print(\\\"\\\\n=== Permission Check Test (Allow) ===\\\")\\n\",\n \"result1 = check_column_permission(test_sql, policy_allow)\\n\",\n \"print(f\\\"Allowed: {result1.allowed}\\\")\\n\",\n \"print(f\\\"Reason: {result1.reason}\\\")\\n\",\n \"\\n\",\n \"print(\\\"\\\\n=== Permission Check Test (Deny) ===\\\")\\n\",\n \"result2 = check_column_permission(test_sql, policy_deny)\\n\",\n \"print(f\\\"Allowed: {result2.allowed}\\\")\\n\",\n \"print(f\\\"Reason: {result2.reason}\\\")\\n\",\n \"print(f\\\"Missing: {result2.missing_columns}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Column extraction/permission tests - verified\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 16,\n \"id\": \"0a018208\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Generator Initialized ===\\n\",\n \"Total BIRD queries: 1534\\n\",\n \"Databases with role assignments: 11\\n\",\n \"Databases with queries: 11\\n\",\n \"\\n\",\n \"Queries per database:\\n\",\n \" california_schools: 89\\n\",\n \" card_games: 191\\n\",\n \" codebase_community: 186\\n\",\n \" debit_card_specializing: 64\\n\",\n \" european_football_2: 129\\n\",\n \" financial: 106\\n\",\n \" formula_1: 174\\n\",\n \" student_club: 158\\n\",\n \" superhero: 129\\n\",\n \" thrombosis_prediction: 163\\n\",\n \" toxicology: 145\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 4.3 Initialize generator\\n\",\n \"generator = ColumnLevelRBACGenerator(\\n\",\n \" role_assignments_path=str(ROLE_ASSIGNMENTS_PATH),\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH),\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" project_root=str(project_root),\\n\",\n \" seed=42\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"=== Generator Initialized ===\\\")\\n\",\n \"print(f\\\"Total BIRD queries: {len(generator.bird_data)}\\\")\\n\",\n \"print(f\\\"Databases with role assignments: {len(generator.role_assignments.get('assignments', {}))}\\\")\\n\",\n \"print(f\\\"Databases with queries: {len(generator.queries_by_db)}\\\")\\n\",\n \"\\n\",\n \"# Show query counts per database\\n\",\n \"print(\\\"\\\\nQueries per database:\\\")\\n\",\n \"for db_id, queries in sorted(generator.queries_by_db.items()):\\n\",\n \" print(f\\\" {db_id}: {len(queries)}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"2a267246\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# [ARCHIVED] 4.4 Generate sample dataset (for format validation)\\n\",\n \"# Sample dataset used for debugging, production uses 4.5\\n\",\n \"\\n\",\n \"'''\\n\",\n \"print(\\\"=== Generating Sample Dataset ===\\\")\\n\",\n \"sample_stats = generator.generate_and_save(\\n\",\n \" output_path=str(project_root / 'outputs' / 'column_level_rbac_dataset_bird_sample.json'),\\n\",\n \" roles_per_db=1, # Use only 1 role per database\\n\",\n \" queries_per_role=3 # Use only 3 queries per role\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"\\\\\\\\nStatistics:\\\")\\n\",\n \"for k, v in sample_stats.items():\\n\",\n \" if k != 'by_database':\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Sample dataset generation - production uses 4.5\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"4851fde0\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Data Format Validation ===\\n\",\n \"Total entries: 33\\n\",\n \"\\n\",\n \"--- Allowed Entry Example ---\\n\",\n \"db_id: toxicology\\n\",\n \"role: MolecularResearcher\\n\",\n \"policy: {\\\"molecule\\\": [\\\"molecule_id\\\", \\\"label\\\"], \\\"atom\\\": [\\\"atom_id\\\", \\\"molecule_id\\\", \\\"element\\\"], \\\"bond\\\": [\\\"bond...\\n\",\n \"input: What are the atom IDs of the bond TR000_2_5?...\\n\",\n \"output: SELECT T.atom_id FROM connected AS T WHERE T.bond_id = 'TR000_2_5'...\\n\",\n \"difficulty: simple\\n\",\n \"instruction length: 1887 chars\\n\",\n \"\\n\",\n \"--- Denied Entry Example ---\\n\",\n \"db_id: thrombosis_prediction\\n\",\n \"role: ClinicalResearcher\\n\",\n \"input: How old was the patient who had the highest hemoglobin count on the date of labo...\\n\",\n \"output: Sorry, I cannot answer.\\n\",\n \"missing_columns: {'patient': ['Diagnosis']}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.5-4.6 Validate generated data format & Show instruction example\\n\",\n \"# Data format verified, archived to simplify notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"sample_file = project_root / 'outputs' / 'column_level_rbac_dataset_bird_sample.json'\\n\",\n \"with open(sample_file, 'r') as f:\\n\",\n \" sample_data = json.load(f)\\n\",\n \"\\n\",\n \"print(\\\"=== Data Format Validation ===\\\")\\n\",\n \"print(f\\\"Total entries: {len(sample_data)}\\\")\\n\",\n \"\\n\",\n \"allowed_entry = next((e for e in sample_data if e['metadata']['permission'] == 'allowed'), None)\\n\",\n \"denied_entry = next((e for e in sample_data if e['metadata']['permission'] == 'denied'), None)\\n\",\n \"\\n\",\n \"if allowed_entry:\\n\",\n \" print(\\\"\\\\\\\\n--- Allowed Entry Example ---\\\")\\n\",\n \" print(f\\\"db_id: {allowed_entry['db_id']}\\\")\\n\",\n \" print(f\\\"role: {allowed_entry['role']}\\\")\\n\",\n \" print(f\\\"policy: {json.dumps(allowed_entry['policy'])[:100]}...\\\")\\n\",\n \" print(f\\\"input: {allowed_entry['input'][:80]}...\\\")\\n\",\n \" print(f\\\"output: {allowed_entry['output'][:80]}...\\\")\\n\",\n \"\\n\",\n \"if denied_entry:\\n\",\n \" print(\\\"\\\\\\\\n--- Denied Entry Example ---\\\")\\n\",\n \" print(f\\\"db_id: {denied_entry['db_id']}\\\")\\n\",\n \" print(f\\\"role: {denied_entry['role']}\\\")\\n\",\n \" print(f\\\"output: {denied_entry['output']}\\\")\\n\",\n \" print(f\\\"missing_columns: {denied_entry['metadata']['missing_columns']}\\\")\\n\",\n \"\\n\",\n \"# Show full instruction\\n\",\n \"print(\\\"\\\\\\\\n=== Full Instruction Example ===\\\")\\n\",\n \"if allowed_entry:\\n\",\n \" print(allowed_entry['instruction'][:2500])\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Data format validation - verified\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"c05e8787\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# 4.5 Generate Full Column-Level RBAC Dataset\\n\",\n \"from datetime import datetime\\n\",\n \"\\n\",\n \"# Reload the modules to ensure we use the latest CTE fix\\n\",\n \"import importlib\\n\",\n \"import src.processors.column_extractor\\n\",\n \"import src.processors.column_permission_checker\\n\",\n \"import src.processors.column_level_rbac_generator\\n\",\n \"\\n\",\n \"importlib.reload(src.processors.column_extractor)\\n\",\n \"importlib.reload(src.processors.column_permission_checker)\\n\",\n \"importlib.reload(src.processors.column_level_rbac_generator)\\n\",\n \"\\n\",\n \"# Re-import with fresh code\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator\\n\",\n \"\\n\",\n \"# Re-create generator with fresh modules\\n\",\n \"generator = ColumnLevelRBACGenerator(\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" role_assignments_path=str(ROLE_ASSIGNMENTS_PATH),\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH)\\n\",\n \")\\n\",\n \"\\n\",\n \"# Output with date suffix\\n\",\n \"today = datetime.now().strftime('%Y%m%d')\\n\",\n \"output_path = project_root / 'outputs' / f'column_level_rbac_dataset_bird_{today}.json'\\n\",\n \"\\n\",\n \"print(\\\"=== Generating Full Column-Level RBAC Dataset ===\\\")\\n\",\n \"print(f\\\"Output: {output_path}\\\")\\n\",\n \"\\n\",\n \"full_stats = generator.generate_and_save(\\n\",\n \" output_path=str(output_path),\\n\",\n \" roles_per_db=None, # Use all roles (number varies per db based on LLM generation)\\n\",\n \" queries_per_role=None # Use all queries\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"\\\\nFull dataset statistics:\\\")\\n\",\n \"for k, v in full_stats.items():\\n\",\n \" if k == 'by_database':\\n\",\n \" print(f\\\"\\\\nPer-database distribution:\\\")\\n\",\n \" for db_id, counts in v.items():\\n\",\n \" print(f\\\" {db_id}: allowed={counts['allowed']}, denied={counts['denied']}\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" {k}: {v}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"2142ac6e\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Comparison with Table-Level Data Format ===\\n\",\n \"\\n\",\n \"--- Table-Level Entry Fields ---\\n\",\n \"['db_id', 'difficulty', 'input', 'instruction', 'metadata', 'output', 'role', 'tables']\\n\",\n \"\\n\",\n \"--- Column-Level Entry Fields ---\\n\",\n \"['db_id', 'difficulty', 'input', 'instruction', 'metadata', 'output', 'policy', 'role']\\n\",\n \"\\n\",\n \"Common fields: {'output', 'role', 'metadata', 'difficulty', 'db_id', 'input', 'instruction'}\\n\",\n \"Table-Level only: {'tables'}\\n\",\n \"Column-Level only: {'policy'}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.6 Compare with Table-Level format\\n\",\n \"# Format comparison completed, archived to simplify notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"print(\\\"=== Comparison with Table-Level Data Format ===\\\")\\n\",\n \"\\n\",\n \"table_level_file = project_root / 'data' / 'selected' / 'bird' / 'bird_dev_with_role_rbac.json'\\n\",\n \"if table_level_file.exists():\\n\",\n \" with open(table_level_file, 'r') as f:\\n\",\n \" table_level_data = json.load(f)\\n\",\n \" \\n\",\n \" print(\\\"\\\\\\\\n--- Table-Level Entry Fields ---\\\")\\n\",\n \" print(sorted(table_level_data[0].keys()))\\n\",\n \" \\n\",\n \" print(\\\"\\\\\\\\n--- Column-Level Entry Fields ---\\\") \\n\",\n \" print(sorted(sample_data[0].keys()))\\n\",\n \" \\n\",\n \" tl_keys = set(table_level_data[0].keys())\\n\",\n \" cl_keys = set(sample_data[0].keys())\\n\",\n \" \\n\",\n \" print(f\\\"\\\\\\\\nCommon fields: {tl_keys & cl_keys}\\\")\\n\",\n \" print(f\\\"Table-Level only: {tl_keys - cl_keys}\\\")\\n\",\n \" print(f\\\"Column-Level only: {cl_keys - tl_keys}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Table-level comparison - format verified compatible\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"7392ebc1\",\n \"metadata\": {},\n \"source\": [\n \"## 5. Role Generation Quality Assessment\\n\",\n \"\\n\",\n \"Automated quality checks for generated role files and RBAC datasets.\\n\",\n \"\\n\",\n \"### Quality Metrics\\n\",\n \"\\n\",\n \"| Metric | Healthy Range | Issue Description |\\n\",\n \"|--------|---------------|-------------------|\\n\",\n \"| **Deny Rate** | 5% - 90% | <5% too permissive, >90% too restrictive |\\n\",\n \"| **All-Allow Queries** | <50% | High value indicates poor role differentiation |\\n\",\n \"| **Role-Schema Similarity** | >0.6 | <0.6 indicates poor semantic alignment |\\n\",\n \"| **Policy Overlap** | <70% | >70% indicates redundant roles |\\n\",\n \"| **Coverage Diversity** | std >0.1 | Low std indicates roles have similar permissions |\\n\",\n \"\\n\",\n \"### Evaluation Pipeline\\n\",\n \"1. **Deny Rate Analysis**: Check permission distribution balance\\n\",\n \"2. **Semantic Quality**: Embedding-based role-schema alignment\\n\",\n \"3. **Policy Overlap**: Jaccard similarity to detect redundant roles\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"71f8f60f\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# 5.1 Setup Quality Evaluator (force reload all modules)\\n\",\n \"import importlib\\n\",\n \"import sys\\n\",\n \"from pathlib import Path\\n\",\n \"import glob\\n\",\n \"\\n\",\n \"# Force reload the entire module tree\\n\",\n \"if 'src.role_evaluator.quality_metrics' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator.quality_metrics']\\n\",\n \"if 'src.role_evaluator.quality_visualizer' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator.quality_visualizer']\\n\",\n \"if 'src.role_evaluator' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator']\\n\",\n \"\\n\",\n \"from src.role_evaluator import RoleQualityEvaluator, RoleQualityVisualizer, print_quality_report\\n\",\n \"\\n\",\n \"# Configure paths\\n\",\n \"project_root = Path.cwd().parent if Path.cwd().name == 'notebooks' else Path.cwd()\\n\",\n \"\\n\",\n \"# Find most recent dataset and role assignments files\\n\",\n \"role_files = sorted(glob.glob(str(project_root / 'outputs' / 'column_level_role_assignments_bird_*.json')))\\n\",\n \"ROLE_ASSIGNMENTS_PATH = role_files[-1] if role_files else str(project_root / 'outputs' / 'column_level_role_assignments_bird.json')\\n\",\n \"\\n\",\n \"# Check for v2 role assignments (after regeneration)\\n\",\n \"role_files_v2 = sorted([f for f in glob.glob(str(project_root / 'outputs' / 'bird_column_level_rbac_assignments_*.json'))\\n\",\n \" if 'v2' in f])\\n\",\n \"if role_files_v2:\\n\",\n \" ROLE_ASSIGNMENTS_PATH = role_files_v2[-1]\\n\",\n \"\\n\",\n \"dataset_files = sorted([f for f in glob.glob(str(project_root / 'outputs' / 'column_level_rbac_dataset_bird_*.json')) \\n\",\n \" if 'metadata' not in f])\\n\",\n \"if not dataset_files:\\n\",\n \" raise FileNotFoundError(\\\"No RBAC dataset found in outputs/\\\")\\n\",\n \"DATASET_PATH = dataset_files[-1]\\n\",\n \"\\n\",\n \"SCHEMA_DIR = str(project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases')\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# CONFIGURABLE QUALITY THRESHOLDS\\n\",\n \"# Adjust based on dataset characteristics\\n\",\n \"# BIRD has larger DBs with more columns, so thresholds differ from Spider\\n\",\n \"# ============================================================\\n\",\n \"QUALITY_THRESHOLDS = {\\n\",\n \" \\\"deny_rate_min\\\": 5.0, # Warn if deny rate < this (roles too permissive)\\n\",\n \" \\\"coverage_max\\\": 0.85, # Warn if avg coverage > this (roles too broad)\\n\",\n \" \\\"coverage_std_min\\\": 0.065, # Warn if coverage std < this (low diversity)\\n\",\n \" \\\"max_overlap\\\": 0.80, # Warn if max overlap > this (redundant roles)\\n\",\n \"}\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EMBEDDING MODEL CONFIGURATION\\n\",\n \"# Backend auto-detected based on model name, or set explicitly\\n\",\n \"# ============================================================\\n\",\n \"# OpenAI models (requires OPENAI_API_KEY):\\n\",\n \"# - \\\"text-embedding-3-small\\\" (default, cost-effective)\\n\",\n \"# - \\\"text-embedding-3-large\\\" (higher accuracy)\\n\",\n \"# - \\\"text-embedding-ada-002\\\" (legacy)\\n\",\n \"# \\n\",\n \"# BAAI/fastembed models (local, no API key needed):\\n\",\n \"# - \\\"BAAI/bge-small-en-v1.5\\\" (fast, lightweight)\\n\",\n \"# - \\\"BAAI/bge-base-en-v1.5\\\" (balanced)\\n\",\n \"# - \\\"BAAI/bge-large-en-v1.5\\\" (best accuracy)\\n\",\n \"# ============================================================\\n\",\n \"# EMBED_MODEL = \\\"BAAI/bge-small-en-v1.5\\\"\\n\",\n \"EMBED_MODEL = \\\"text-embedding-3-small\\\"\\n\",\n \"EMBED_BACKEND = None # None = auto-detect, or \\\"openai\\\" / \\\"fastembed\\\"\\n\",\n \"\\n\",\n \"print(\\\"=== Quality Thresholds (Configurable for BIRD) ===\\\")\\n\",\n \"for k, v in QUALITY_THRESHOLDS.items():\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"print(f\\\" embed_model: {EMBED_MODEL}\\\")\\n\",\n \"print(f\\\" embed_backend: {EMBED_BACKEND or 'auto'}\\\")\\n\",\n \"\\n\",\n \"print(f\\\"\\\\nRole Assignments: {Path(ROLE_ASSIGNMENTS_PATH).name if ROLE_ASSIGNMENTS_PATH else 'Not found'}\\\")\\n\",\n \"print(f\\\"Dataset: {Path(DATASET_PATH).name if DATASET_PATH else 'Not found'}\\\")\\n\",\n \"print(f\\\"Schema Dir: {SCHEMA_DIR}\\\")\\n\",\n \"\\n\",\n \"if ROLE_ASSIGNMENTS_PATH and DATASET_PATH:\\n\",\n \" evaluator = RoleQualityEvaluator(\\n\",\n \" role_assignments_path=ROLE_ASSIGNMENTS_PATH,\\n\",\n \" dataset_path=DATASET_PATH,\\n\",\n \" schema_dir=SCHEMA_DIR,\\n\",\n \" embed_model=EMBED_MODEL,\\n\",\n \" embed_backend=EMBED_BACKEND,\\n\",\n \" thresholds=QUALITY_THRESHOLDS\\n\",\n \" )\\n\",\n \" print(\\\"\\\\n✓ Quality evaluator initialized with custom thresholds\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"\\\\n Missing files - generate roles and dataset first\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 74,\n \"id\": \"8135e2e3\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[embedding_utils] Using OpenAI embeddings (text-embedding-3-small) for 11 texts\\n\",\n \"[embedding_utils] Using OpenAI embeddings (text-embedding-3-small) for 47 texts\\n\",\n \"Overall: 11 databases, 53 roles, 7252 entries\\n\",\n \"Pass Rate: 100.0% (11/11 pass)\\n\",\n \"\\n\",\n \"Using Thresholds: deny_rate_min=5.0%, max_overlap=80%, coverage_std_min=0.065\\n\",\n \"\\n\",\n \"=== Deny Rate Analysis ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Deny%</th>\\n\",\n \" <th>AllAllow%</th>\\n\",\n \" <th>AllDeny</th>\\n\",\n \" <th>Roles</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>73.5%</td>\\n\",\n \" <td>1.1%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>46.9%</td>\\n\",\n \" <td>2.1%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>37.8%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>54.7%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>57.1%</td>\\n\",\n \" <td>1.6%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>70.6%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>44.7%</td>\\n\",\n \" <td>8.7%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>49.9%</td>\\n\",\n \" <td>1.3%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>49.1%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>33.7%</td>\\n\",\n \" <td>2.5%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>47.4%</td>\\n\",\n \" <td>11.7%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Deny% AllAllow% AllDeny Roles Status\\n\",\n \"0 california_schools 73.5% 1.1% 0 5 OK\\n\",\n \"1 card_games 46.9% 2.1% 0 5 OK\\n\",\n \"2 codebase_community 37.8% 0.0% 0 5 OK\\n\",\n \"3 debit_card_specializing 54.7% 0.0% 0 6 OK\\n\",\n \"4 european_football_2 57.1% 1.6% 0 5 OK\\n\",\n \"5 financial 70.6% 0.0% 0 5 OK\\n\",\n \"6 formula_1 44.7% 8.7% 0 4 OK\\n\",\n \"7 student_club 49.9% 1.3% 0 5 OK\\n\",\n \"8 superhero 49.1% 0.0% 0 5 OK\\n\",\n \"9 thrombosis_prediction 33.7% 2.5% 0 4 OK\\n\",\n \"10 toxicology 47.4% 11.7% 0 4 OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Semantic Quality (Similarity: role-schema alignment, Coverage: column access, Diversity: coverage std) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Similarity</th>\\n\",\n \" <th>Coverage</th>\\n\",\n \" <th>Diversity</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>0.6902</td>\\n\",\n \" <td>34.8%</td>\\n\",\n \" <td>0.0996</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>0.6843</td>\\n\",\n \" <td>50.0%</td>\\n\",\n \" <td>0.2965</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>0.6332</td>\\n\",\n \" <td>60.6%</td>\\n\",\n \" <td>0.2888</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>0.6264</td>\\n\",\n \" <td>63.8%</td>\\n\",\n \" <td>0.1299</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>0.6799</td>\\n\",\n \" <td>31.3%</td>\\n\",\n \" <td>0.0984</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>0.6410</td>\\n\",\n \" <td>46.8%</td>\\n\",\n \" <td>0.1288</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>0.6599</td>\\n\",\n \" <td>44.0%</td>\\n\",\n \" <td>0.1480</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>0.6780</td>\\n\",\n \" <td>50.5%</td>\\n\",\n \" <td>0.1701</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>0.6835</td>\\n\",\n \" <td>61.3%</td>\\n\",\n \" <td>0.1895</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>0.6607</td>\\n\",\n \" <td>69.3%</td>\\n\",\n \" <td>0.3798</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>0.6698</td>\\n\",\n \" <td>69.7%</td>\\n\",\n \" <td>0.1134</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Similarity Coverage Diversity Status\\n\",\n \"0 california_schools 0.6902 34.8% 0.0996 OK\\n\",\n \"1 card_games 0.6843 50.0% 0.2965 OK\\n\",\n \"2 codebase_community 0.6332 60.6% 0.2888 OK\\n\",\n \"3 debit_card_specializing 0.6264 63.8% 0.1299 OK\\n\",\n \"4 european_football_2 0.6799 31.3% 0.0984 OK\\n\",\n \"5 financial 0.6410 46.8% 0.1288 OK\\n\",\n \"6 formula_1 0.6599 44.0% 0.1480 OK\\n\",\n \"7 student_club 0.6780 50.5% 0.1701 OK\\n\",\n \"8 superhero 0.6835 61.3% 0.1895 OK\\n\",\n \"9 thrombosis_prediction 0.6607 69.3% 0.3798 OK\\n\",\n \"10 toxicology 0.6698 69.7% 0.1134 OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Policy Overlap Analysis ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>AvgOverlap</th>\\n\",\n \" <th>MaxOverlap</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>30.7%</td>\\n\",\n \" <td>57.1%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>11.9%</td>\\n\",\n \" <td>23.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>28.0%</td>\\n\",\n \" <td>57.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>47.0%</td>\\n\",\n \" <td>65.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>14.4%</td>\\n\",\n \" <td>23.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>21.5%</td>\\n\",\n \" <td>37.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>29.3%</td>\\n\",\n \" <td>32.9%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>30.2%</td>\\n\",\n \" <td>45.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>33.0%</td>\\n\",\n \" <td>52.6%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>18.1%</td>\\n\",\n \" <td>38.5%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>53.7%</td>\\n\",\n \" <td>66.7%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database AvgOverlap MaxOverlap Status\\n\",\n \"0 california_schools 30.7% 57.1% OK\\n\",\n \"1 card_games 11.9% 23.8% OK\\n\",\n \"2 codebase_community 28.0% 57.8% OK\\n\",\n \"3 debit_card_specializing 47.0% 65.0% OK\\n\",\n \"4 european_football_2 14.4% 23.8% OK\\n\",\n \"5 financial 21.5% 37.0% OK\\n\",\n \"6 formula_1 29.3% 32.9% OK\\n\",\n \"7 student_club 30.2% 45.0% OK\\n\",\n \"8 superhero 33.0% 52.6% OK\\n\",\n \"9 thrombosis_prediction 18.1% 38.5% OK\\n\",\n \"10 toxicology 53.7% 66.7% OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n }\n ],\n \"source\": [\n \"# 5.2 Run Quality Evaluations and Display as DataFrame\\n\",\n \"import pandas as pd\\n\",\n \"import importlib\\n\",\n \"\\n\",\n \"# Force reload modules to use latest code\\n\",\n \"import src.role_evaluator.quality_metrics\\n\",\n \"import src.role_evaluator.quality_visualizer\\n\",\n \"importlib.reload(src.role_evaluator.quality_metrics)\\n\",\n \"importlib.reload(src.role_evaluator.quality_visualizer)\\n\",\n \"\\n\",\n \"from src.role_evaluator.quality_metrics import print_quality_report\\n\",\n \"\\n\",\n \"# Set display options for full content (no truncation)\\n\",\n \"pd.set_option('display.max_colwidth', None)\\n\",\n \"pd.set_option('display.max_rows', None)\\n\",\n \"pd.set_option('display.width', None)\\n\",\n \"\\n\",\n \"if 'evaluator' in dir():\\n\",\n \" report = evaluator.evaluate_all()\\n\",\n \" \\n\",\n \" # Summary\\n\",\n \" print(f\\\"Overall: {report.total_databases} databases, {report.total_roles} roles, {report.total_entries} entries\\\")\\n\",\n \" print(f\\\"Pass Rate: {report.pass_rate:.1%} ({report.total_databases - len(report.databases_with_issues)}/{report.total_databases} pass)\\\")\\n\",\n \" print(f\\\"\\\\nUsing Thresholds: deny_rate_min={QUALITY_THRESHOLDS['deny_rate_min']}%, \\\"\\n\",\n \" f\\\"max_overlap={QUALITY_THRESHOLDS['max_overlap']:.0%}, \\\"\\n\",\n \" f\\\"coverage_std_min={QUALITY_THRESHOLDS['coverage_std_min']}\\\\n\\\")\\n\",\n \" \\n\",\n \" # Deny Rate DataFrame - Full display\\n\",\n \" if report.deny_rate_metrics:\\n\",\n \" deny_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"Deny%\\\": f\\\"{m.deny_rate:.1f}%\\\",\\n\",\n \" \\\"AllAllow%\\\": f\\\"{m.all_allow_pct:.1f}%\\\",\\n\",\n \" \\\"AllDeny\\\": m.all_deny_queries,\\n\",\n \" \\\"Roles\\\": len(m.roles),\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.deny_rate_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"=== Deny Rate Analysis ===\\\")\\n\",\n \" display(deny_df)\\n\",\n \"\\n\",\n \" # Semantic Quality DataFrame \\n\",\n \" if report.semantic_metrics:\\n\",\n \" sem_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"Similarity\\\": f\\\"{m.avg_similarity:.4f}\\\",\\n\",\n \" \\\"Coverage\\\": f\\\"{m.avg_coverage:.1%}\\\",\\n\",\n \" \\\"Diversity\\\": f\\\"{m.coverage_std:.4f}\\\",\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.semantic_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Semantic Quality (Similarity: role-schema alignment, Coverage: column access, Diversity: coverage std) ===\\\")\\n\",\n \" display(sem_df)\\n\",\n \"\\n\",\n \" # Policy Overlap DataFrame\\n\",\n \" if report.overlap_metrics:\\n\",\n \" ovl_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"AvgOverlap\\\": f\\\"{m.avg_overlap:.1%}\\\",\\n\",\n \" \\\"MaxOverlap\\\": f\\\"{m.max_overlap:.1%}\\\",\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.overlap_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Policy Overlap Analysis ===\\\")\\n\",\n \" display(ovl_df)\\n\",\n \"else:\\n\",\n \" print(\\\" Evaluator not initialized\\\")\\n\",\n \"\\n\",\n \"pd.reset_option('display.max_colwidth')\\n\",\n \"pd.reset_option('display.max_rows')\\n\",\n \"pd.reset_option('display.width')\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 75,\n \"id\": \"89f843e7\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# # 5.3 Quality Dashboard Visualization\\n\",\n \"# import matplotlib.pyplot as plt\\n\",\n \"# from src.role_evaluator.quality_visualizer import RoleQualityVisualizer\\n\",\n \"\\n\",\n \"# visualizer = RoleQualityVisualizer(evaluator=evaluator, report=report)\\n\",\n \"# fig = visualizer.plot_quality_dashboard()\\n\",\n \"# plt.show()\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"86eb26de\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# 5.4 Semantic Alignment Visualization (Column-Level RBAC)\\n\",\n \"# Circle size = column coverage, Red triangles = SystemManager\\n\",\n \"import random\\n\",\n \"import math\\n\",\n \"import matplotlib.pyplot as plt\\n\",\n \"from matplotlib.colors import PowerNorm\\n\",\n \"from matplotlib import colormaps as cm\\n\",\n \"from collections import defaultdict\\n\",\n \"from matplotlib.lines import Line2D\\n\",\n \"import numpy as np\\n\",\n \"\\n\",\n \"sys.path.insert(0, str(project_root / \\\"plot\\\"))\\n\",\n \"from embedding_utils import cosine_similarity\\n\",\n \"\\n\",\n \"# Prepare records (recompute)\\n\",\n \"role_records = evaluator._prepare_role_records()\\n\",\n \"evaluator._compute_embeddings()\\n\",\n \"\\n\",\n \"# Build vis_records with is_system_manager\\n\",\n \"vis_records = []\\n\",\n \"for spec in role_records:\\n\",\n \" schema_emb = evaluator._schema_emb_dict.get(spec[\\\"schema_text\\\"])\\n\",\n \" role_emb = evaluator._role_emb_dict.get(spec[\\\"role_text\\\"])\\n\",\n \" \\n\",\n \" if schema_emb is not None and role_emb is not None:\\n\",\n \" raw_sim = cosine_similarity(role_emb, schema_emb)\\n\",\n \" similarity = max(0.0, min(1.0, (raw_sim + 1.0) / 2.0))\\n\",\n \" else:\\n\",\n \" similarity = 0.5\\n\",\n \" \\n\",\n \" total_cols = spec.get(\\\"total_columns\\\", 1)\\n\",\n \" accessible_cols = spec.get(\\\"accessible_columns\\\", 0)\\n\",\n \" col_coverage = accessible_cols / total_cols if total_cols > 0 else 0\\n\",\n \" \\n\",\n \" vis_records.append({\\n\",\n \" \\\"db_id\\\": spec[\\\"db_id\\\"],\\n\",\n \" \\\"role\\\": spec[\\\"role\\\"],\\n\",\n \" \\\"similarity\\\": similarity,\\n\",\n \" \\\"is_system_manager\\\": spec.get(\\\"is_system_manager\\\", False),\\n\",\n \" \\\"column_coverage\\\": col_coverage,\\n\",\n \" })\\n\",\n \"\\n\",\n \"sm_count = sum(1 for r in vis_records if r[\\\"is_system_manager\\\"])\\n\",\n \"print(f\\\"Total roles: {len(vis_records)}, SystemManager: {sm_count}\\\")\\n\",\n \"\\n\",\n \"# Plot function\\n\",\n \"def plot_semantic(records, output_path, title, dbs_per_panel=12):\\n\",\n \" db_to_entries = defaultdict(list)\\n\",\n \" sims = []\\n\",\n \" for rec in records:\\n\",\n \" sims.append(rec[\\\"similarity\\\"])\\n\",\n \" size = 90.0 + rec[\\\"column_coverage\\\"] * 220.0\\n\",\n \" db_to_entries[rec[\\\"db_id\\\"]].append({\\n\",\n \" \\\"sim\\\": rec[\\\"similarity\\\"], \\\"size\\\": size,\\n\",\n \" \\\"is_sm\\\": rec[\\\"is_system_manager\\\"],\\n\",\n \" })\\n\",\n \" \\n\",\n \" db_ids = sorted(db_to_entries.keys())\\n\",\n \" panels = [db_ids[i:i+dbs_per_panel] for i in range(0, len(db_ids), dbs_per_panel)]\\n\",\n \" \\n\",\n \" min_s, max_s = min(sims), max(sims)\\n\",\n \" if math.isclose(min_s, max_s): max_s = min_s + 1e-6\\n\",\n \" span = max_s - min_s\\n\",\n \" norm = PowerNorm(gamma=0.55, vmin=min_s, vmax=max_s)\\n\",\n \" cmap = cm[\\\"viridis\\\"]\\n\",\n \" \\n\",\n \" fig = plt.figure(figsize=(11, sum(0.35*len(p)+0.6 for p in panels)+2))\\n\",\n \" gs = fig.add_gridspec(len(panels), 1, height_ratios=[0.35*len(p)+0.6 for p in panels], hspace=0.18)\\n\",\n \" axes = []\\n\",\n \" \\n\",\n \" random.seed(42)\\n\",\n \" sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)\\n\",\n \" sm.set_array([])\\n\",\n \" \\n\",\n \" for pi, chunk in enumerate(panels):\\n\",\n \" ax = fig.add_subplot(gs[pi, 0])\\n\",\n \" axes.append(ax)\\n\",\n \" for ri, db in enumerate(chunk):\\n\",\n \" y = float(ri)\\n\",\n \" ax.axhline(y, color=\\\"#d0d0d0\\\", linestyle=\\\"--\\\", lw=0.9, zorder=0)\\n\",\n \" for e in db_to_entries[db]:\\n\",\n \" x = (e[\\\"sim\\\"] - min_s) / span + random.uniform(-0.012, 0.012)\\n\",\n \" if e[\\\"is_sm\\\"]:\\n\",\n \" ax.scatter(x, y, s=250, c=\\\"#d62728\\\", alpha=0.95,\\n\",\n \" edgecolors=\\\"#8c1c15\\\", lw=1.0, marker=\\\"^\\\", zorder=4)\\n\",\n \" else:\\n\",\n \" ax.scatter(x, y + random.uniform(-0.16, 0.16), s=e[\\\"size\\\"]*2.5,\\n\",\n \" c=[e[\\\"sim\\\"]], cmap=cmap, norm=norm, alpha=0.82,\\n\",\n \" edgecolors=\\\"#333\\\", lw=0.45, marker=\\\"o\\\")\\n\",\n \" ax.set_yticks(range(len(chunk)))\\n\",\n \" ax.set_yticklabels(chunk, fontsize=10, weight='bold')\\n\",\n \" ax.set_xlim(-0.05, 1.05)\\n\",\n \" ax.set_ylim(-0.6, len(chunk)-0.4)\\n\",\n \" ax.set_xticks(np.linspace(0, 1, 5))\\n\",\n \" ax.set_xticklabels([f\\\"{min_s + p*span:.2f}\\\" for p in np.linspace(0,1,5)], fontsize=9)\\n\",\n \" ax.grid(axis=\\\"x\\\", ls=\\\":\\\", lw=0.5, alpha=0.45)\\n\",\n \" \\n\",\n \" axes[-1].set_xlabel(\\\"Semantic similarity\\\", fontsize=12, weight='bold')\\n\",\n \" fig.suptitle(f\\\"{title}: Role-Schema Alignment\\\", fontsize=14, y=0.98, weight='bold')\\n\",\n \" cbar = fig.colorbar(sm, ax=axes, fraction=0.03, pad=0.015)\\n\",\n \" cbar.set_label(\\\"Similarity\\\", fontsize=10)\\n\",\n \" \\n\",\n \" legend = [\\n\",\n \" Line2D([0],[0], marker='^', color='w', markerfacecolor='#d62728',\\n\",\n \" markeredgecolor='#8c1c15', markersize=12, label='SystemManager'),\\n\",\n \" Line2D([0],[0], marker='o', color='w', markerfacecolor='#21918c',\\n\",\n \" markeredgecolor='#333', markersize=8, label='Other (size=col coverage)'),\\n\",\n \" ]\\n\",\n \" fig.legend(handles=legend, loc='upper right', fontsize=9)\\n\",\n \" plt.tight_layout(rect=[0,0,0.95,0.95])\\n\",\n \" fig.savefig(output_path, dpi=150, bbox_inches='tight')\\n\",\n \" plt.close(fig)\\n\",\n \" print(f\\\"Saved: {output_path}\\\")\\n\",\n \"\\n\",\n \"output_path = project_root / \\\"plot\\\" / \\\"column_level_rbac_semantic.png\\\"\\n\",\n \"plot_semantic(vis_records, output_path, \\\"Column-Level RBAC (BIRD)\\\")\\n\",\n \"from IPython.display import Image, display\\n\",\n \"display(Image(filename=str(output_path)))\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 13,\n \"id\": \"2dd807b4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Databases Requiring Review (1/11) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Deny%</th>\\n\",\n \" <th>AllAllow%</th>\\n\",\n \" <th>Coverage</th>\\n\",\n \" <th>CovStd</th>\\n\",\n \" <th>MaxOverlap</th>\\n\",\n \" <th>Issues</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>63.1%</td>\\n\",\n \" <td>5.6%</td>\\n\",\n \" <td>50.8%</td>\\n\",\n \" <td>0.059</td>\\n\",\n \" <td>72.3%</td>\\n\",\n \" <td>all-deny queries found (1) - SystemManager coverage broken; Low role diversity</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Deny% AllAllow% Coverage CovStd MaxOverlap \\\\\\n\",\n \"0 california_schools 63.1% 5.6% 50.8% 0.059 72.3% \\n\",\n \"\\n\",\n \" Issues \\n\",\n \"0 all-deny queries found (1) - SystemManager coverage broken; Low role diversity \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"Issue Thresholds:\\n\",\n \" - Deny% < 5%: roles too permissive\\n\",\n \" - Coverage > 85%: most roles have near-full access\\n\",\n \" - CovStd < 0.065: roles have similar coverage (low diversity)\\n\",\n \" - MaxOverlap >80%: redundant roles with similar policies\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 5.5 Export Problematic Databases as DataFrame\\n\",\n \"if report.databases_with_issues:\\n\",\n \" issues_data = []\\n\",\n \" for db_id in report.databases_with_issues:\\n\",\n \" row = {\\\"Database\\\": db_id}\\n\",\n \" \\n\",\n \" if db_id in report.deny_rate_metrics:\\n\",\n \" m = report.deny_rate_metrics[db_id]\\n\",\n \" row[\\\"Deny%\\\"] = f\\\"{m.deny_rate:.1f}%\\\"\\n\",\n \" row[\\\"AllAllow%\\\"] = f\\\"{m.all_allow_pct:.1f}%\\\"\\n\",\n \" \\n\",\n \" if db_id in report.semantic_metrics:\\n\",\n \" m = report.semantic_metrics[db_id]\\n\",\n \" row[\\\"Coverage\\\"] = f\\\"{m.avg_coverage:.1%}\\\" # Source of \\\"Most roles too permissive\\\" (>90%)\\n\",\n \" row[\\\"CovStd\\\"] = f\\\"{m.coverage_std:.3f}\\\" # Source of \\\"Low role diversity\\\" (<0.1)\\n\",\n \" \\n\",\n \" if db_id in report.overlap_metrics:\\n\",\n \" m = report.overlap_metrics[db_id]\\n\",\n \" row[\\\"MaxOverlap\\\"] = f\\\"{m.max_overlap:.1%}\\\" # Source of \\\"High max overlap\\\" (>70%)\\n\",\n \" \\n\",\n \" row[\\\"Issues\\\"] = \\\"; \\\".join(report.all_issues.get(db_id, []))\\n\",\n \" issues_data.append(row)\\n\",\n \" \\n\",\n \" issues_df = pd.DataFrame(issues_data)\\n\",\n \" print(f\\\"=== Databases Requiring Review ({len(issues_df)}/{report.total_databases}) ===\\\")\\n\",\n \" \\n\",\n \" # Display with full column width for Issues column\\n\",\n \" pd.set_option('display.max_colwidth', None)\\n\",\n \" pd.set_option('display.max_rows', None)\\n\",\n \" display(issues_df)\\n\",\n \" pd.reset_option('display.max_colwidth')\\n\",\n \" pd.reset_option('display.max_rows')\\n\",\n \" \\n\",\n \" print(\\\"\\\\nIssue Thresholds:\\\")\\n\",\n \" print(\\\" - Deny% < 5%: roles too permissive\\\")\\n\",\n \" print(\\\" - Coverage > 85%: most roles have near-full access\\\")\\n\",\n \" print(\\\" - CovStd < 0.065: roles have similar coverage (low diversity)\\\")\\n\",\n \" print(\\\" - MaxOverlap >80%: redundant roles with similar policies\\\")\\n\",\n \"\\n\",\n \"else:\\n\",\n \" print(\\\"[OK] All databases pass quality checks!\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"7586c455\",\n \"metadata\": {},\n \"source\": [\n \"## 6. Regenerate Roles for Problematic Databases\\n\",\n \"\\n\",\n \"Human-in-the-loop workflow for fixing quality issues:\\n\",\n \"\\n\",\n \"| Step | Cell | Description |\\n\",\n \"|------|------|-------------|\\n\",\n \"| 1 | 6.1 | Select database, view schema and current roles |\\n\",\n \"| 2 | 6.2 | Write regeneration hints (edit manually) |\\n\",\n \"| 3 | 6.3 | Execute regeneration with LLM |\\n\",\n \"| 4 | 6.4 | Preview and compare OLD vs NEW |\\n\",\n \"| 5 | 6.4.1 | Select & combine roles from OLD and NEW |\\n\",\n \"| 6 | 6.5 | Apply changes if satisfied |\\n\",\n \"| 7 | 6.6 | Regenerate RBAC Dataset with updated roles |\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 32,\n \"id\": \"70f25858\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"Database: formula_1\\n\",\n \"======================================================================\\n\",\n \"\\n\",\n \"=== Schema (13 tables, 94 columns) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Table</th>\\n\",\n \" <th>Column</th>\\n\",\n \" <th>Type</th>\\n\",\n \" <th>PK</th>\\n\",\n \" <th>FK</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>circuitId</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td>Yes</td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>circuitRef</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>name</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>location</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>country</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>...</th>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>89</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLap</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>90</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>rank</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>91</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLapTime</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>92</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLapSpeed</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>93</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>statusId</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td>status.statusId</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"<p>94 rows × 5 columns</p>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Table Column Type PK FK\\n\",\n \"0 circuits circuitId INTEGER Yes \\n\",\n \"1 circuits circuitRef TEXT \\n\",\n \"2 circuits name TEXT \\n\",\n \"3 circuits location TEXT \\n\",\n \"4 circuits country TEXT \\n\",\n \".. ... ... ... ... ...\\n\",\n \"89 results fastestLap INTEGER \\n\",\n \"90 results rank INTEGER \\n\",\n \"91 results fastestLapTime TEXT \\n\",\n \"92 results fastestLapSpeed TEXT \\n\",\n \"93 results statusId INTEGER status.statusId\\n\",\n \"\\n\",\n \"[94 rows x 5 columns]\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Current Roles (5) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Columns</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap times, pit stops, and qualifying results</td>\\n\",\n \" <td>9</td>\\n\",\n \" <td>65 (*)</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date, time]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>StandingsManager</td>\\n\",\n \" <td>Manages championship standings and constructor/driver points calculations</td>\\n\",\n \" <td>7</td>\\n\",\n \" <td>43 (*)</td>\\n\",\n \" <td>constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; results: [resultId, raceId, driverId, constructorId, position, positionOrder, points]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>MediaCoordinator</td>\\n\",\n \" <td>Provides public information for media and fan engagement</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>33 (*)</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, constructorRef, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [*]; races: [raceId, year, round, circuitId, name, date, time, url]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal details including date of birth</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>32 (*)</td>\\n\",\n \" <td>drivers: [*]; races: [raceId, year, round, name, date]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, statusId]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintenance and data integrity</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>94 (*)</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*]</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Role \\\\\\n\",\n \"0 RaceAnalyst \\n\",\n \"1 StandingsManager \\n\",\n \"2 MediaCoordinator \\n\",\n \"3 DriverManager \\n\",\n \"4 SystemManager \\n\",\n \"\\n\",\n \" Description \\\\\\n\",\n \"0 Analyzes race performance data including lap times, pit stops, and qualifying results \\n\",\n \"1 Manages championship standings and constructor/driver points calculations \\n\",\n \"2 Provides public information for media and fan engagement \\n\",\n \"3 Manages driver information and personal details including date of birth \\n\",\n \"4 Full administrative access for system maintenance and data integrity \\n\",\n \"\\n\",\n \" Tables Columns \\\\\\n\",\n \"0 9 65 (*) \\n\",\n \"1 7 43 (*) \\n\",\n \"2 6 33 (*) \\n\",\n \"3 4 32 (*) \\n\",\n \"4 13 94 (*) \\n\",\n \"\\n\",\n \" Policy \\n\",\n \"0 circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date, time]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]; status: [statusId, status] \\n\",\n \"1 constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; results: [resultId, raceId, driverId, constructorId, position, positionOrder, points] \\n\",\n \"2 circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, constructorRef, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [*]; races: [raceId, year, round, circuitId, name, date, time, url]; status: [statusId, status] \\n\",\n \"3 drivers: [*]; races: [raceId, year, round, name, date]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, statusId]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins] \\n\",\n \"4 circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*] \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Role x Table Matrix (column counts, * expanded) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<style type=\\\"text/css\\\">\\n\",\n \"#T_3c946_row0_col0, #T_3c946_row0_col1, #T_3c946_row0_col2, #T_3c946_row0_col3, #T_3c946_row0_col6, #T_3c946_row0_col7, #T_3c946_row0_col8, #T_3c946_row0_col11, #T_3c946_row0_col12, #T_3c946_row1_col1, #T_3c946_row1_col2, #T_3c946_row1_col4, #T_3c946_row1_col6, #T_3c946_row1_col7, #T_3c946_row1_col8, #T_3c946_row1_col10, #T_3c946_row2_col1, #T_3c946_row2_col2, #T_3c946_row2_col4, #T_3c946_row2_col11, #T_3c946_row3_col0, #T_3c946_row3_col6, #T_3c946_row3_col7, #T_3c946_row3_col8, #T_3c946_row3_col11, #T_3c946_row3_col12 {\\n\",\n \" background-color: #f7fbff;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col4, #T_3c946_row2_col5, #T_3c946_row2_col7, #T_3c946_row2_col9, #T_3c946_row3_col2, #T_3c946_row3_col4, #T_3c946_row3_col5, #T_3c946_row3_col10, #T_3c946_row4_col2, #T_3c946_row4_col4, #T_3c946_row4_col7 {\\n\",\n \" background-color: #99c7e0;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col5, #T_3c946_row2_col8, #T_3c946_row4_col0, #T_3c946_row4_col5, #T_3c946_row4_col8 {\\n\",\n \" background-color: #6aaed6;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col9, #T_3c946_row1_col3, #T_3c946_row3_col1, #T_3c946_row4_col1, #T_3c946_row4_col3 {\\n\",\n \" background-color: #bdd7ec;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col10 {\\n\",\n \" background-color: #4695c8;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row1_col0, #T_3c946_row1_col5, #T_3c946_row1_col9, #T_3c946_row4_col9 {\\n\",\n \" background-color: #82bbdb;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row1_col11, #T_3c946_row1_col12, #T_3c946_row2_col12, #T_3c946_row4_col11, #T_3c946_row4_col12 {\\n\",\n \" background-color: #e1edf8;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col0, #T_3c946_row2_col6, #T_3c946_row3_col9, #T_3c946_row4_col6 {\\n\",\n \" background-color: #abd0e6;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col3, #T_3c946_row3_col3 {\\n\",\n \" background-color: #ccdff1;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col10 {\\n\",\n \" background-color: #083e81;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row4_col10 {\\n\",\n \" background-color: #08306b;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"</style>\\n\",\n \"<table id=\\\"T_3c946\\\">\\n\",\n \" <thead>\\n\",\n \" <tr>\\n\",\n \" <th class=\\\"index_name level0\\\" >Table</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col0\\\" class=\\\"col_heading level0 col0\\\" >circuits</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col1\\\" class=\\\"col_heading level0 col1\\\" >constructorResults</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col2\\\" class=\\\"col_heading level0 col2\\\" >constructorStandings</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col3\\\" class=\\\"col_heading level0 col3\\\" >constructors</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col4\\\" class=\\\"col_heading level0 col4\\\" >driverStandings</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col5\\\" class=\\\"col_heading level0 col5\\\" >drivers</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col6\\\" class=\\\"col_heading level0 col6\\\" >lapTimes</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col7\\\" class=\\\"col_heading level0 col7\\\" >pitStops</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col8\\\" class=\\\"col_heading level0 col8\\\" >qualifying</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col9\\\" class=\\\"col_heading level0 col9\\\" >races</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col10\\\" class=\\\"col_heading level0 col10\\\" >results</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col11\\\" class=\\\"col_heading level0 col11\\\" >seasons</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col12\\\" class=\\\"col_heading level0 col12\\\" >status</th>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th class=\\\"index_name level0\\\" >Role</th>\\n\",\n \" <th class=\\\"blank col0\\\" > </th>\\n\",\n \" <th class=\\\"blank col1\\\" > </th>\\n\",\n \" <th class=\\\"blank col2\\\" > </th>\\n\",\n \" <th class=\\\"blank col3\\\" > </th>\\n\",\n \" <th class=\\\"blank col4\\\" > </th>\\n\",\n \" <th class=\\\"blank col5\\\" > </th>\\n\",\n \" <th class=\\\"blank col6\\\" > </th>\\n\",\n \" <th class=\\\"blank col7\\\" > </th>\\n\",\n \" <th class=\\\"blank col8\\\" > </th>\\n\",\n \" <th class=\\\"blank col9\\\" > </th>\\n\",\n \" <th class=\\\"blank col10\\\" > </th>\\n\",\n \" <th class=\\\"blank col11\\\" > </th>\\n\",\n \" <th class=\\\"blank col12\\\" > </th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row0\\\" class=\\\"row_heading level0 row0\\\" >DriverManager</th>\\n\",\n \" <td id=\\\"T_3c946_row0_col0\\\" class=\\\"data row0 col0\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col1\\\" class=\\\"data row0 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col2\\\" class=\\\"data row0 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col3\\\" class=\\\"data row0 col3\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col4\\\" class=\\\"data row0 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col5\\\" class=\\\"data row0 col5\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col6\\\" class=\\\"data row0 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col7\\\" class=\\\"data row0 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col8\\\" class=\\\"data row0 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col9\\\" class=\\\"data row0 col9\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col10\\\" class=\\\"data row0 col10\\\" >11.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col11\\\" class=\\\"data row0 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col12\\\" class=\\\"data row0 col12\\\" >0.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row1\\\" class=\\\"row_heading level0 row1\\\" >MediaCoordinator</th>\\n\",\n \" <td id=\\\"T_3c946_row1_col0\\\" class=\\\"data row1 col0\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col1\\\" class=\\\"data row1 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col2\\\" class=\\\"data row1 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col3\\\" class=\\\"data row1 col3\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col4\\\" class=\\\"data row1 col4\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col5\\\" class=\\\"data row1 col5\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col6\\\" class=\\\"data row1 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col7\\\" class=\\\"data row1 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col8\\\" class=\\\"data row1 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col9\\\" class=\\\"data row1 col9\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col10\\\" class=\\\"data row1 col10\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col11\\\" class=\\\"data row1 col11\\\" >2.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col12\\\" class=\\\"data row1 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row2\\\" class=\\\"row_heading level0 row2\\\" >RaceAnalyst</th>\\n\",\n \" <td id=\\\"T_3c946_row2_col0\\\" class=\\\"data row2 col0\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col1\\\" class=\\\"data row2 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col2\\\" class=\\\"data row2 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col3\\\" class=\\\"data row2 col3\\\" >4.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col4\\\" class=\\\"data row2 col4\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col5\\\" class=\\\"data row2 col5\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col6\\\" class=\\\"data row2 col6\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col7\\\" class=\\\"data row2 col7\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col8\\\" class=\\\"data row2 col8\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col9\\\" class=\\\"data row2 col9\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col10\\\" class=\\\"data row2 col10\\\" >17.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col11\\\" class=\\\"data row2 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col12\\\" class=\\\"data row2 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row3\\\" class=\\\"row_heading level0 row3\\\" >StandingsManager</th>\\n\",\n \" <td id=\\\"T_3c946_row3_col0\\\" class=\\\"data row3 col0\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col1\\\" class=\\\"data row3 col1\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col2\\\" class=\\\"data row3 col2\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col3\\\" class=\\\"data row3 col3\\\" >4.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col4\\\" class=\\\"data row3 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col5\\\" class=\\\"data row3 col5\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col6\\\" class=\\\"data row3 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col7\\\" class=\\\"data row3 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col8\\\" class=\\\"data row3 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col9\\\" class=\\\"data row3 col9\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col10\\\" class=\\\"data row3 col10\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col11\\\" class=\\\"data row3 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col12\\\" class=\\\"data row3 col12\\\" >0.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row4\\\" class=\\\"row_heading level0 row4\\\" >SystemManager</th>\\n\",\n \" <td id=\\\"T_3c946_row4_col0\\\" class=\\\"data row4 col0\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col1\\\" class=\\\"data row4 col1\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col2\\\" class=\\\"data row4 col2\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col3\\\" class=\\\"data row4 col3\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col4\\\" class=\\\"data row4 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col5\\\" class=\\\"data row4 col5\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col6\\\" class=\\\"data row4 col6\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col7\\\" class=\\\"data row4 col7\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col8\\\" class=\\\"data row4 col8\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col9\\\" class=\\\"data row4 col9\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col10\\\" class=\\\"data row4 col10\\\" >18.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col11\\\" class=\\\"data row4 col11\\\" >2.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col12\\\" class=\\\"data row4 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\"\n ],\n \"text/plain\": [\n \"<pandas.io.formats.style.Styler at 0x7f5045bdae00>\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"[SUMMARY] 1 database(s) selected: ['formula_1']\\n\",\n \"[NEXT] Edit hints in Cell 6.2, then run Cell 6.3\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.1 Select Database(s) & View Current State\\n\",\n \"# Supports single db (str) or multiple dbs (list)\\n\",\n \"\\n\",\n \"TARGET_DBS = [\\\"formula_1\\\"] # Single: \\\"toxicology\\\" or Multiple: [\\\"toxicology\\\", \\\"california_schools\\\"]\\n\",\n \"\\n\",\n \"# Normalize to list\\n\",\n \"if isinstance(TARGET_DBS, str):\\n\",\n \" TARGET_DBS = [TARGET_DBS]\\n\",\n \"\\n\",\n \"# Load current role assignments\\n\",\n \"with open(ROLE_ASSIGNMENTS_PATH, 'r') as f:\\n\",\n \" all_assignments = json.load(f)\\n\",\n \"\\n\",\n \"available_dbs = set(all_assignments.get('assignments', {}).keys())\\n\",\n \"valid_target_dbs = [db for db in TARGET_DBS if db in available_dbs]\\n\",\n \"invalid_dbs = [db for db in TARGET_DBS if db not in available_dbs]\\n\",\n \"\\n\",\n \"if invalid_dbs:\\n\",\n \" print(f\\\"[WARN] Databases not found: {invalid_dbs}\\\")\\n\",\n \" print(f\\\"Available: {sorted(available_dbs)}\\\\n\\\")\\n\",\n \"\\n\",\n \"# Store schemas for later use\\n\",\n \"target_schemas = {}\\n\",\n \"\\n\",\n \"for TARGET_DB in valid_target_dbs:\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"Database: {TARGET_DB}\\\")\\n\",\n \" print(f\\\"{'='*70}\\\")\\n\",\n \" \\n\",\n \" # --- Schema Summary ---\\n\",\n \" db_path = bird_db_dir / TARGET_DB / f\\\"{TARGET_DB}.sqlite\\\"\\n\",\n \" if db_path.exists():\\n\",\n \" target_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" target_schemas[TARGET_DB] = target_schema\\n\",\n \" \\n\",\n \" schema_rows = []\\n\",\n \" for table, info in target_schema.items():\\n\",\n \" for col in info['columns']:\\n\",\n \" schema_rows.append({\\n\",\n \" \\\"Table\\\": table,\\n\",\n \" \\\"Column\\\": col['name'],\\n\",\n \" \\\"Type\\\": col['type'],\\n\",\n \" \\\"PK\\\": \\\"Yes\\\" if col['pk'] else \\\"\\\",\\n\",\n \" \\\"FK\\\": col.get('fk', '') or \\\"\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" schema_df = pd.DataFrame(schema_rows)\\n\",\n \" print(f\\\"\\\\n=== Schema ({len(target_schema)} tables, {len(schema_df)} columns) ===\\\")\\n\",\n \" display(schema_df)\\n\",\n \" else:\\n\",\n \" print(f\\\"[ERROR] Database not found: {db_path}\\\")\\n\",\n \" target_schemas[TARGET_DB] = None\\n\",\n \" continue\\n\",\n \"\\n\",\n \" # --- Current Roles ---\\n\",\n \" if TARGET_DB in all_assignments.get('assignments', {}):\\n\",\n \" current_roles = all_assignments['assignments'][TARGET_DB]\\n\",\n \" \\n\",\n \" # Set display options for full content\\n\",\n \" pd.set_option('display.max_colwidth', None)\\n\",\n \" \\n\",\n \" roles_rows = []\\n\",\n \" for role_info in current_roles:\\n\",\n \" policy = role_info.get('policy', {})\\n\",\n \" total_access = count_policy_columns(policy, target_schema)\\n\",\n \" has_wildcard = has_wildcard_policy(policy)\\n\",\n \" cols_display = f\\\"{total_access}\\\" + (\\\" (*)\\\" if has_wildcard else \\\"\\\")\\n\",\n \" # Full policy string\\n\",\n \" policy_str = \\\"; \\\".join([\\n\",\n \" f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" \\n\",\n \" for t, c in policy.items()\\n\",\n \" ])\\n\",\n \" roles_rows.append({\\n\",\n \" \\\"Role\\\": role_info['role'],\\n\",\n \" \\\"Description\\\": role_info.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Columns\\\": cols_display,\\n\",\n \" \\\"Policy\\\": policy_str\\n\",\n \" })\\n\",\n \" \\n\",\n \" roles_df = pd.DataFrame(roles_rows)\\n\",\n \" print(f\\\"\\\\n=== Current Roles ({len(current_roles)}) ===\\\")\\n\",\n \" display(roles_df)\\n\",\n \" \\n\",\n \" pd.reset_option('display.max_colwidth')\\n\",\n \" \\n\",\n \" # Policy detail matrix\\n\",\n \" policy_rows = []\\n\",\n \" for role_info in current_roles:\\n\",\n \" policy = role_info.get('policy', {})\\n\",\n \" for table, cols in policy.items():\\n\",\n \" num_cols = count_policy_columns({table: cols}, target_schema)\\n\",\n \" policy_rows.append({\\n\",\n \" \\\"Role\\\": role_info['role'],\\n\",\n \" \\\"Table\\\": table,\\n\",\n \" \\\"NumCols\\\": num_cols\\n\",\n \" })\\n\",\n \" \\n\",\n \" if policy_rows:\\n\",\n \" policy_df = pd.DataFrame(policy_rows)\\n\",\n \" pivot = policy_df.pivot_table(index='Role', columns='Table', values='NumCols', fill_value=0)\\n\",\n \" print(\\\"\\\\n=== Role x Table Matrix (column counts, * expanded) ===\\\")\\n\",\n \" display(pivot.style.background_gradient(cmap='Blues', axis=None))\\n\",\n \" \\n\",\n \" # Quality issues\\n\",\n \" if 'report' in dir() and TARGET_DB in report.all_issues:\\n\",\n \" print(f\\\"\\\\n=== Quality Issues ===\\\")\\n\",\n \" issues = report.all_issues[TARGET_DB]\\n\",\n \" if TARGET_DB in report.deny_rate_metrics:\\n\",\n \" m = report.deny_rate_metrics[TARGET_DB]\\n\",\n \" issues_df = pd.DataFrame([{\\n\",\n \" \\\"Deny Rate\\\": f\\\"{m.deny_rate:.1f}%\\\",\\n\",\n \" \\\"All-Allow %\\\": f\\\"{m.all_allow_pct:.1f}%\\\",\\n\",\n \" \\\"Issues\\\": \\\"; \\\".join(issues)\\n\",\n \" }])\\n\",\n \" display(issues_df)\\n\",\n \" else:\\n\",\n \" print(f\\\"[ERROR] No roles found for {TARGET_DB}\\\")\\n\",\n \"\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"[SUMMARY] {len(valid_target_dbs)} database(s) selected: {valid_target_dbs}\\\")\\n\",\n \" print(\\\"[NEXT] Edit hints in Cell 6.2, then run Cell 6.3\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[INFO] No databases selected. Edit TARGET_DBS above and re-run.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 34,\n \"id\": \"f8416bc4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Applying shared hint to 1 database(s) ===\\n\",\n \"\\n\",\n \" ✓ formula_1: 13 tables, 94 columns\\n\",\n \"\\n\",\n \"[NEXT] Run Cell 6.3 to regenerate all 1 database(s)\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.2 Write Regeneration Hints (Human-in-the-Loop)\\n\",\n \"# Write ONE shared hint for all selected databases (they have similar issues)\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EDIT HERE: Shared problem description for all selected DBs\\n\",\n \"# Supports {db}, {num_tables}, {num_cols} placeholders\\n\",\n \"# ============================================================\\n\",\n \"# PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"# [PROBLEM]\\n\",\n \"# Low deny rate - almost all queries are allowed by all roles (roles too permissive)\\n\",\n \"# Low diversity - all roles have similar access patterns (coverage_std too low)\\n\",\n \"\\n\",\n \"\\n\",\n \"# [ROOT CAUSE]\\n\",\n \"# - Most roles can access most/all columns, so very few queries get denied\\n\",\n \"# - This defeats the purpose of RBAC - no meaningful access control\\n\",\n \"\\n\",\n \"# [DIRECTION]\\n\",\n \"# - Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"# - Create restrictive roles that only access specific subsets of data\\n\",\n \"# - Each role should have a clear, limited scope\\n\",\n \"# - Some roles should be restricted to fewer tables\\n\",\n \"# - Do not frequently giving non-admin roles \\\"*\\\" (all columns) access\\n\",\n \"# - Create clear functional boundaries between roles\\n\",\n \"# \\\"\\\"\\\"\\n\",\n \"\\n\",\n \"# All-denied cases - even SystemManager role cannot access some columns\\n\",\n \"# - Ensure SystemManager role has full access to all tables and columns\\n\",\n \"\\n\",\n \"\\n\",\n \"PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"[PROBLEM]\\n\",\n \"Low semantic alignment - roles do not align well with schema\\n\",\n \"All-denied cases - even SystemManager role cannot access some columns\\n\",\n \"\\n\",\n \"[ROOT CAUSE]\\n\",\n \"- This defeats the purpose of RBAC - no meaningful access control\\n\",\n \"\\n\",\n \"[DIRECTION]\\n\",\n \"- Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"- Ensure SystemManager role has full access to all tables and columns\\n\",\n \"- Review other roles to ensure they align with specific functional areas\\n\",\n \"- Each role should have a clear, limited scope\\n\",\n \"- Create meaningful semantic role names and descriptions that reflect their access patterns\\n\",\n \"\\\"\\\"\\\"\\n\",\n \"\\n\",\n \"\\n\",\n \"# PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"# [PROBLEM]\\n\",\n \"# Low diversity - all roles have similar access patterns (coverage_std too low)\\n\",\n \"\\n\",\n \"# [ROOT CAUSE]\\n\",\n \"# - This defeats the purpose of RBAC - no meaningful access control differentiation\\n\",\n \"\\n\",\n \"# [DIRECTION]\\n\",\n \"# - Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"# - Create restrictive roles that only access specific subsets of data\\n\",\n \"# - Each role should have a clear, limited scope\\n\",\n \"# - Some roles should be restricted to fewer tables\\n\",\n \"# - Do not frequently giving non-admin roles \\\"*\\\" (all columns) access\\n\",\n \"# - Create clear functional boundaries between roles\\n\",\n \"# \\\"\\\"\\\"\\n\",\n \"\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# Auto-apply to all selected databases (no need to edit below)\\n\",\n \"# ============================================================\\n\",\n \"REGENERATION_HINTS = {}\\n\",\n \"\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"=== Applying shared hint to {len(valid_target_dbs)} database(s) ===\\\\n\\\")\\n\",\n \" for db in valid_target_dbs:\\n\",\n \" schema = target_schemas.get(db, {})\\n\",\n \" num_tables = len(schema)\\n\",\n \" num_cols = sum(len(t['columns']) for t in schema.values())\\n\",\n \" \\n\",\n \" # Fill placeholders\\n\",\n \" REGENERATION_HINTS[db] = PROBLEM_TO_FIX.format(\\n\",\n \" db=db, num_tables=num_tables, num_cols=num_cols\\n\",\n \" )\\n\",\n \" print(f\\\" ✓ {db}: {num_tables} tables, {num_cols} columns\\\")\\n\",\n \"\\n\",\n \" print(f\\\"\\\\n[NEXT] Run Cell 6.3 to regenerate all {len(valid_target_dbs)} database(s)\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[INFO] No databases selected. Run Cell 6.1 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 68,\n \"id\": \"91109a7c\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Regenerating roles for 1 database(s)...\\n\",\n \"============================================================\\n\",\n \"\\n\",\n \"[formula_1] Generating...\\n\",\n \" ✓ 5 roles (5 valid)\\n\",\n \"\\n\",\n \"============================================================\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Status</th>\\n\",\n \" <th>Roles</th>\\n\",\n \" <th>Valid</th>\\n\",\n \" <th>Tokens</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>success</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>3058</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Status Roles Valid Tokens\\n\",\n \"0 formula_1 success 5 5 3058\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"[OK] All databases regenerated. Run Cell 6.4 to preview.\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.3 Execute Regeneration with Hints (supports multiple databases)\\n\",\n \"\\n\",\n \"def regenerate_single_database(db_name: str, hint: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Regenerate roles for a single database with guidance hints.\\\"\\\"\\\"\\n\",\n \" db_path = bird_db_dir / db_name / f\\\"{db_name}.sqlite\\\"\\n\",\n \" \\n\",\n \" if not db_path.exists():\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": \\\"Database not found\\\"}\\n\",\n \" \\n\",\n \" try:\\n\",\n \" db_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" db_schema_text = format_schema_for_prompt(db_schema, db_name)\\n\",\n \" \\n\",\n \" enhanced_system_prompt = f\\\"{COLUMN_LEVEL_SYSTEM_PROMPT}\\\\n\\\\n[OPTIMIZATION GUIDANCE]\\\\n{hint}\\\"\\n\",\n \" user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=db_schema_text)\\n\",\n \" \\n\",\n \" response = oracle.query(\\n\",\n \" prompt_sys=enhanced_system_prompt,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3,\\n\",\n \" top_p=0.9,\\n\",\n \" max_completion_tokens=DEFAULT_MAX_TOKENS\\n\",\n \" )\\n\",\n \" \\n\",\n \" llm_response = response['answer']\\n\",\n \" usage = response.get('usage', {})\\n\",\n \" roles = parse_column_level_roles(llm_response)\\n\",\n \" \\n\",\n \" if not roles:\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"parse_error\\\", \\n\",\n \" \\\"error\\\": \\\"Failed to parse LLM response\\\", \\\"raw_response\\\": llm_response[:500]}\\n\",\n \" \\n\",\n \" validation = validate_roles_against_schema(roles, str(db_path))\\n\",\n \" assignments = [{\\\"role\\\": r[\\\"role\\\"], \\\"description\\\": r.get(\\\"description\\\", \\\"\\\"), \\n\",\n \" \\\"policy\\\": r.get(\\\"columns\\\", {})} for r in roles]\\n\",\n \" \\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"success\\\", \\\"roles_count\\\": len(assignments),\\n\",\n \" \\\"valid_roles\\\": validation['valid_roles'], \\\"all_valid\\\": validation['all_valid'],\\n\",\n \" \\\"assignments\\\": assignments, \\\"usage\\\": usage, \\\"raw_response\\\": llm_response}\\n\",\n \" except Exception as e:\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": str(e)}\\n\",\n \"\\n\",\n \"# Execute for all target databases\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"Regenerating roles for {len(valid_target_dbs)} database(s)...\\\")\\n\",\n \" print(\\\"=\\\"*60)\\n\",\n \"\\n\",\n \" regenerated_results = {} # Store all results\\n\",\n \" all_success = True\\n\",\n \"\\n\",\n \" for db_name in valid_target_dbs:\\n\",\n \" hint = REGENERATION_HINTS.get(db_name, PROBLEM_TO_FIX)\\n\",\n \" print(f\\\"\\\\n[{db_name}] Generating...\\\")\\n\",\n \" \\n\",\n \" result = regenerate_single_database(db_name, hint)\\n\",\n \" regenerated_results[db_name] = result\\n\",\n \" \\n\",\n \" if result[\\\"status\\\"] == \\\"success\\\":\\n\",\n \" print(f\\\" ✓ {result['roles_count']} roles ({result['valid_roles']} valid)\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" ✗ {result.get('error', result['status'])}\\\")\\n\",\n \" all_success = False\\n\",\n \"\\n\",\n \" # Summary table\\n\",\n \" print(\\\"\\\\n\\\" + \\\"=\\\"*60)\\n\",\n \" result_df = pd.DataFrame([{\\n\",\n \" \\\"Database\\\": r[\\\"database\\\"],\\n\",\n \" \\\"Status\\\": r[\\\"status\\\"],\\n\",\n \" \\\"Roles\\\": r.get(\\\"roles_count\\\", \\\"N/A\\\"),\\n\",\n \" \\\"Valid\\\": r.get(\\\"valid_roles\\\", \\\"N/A\\\"),\\n\",\n \" \\\"Tokens\\\": r.get(\\\"usage\\\", {}).get(\\\"total_tokens\\\", \\\"N/A\\\")\\n\",\n \" } for r in regenerated_results.values()])\\n\",\n \" display(result_df)\\n\",\n \"\\n\",\n \" if all_success:\\n\",\n \" print(\\\"\\\\n[OK] All databases regenerated. Run Cell 6.4 to preview.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"\\\\n[WARN] Some databases failed. Check errors above.\\\")\\n\",\n \"else:\\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"[INFO] No databases selected. Run Cell 6.1 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 69,\n \"id\": \"a2b0ba98\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"Database: formula_1\\n\",\n \"======================================================================\\n\",\n \"\\n\",\n \"=== NEW Roles (5) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Cols</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintenance and data management</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>94 (*)</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap times, pit stops, and qualifying results</td>\\n\",\n \" <td>8</td>\\n\",\n \" <td>61</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; lapTimes: [raceId, driverId, lap, position, time, milliseconds]; pitStops: [raceId, driverId, stop, lap, time, duration, milliseconds]; qualifying: [qualifyId, raceId, driverId, constructorId, number, position, q1, q2, q3]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>StandingsManager</td>\\n\",\n \" <td>Manages and monitors championship standings for drivers and constructors</td>\\n\",\n \" <td>7</td>\\n\",\n \" <td>42</td>\\n\",\n \" <td>constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, name, date]; constructorResults: [constructorResultsId, raceId, constructorId, points, status]; constructorStandings: [constructorStandingsId, raceId, constructorId, points, position, positionText, wins]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, points, position, positionText, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>PublicRelations</td>\\n\",\n \" <td>Accesses public information about circuits, drivers, constructors, and race schedules</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>31</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [year, url]; races: [raceId, year, round, circuitId, name, date, url]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal details including date of birth</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>32</td>\\n\",\n \" <td>drivers: [driverId, driverRef, number, code, forename, surname, dob, nationality]; races: [raceId, year, round, name, date]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionText, positionOrder, points, laps, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Role \\\\\\n\",\n \"0 SystemManager \\n\",\n \"1 RaceAnalyst \\n\",\n \"2 StandingsManager \\n\",\n \"3 PublicRelations \\n\",\n \"4 DriverManager \\n\",\n \"\\n\",\n \" Description \\\\\\n\",\n \"0 Full administrative access for system maintenance and data management \\n\",\n \"1 Analyzes race performance data including lap times, pit stops, and qualifying results \\n\",\n \"2 Manages and monitors championship standings for drivers and constructors \\n\",\n \"3 Accesses public information about circuits, drivers, constructors, and race schedules \\n\",\n \"4 Manages driver information and personal details including date of birth \\n\",\n \"\\n\",\n \" Tables Cols \\\\\\n\",\n \"0 13 94 (*) \\n\",\n \"1 8 61 \\n\",\n \"2 7 42 \\n\",\n \"3 6 31 \\n\",\n \"4 4 32 \\n\",\n \"\\n\",\n \" Policy \\n\",\n \"0 circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*] \\n\",\n \"1 circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; lapTimes: [raceId, driverId, lap, position, time, milliseconds]; pitStops: [raceId, driverId, stop, lap, time, duration, milliseconds]; qualifying: [qualifyId, raceId, driverId, constructorId, number, position, q1, q2, q3]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId] \\n\",\n \"2 constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, name, date]; constructorResults: [constructorResultsId, raceId, constructorId, points, status]; constructorStandings: [constructorStandingsId, raceId, constructorId, points, position, positionText, wins]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, points, position, positionText, statusId] \\n\",\n \"3 circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [year, url]; races: [raceId, year, round, circuitId, name, date, url]; status: [statusId, status] \\n\",\n \"4 drivers: [driverId, driverRef, number, code, forename, surname, dob, nationality]; races: [raceId, year, round, name, date]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionText, positionOrder, points, laps, statusId] \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Quality Comparison ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Metric</th>\\n\",\n \" <th>OLD</th>\\n\",\n \" <th>NEW</th>\\n\",\n \" <th>Threshold</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>Roles</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>NaN</td>\\n\",\n \" <td>NaN</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>Avg Coverage</td>\\n\",\n \" <td>65.5%</td>\\n\",\n \" <td>55.3%</td>\\n\",\n \" <td><85%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>Coverage Std</td>\\n\",\n \" <td>0.183</td>\\n\",\n \" <td>0.251</td>\\n\",\n \" <td>>0.065</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>Max Overlap</td>\\n\",\n \" <td>87.3%</td>\\n\",\n \" <td>57.4%</td>\\n\",\n \" <td><80%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Metric OLD NEW Threshold Status\\n\",\n \"0 Roles 5 5 NaN NaN\\n\",\n \"1 Avg Coverage 65.5% 55.3% <85% OK\\n\",\n \"2 Coverage Std 0.183 0.251 >0.065 OK\\n\",\n \"3 Max Overlap 87.3% 57.4% <80% OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[OK] formula_1: All quality checks passed\\n\",\n \"\\n\",\n \"======================================================================\\n\",\n \"[DECISION] Satisfied? Run Cell 6.5 to apply. Otherwise edit Cell 6.2 and re-run 6.3.\\n\",\n \"[TIP] Use Cell 6.4.1 to select specific roles from OLD/NEW\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.4 Preview Regenerated Roles (Compare OLD vs NEW + Quality Check)\\n\",\n \"# Supports multiple databases\\n\",\n \"import numpy as np\\n\",\n \"from src.role_evaluator.quality_metrics import RoleQualityEvaluator\\n\",\n \"\\n\",\n \"pd.set_option('display.max_colwidth', None)\\n\",\n \"pd.set_option('display.max_rows', None)\\n\",\n \"pd.set_option('display.width', None)\\n\",\n \"\\n\",\n \"if 'regenerated_results' in dir() and regenerated_results:\\n\",\n \" for db_name, regen_result in regenerated_results.items():\\n\",\n \" if regen_result[\\\"status\\\"] != \\\"success\\\":\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"[SKIP] {db_name}: {regen_result.get('error', 'failed')}\\\")\\n\",\n \" continue\\n\",\n \" \\n\",\n \" regenerated_assignments = regen_result[\\\"assignments\\\"]\\n\",\n \" target_schema = target_schemas.get(db_name)\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"Database: {db_name}\\\")\\n\",\n \" print(f\\\"{'='*70}\\\")\\n\",\n \" \\n\",\n \" # --- NEW Role Summary ---\\n\",\n \" new_summary = []\\n\",\n \" for r in regenerated_assignments:\\n\",\n \" policy = r.get('policy', {})\\n\",\n \" total_cols = count_policy_columns(policy, target_schema)\\n\",\n \" has_wc = has_wildcard_policy(policy)\\n\",\n \" cols_display = f\\\"{total_cols}\\\" + (\\\" (*)\\\" if has_wc else \\\"\\\")\\n\",\n \" policy_str = \\\"; \\\".join([\\n\",\n \" f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" \\n\",\n \" for t, c in policy.items()\\n\",\n \" ])\\n\",\n \" new_summary.append({\\n\",\n \" \\\"Role\\\": r['role'],\\n\",\n \" \\\"Description\\\": r.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Cols\\\": cols_display,\\n\",\n \" \\\"Policy\\\": policy_str\\n\",\n \" })\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n=== NEW Roles ({len(regenerated_assignments)}) ===\\\")\\n\",\n \" display(pd.DataFrame(new_summary))\\n\",\n \" \\n\",\n \" # --- Quality Metrics ---\\n\",\n \" if target_schema:\\n\",\n \" total_schema_cols = sum(len(t['columns']) for t in target_schema.values())\\n\",\n \" else:\\n\",\n \" total_schema_cols = 10\\n\",\n \" \\n\",\n \" # NEW metrics\\n\",\n \" new_coverages = [count_policy_columns(r.get('policy', {}), target_schema) / total_schema_cols \\n\",\n \" for r in regenerated_assignments]\\n\",\n \" new_avg_cov = np.mean(new_coverages)\\n\",\n \" new_cov_std = np.std(new_coverages)\\n\",\n \" \\n\",\n \" new_max_overlap = 0.0\\n\",\n \" non_admin_new = [r for r in regenerated_assignments if 'admin' not in r['role'].lower()]\\n\",\n \" for i, r1 in enumerate(non_admin_new):\\n\",\n \" set1 = RoleQualityEvaluator.policy_to_column_set(r1.get('policy', {}))\\n\",\n \" for r2 in non_admin_new[i+1:]:\\n\",\n \" set2 = RoleQualityEvaluator.policy_to_column_set(r2.get('policy', {}))\\n\",\n \" new_max_overlap = max(new_max_overlap, RoleQualityEvaluator.jaccard_similarity(set1, set2))\\n\",\n \" \\n\",\n \" # OLD metrics\\n\",\n \" old_roles = all_assignments.get('assignments', {}).get(db_name, [])\\n\",\n \" if old_roles:\\n\",\n \" old_coverages = [count_policy_columns(r.get('policy', {}), target_schema) / total_schema_cols \\n\",\n \" for r in old_roles]\\n\",\n \" old_avg_cov = np.mean(old_coverages)\\n\",\n \" old_cov_std = np.std(old_coverages)\\n\",\n \" \\n\",\n \" old_max_overlap = 0.0\\n\",\n \" non_admin_old = [r for r in old_roles if 'admin' not in r['role'].lower()]\\n\",\n \" for i, r1 in enumerate(non_admin_old):\\n\",\n \" set1 = RoleQualityEvaluator.policy_to_column_set(r1.get('policy', {}))\\n\",\n \" for r2 in non_admin_old[i+1:]:\\n\",\n \" set2 = RoleQualityEvaluator.policy_to_column_set(r2.get('policy', {}))\\n\",\n \" old_max_overlap = max(old_max_overlap, RoleQualityEvaluator.jaccard_similarity(set1, set2))\\n\",\n \" else:\\n\",\n \" old_avg_cov, old_cov_std, old_max_overlap = 0, 0, 0\\n\",\n \" \\n\",\n \" # Comparison table (using QUALITY_THRESHOLDS from Cell 5.1)\\n\",\n \" def status(metric, val):\\n\",\n \" if metric == \\\"Coverage\\\": \\n\",\n \" return \\\"WARN\\\" if val > QUALITY_THRESHOLDS.get(\\\"coverage_max\\\", 0.90) else \\\"OK\\\"\\n\",\n \" if metric == \\\"CovStd\\\": \\n\",\n \" return \\\"WARN\\\" if val < QUALITY_THRESHOLDS.get(\\\"coverage_std_min\\\", 0.08) else \\\"OK\\\"\\n\",\n \" if metric == \\\"MaxOverlap\\\": \\n\",\n \" return \\\"WARN\\\" if val > QUALITY_THRESHOLDS.get(\\\"max_overlap\\\", 0.80) else \\\"OK\\\"\\n\",\n \" return \\\"OK\\\"\\n\",\n \" \\n\",\n \" quality_df = pd.DataFrame([\\n\",\n \" {\\\"Metric\\\": \\\"Roles\\\", \\\"OLD\\\": len(old_roles), \\\"NEW\\\": len(regenerated_assignments)},\\n\",\n \" {\\\"Metric\\\": \\\"Avg Coverage\\\", \\\"OLD\\\": f\\\"{old_avg_cov:.1%}\\\", \\\"NEW\\\": f\\\"{new_avg_cov:.1%}\\\", \\n\",\n \" \\\"Threshold\\\": f\\\"<{QUALITY_THRESHOLDS.get('coverage_max', 0.90):.0%}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"Coverage\\\", new_avg_cov)},\\n\",\n \" {\\\"Metric\\\": \\\"Coverage Std\\\", \\\"OLD\\\": f\\\"{old_cov_std:.3f}\\\", \\\"NEW\\\": f\\\"{new_cov_std:.3f}\\\",\\n\",\n \" \\\"Threshold\\\": f\\\">{QUALITY_THRESHOLDS.get('coverage_std_min', 0.08)}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"CovStd\\\", new_cov_std)},\\n\",\n \" {\\\"Metric\\\": \\\"Max Overlap\\\", \\\"OLD\\\": f\\\"{old_max_overlap:.1%}\\\", \\\"NEW\\\": f\\\"{new_max_overlap:.1%}\\\",\\n\",\n \" \\\"Threshold\\\": f\\\"<{QUALITY_THRESHOLDS.get('max_overlap', 0.80):.0%}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"MaxOverlap\\\", new_max_overlap)},\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Quality Comparison ===\\\")\\n\",\n \" display(quality_df)\\n\",\n \" \\n\",\n \" warn_count = quality_df['Status'].eq('WARN').sum() if 'Status' in quality_df.columns else 0\\n\",\n \" if warn_count == 0:\\n\",\n \" print(f\\\"[OK] {db_name}: All quality checks passed\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\"[WARN] {db_name}: {warn_count} issue(s)\\\")\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(\\\"[DECISION] Satisfied? Run Cell 6.5 to apply. Otherwise edit Cell 6.2 and re-run 6.3.\\\")\\n\",\n \" print(\\\"[TIP] Use Cell 6.4.1 to select specific roles from OLD/NEW\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[WARN] No regenerated results. Run Cell 6.3 first.\\\")\\n\",\n \"\\n\",\n \"pd.reset_option('display.max_colwidth')\\n\",\n \"pd.reset_option('display.max_rows')\\n\",\n \"pd.reset_option('display.width')\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 70,\n \"id\": \"f003a014\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Role Selection Summary ===\\n\",\n \"\\n\",\n \"============================================================\\n\",\n \"Database: formula_1\\n\",\n \" From OLD (0): (none)\\n\",\n \" From NEW (4): ['SystemManager', 'RaceAnalyst', 'PublicRelations', 'DriverManager']\\n\",\n \" Combined: 4 roles\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Source</th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintena...</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap t...</td>\\n\",\n \" <td>8</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country,...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>PublicRelations</td>\\n\",\n \" <td>Accesses public information about circuits, dr...</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country,...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal detail...</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>drivers: [driverId, driverRef, number, code, f...</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Source Role Description \\\\\\n\",\n \"0 NEW SystemManager Full administrative access for system maintena... \\n\",\n \"1 NEW RaceAnalyst Analyzes race performance data including lap t... \\n\",\n \"2 NEW PublicRelations Accesses public information about circuits, dr... \\n\",\n \"3 NEW DriverManager Manages driver information and personal detail... \\n\",\n \"\\n\",\n \" Tables Policy \\n\",\n \"0 13 circuits: [*]; constructors: [*]; drivers: [*]... \\n\",\n \"1 8 circuits: [circuitId, name, location, country,... \\n\",\n \"2 6 circuits: [circuitId, name, location, country,... \\n\",\n \"3 4 drivers: [driverId, driverRef, number, code, f... \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"============================================================\\n\",\n \"[NEXT] Run Cell 6.5 to apply combined role sets\\n\",\n \"[TIP] Edit ROLE_SELECTION above to customize, then re-run this cell\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.4.1 Select & Combine Roles from OLD and NEW\\n\",\n \"# Human expert picks best roles from both versions to create final role set\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EDIT HERE: Specify which roles to keep from each version\\n\",\n \"# Format: {\\\"db_name\\\": {\\\"old\\\": [role_names], \\\"new\\\": [role_names]}}\\n\",\n \"# Leave empty [] to skip a version, or use [\\\"*\\\"] to keep all from that version\\n\",\n \"# ============================================================\\n\",\n \"ROLE_SELECTION = {\\n\",\n \" \\\"formula_1\\\": {\\n\",\n \" \\\"old\\\": [], # e.g., [\\\"DataAnalyst\\\", \\\"Researcher\\\"]\\n\",\n \" \\\"new\\\": [\\\"SystemManager\\\", \\\"RaceAnalyst\\\", \\\"PublicRelations\\\", \\\"DriverManager\\\"], # keep all new roles\\n\",\n \" },\\n\",\n \" # \\\"financial\\\": {\\n\",\n \" # \\\"old\\\": [],\\n\",\n \" # \\\"new\\\": [\\\"*\\\"],\\n\",\n \" # },\\n\",\n \"}\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# Process selections and create combined role sets\\n\",\n \"# ============================================================\\n\",\n \"combined_results = {}\\n\",\n \"\\n\",\n \"if 'regenerated_results' in dir() and regenerated_results:\\n\",\n \" print(\\\"=== Role Selection Summary ===\\\\n\\\")\\n\",\n \" \\n\",\n \" for db_name in valid_target_dbs:\\n\",\n \" selection = ROLE_SELECTION.get(db_name, {\\\"old\\\": [], \\\"new\\\": [\\\"*\\\"]})\\n\",\n \" old_keep = selection.get(\\\"old\\\", [])\\n\",\n \" new_keep = selection.get(\\\"new\\\", [\\\"*\\\"])\\n\",\n \" \\n\",\n \" # Get OLD roles\\n\",\n \" old_roles = all_assignments.get('assignments', {}).get(db_name, [])\\n\",\n \" old_role_dict = {r['role']: r for r in old_roles}\\n\",\n \" \\n\",\n \" # Get NEW roles\\n\",\n \" regen_result = regenerated_results.get(db_name, {})\\n\",\n \" if regen_result.get(\\\"status\\\") != \\\"success\\\":\\n\",\n \" print(f\\\"[SKIP] {db_name}: regeneration failed\\\")\\n\",\n \" continue\\n\",\n \" new_roles = regen_result[\\\"assignments\\\"]\\n\",\n \" new_role_dict = {r['role']: r for r in new_roles}\\n\",\n \" \\n\",\n \" # Build combined role set\\n\",\n \" combined_roles = []\\n\",\n \" selected_from_old = []\\n\",\n \" selected_from_new = []\\n\",\n \" \\n\",\n \" # Select from OLD\\n\",\n \" if old_keep == [\\\"*\\\"]:\\n\",\n \" combined_roles.extend(old_roles)\\n\",\n \" selected_from_old = [r['role'] for r in old_roles]\\n\",\n \" else:\\n\",\n \" for role_name in old_keep:\\n\",\n \" if role_name in old_role_dict:\\n\",\n \" combined_roles.append(old_role_dict[role_name])\\n\",\n \" selected_from_old.append(role_name)\\n\",\n \" else:\\n\",\n \" print(f\\\" [WARN] {db_name}: OLD role '{role_name}' not found\\\")\\n\",\n \" \\n\",\n \" # Select from NEW\\n\",\n \" if new_keep == [\\\"*\\\"]:\\n\",\n \" combined_roles.extend(new_roles)\\n\",\n \" selected_from_new = [r['role'] for r in new_roles]\\n\",\n \" else:\\n\",\n \" for role_name in new_keep:\\n\",\n \" if role_name in new_role_dict:\\n\",\n \" combined_roles.append(new_role_dict[role_name])\\n\",\n \" selected_from_new.append(role_name)\\n\",\n \" else:\\n\",\n \" print(f\\\" [WARN] {db_name}: NEW role '{role_name}' not found\\\")\\n\",\n \" \\n\",\n \" combined_results[db_name] = combined_roles\\n\",\n \" \\n\",\n \" # Display summary\\n\",\n \" print(f\\\"{'='*60}\\\")\\n\",\n \" print(f\\\"Database: {db_name}\\\")\\n\",\n \" print(f\\\" From OLD ({len(selected_from_old)}): {selected_from_old if selected_from_old else '(none)'}\\\")\\n\",\n \" print(f\\\" From NEW ({len(selected_from_new)}): {selected_from_new if selected_from_new else '(none)'}\\\")\\n\",\n \" print(f\\\" Combined: {len(combined_roles)} roles\\\")\\n\",\n \" \\n\",\n \" # Show combined roles table\\n\",\n \" target_schema = target_schemas.get(db_name)\\n\",\n \" combined_summary = []\\n\",\n \" for r in combined_roles:\\n\",\n \" policy = r.get('policy', {})\\n\",\n \" source = \\\"OLD\\\" if r['role'] in old_role_dict and r['role'] not in [nr['role'] for nr in new_roles] else \\\"NEW\\\"\\n\",\n \" if r['role'] in old_role_dict and r['role'] in [nr['role'] for nr in new_roles]:\\n\",\n \" source = \\\"NEW\\\" # Prefer NEW if same name exists in both\\n\",\n \" combined_summary.append({\\n\",\n \" \\\"Source\\\": source,\\n\",\n \" \\\"Role\\\": r['role'],\\n\",\n \" \\\"Description\\\": r.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Policy\\\": \\\"; \\\".join([f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" for t, c in policy.items()])\\n\",\n \" })\\n\",\n \" \\n\",\n \" display(pd.DataFrame(combined_summary))\\n\",\n \" print()\\n\",\n \" \\n\",\n \" print(\\\"=\\\"*60)\\n\",\n \" print(f\\\"[NEXT] Run Cell 6.5 to apply combined role sets\\\")\\n\",\n \" print(\\\"[TIP] Edit ROLE_SELECTION above to customize, then re-run this cell\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[WARN] No regenerated results. Run Cell 6.3 first.\\\")\\n\",\n \" print(\\\"\\\\n[HINT] Available roles to select from:\\\")\\n\",\n \" print(\\\" OLD: roles from current assignments\\\")\\n\",\n \" print(\\\" NEW: roles from regeneration\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 71,\n \"id\": \"6cb24c7d\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[MODE] Applying COMBINED role sets (from Cell 6.4.1 selection)\\n\",\n \"\\n\",\n \"=== Changes Applied ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Old Roles</th>\\n\",\n \" <th>New Roles</th>\\n\",\n \" <th>Source</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>Combined (OLD+NEW)</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Old Roles New Roles Source\\n\",\n \"0 formula_1 5 4 Combined (OLD+NEW)\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"Saved to: bird_column_level_rbac_assignments_v2_20251230.json\\n\",\n \"\\n\",\n \"[OK] State cleared. Select new database(s) to continue.\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.5 Apply Changes (Save Combined Role Sets)\\n\",\n \"# Uses combined_results from Cell 6.4.1 (selected from OLD + NEW)\\n\",\n \"from datetime import datetime\\n\",\n \"\\n\",\n \"APPLY_CHANGES = True # Set to True to confirm\\n\",\n \"\\n\",\n \"if APPLY_CHANGES:\\n\",\n \" # Prefer combined_results (from selection), fallback to regenerated_results\\n\",\n \" use_combined = 'combined_results' in dir() and combined_results\\n\",\n \" use_regenerated = 'regenerated_results' in dir() and regenerated_results\\n\",\n \" \\n\",\n \" if use_combined:\\n\",\n \" print(\\\"[MODE] Applying COMBINED role sets (from Cell 6.4.1 selection)\\\\n\\\")\\n\",\n \" applied = []\\n\",\n \" \\n\",\n \" for db_name, combined_roles in combined_results.items():\\n\",\n \" if not combined_roles:\\n\",\n \" continue\\n\",\n \" \\n\",\n \" old_count = len(all_assignments.get('assignments', {}).get(db_name, []))\\n\",\n \" all_assignments['assignments'][db_name] = combined_roles\\n\",\n \" applied.append({\\n\",\n \" \\\"Database\\\": db_name,\\n\",\n \" \\\"Old Roles\\\": old_count,\\n\",\n \" \\\"New Roles\\\": len(combined_roles),\\n\",\n \" \\\"Source\\\": \\\"Combined (OLD+NEW)\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" if applied:\\n\",\n \" OUTPUT_DIR = project_root / 'outputs'\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = OUTPUT_DIR / f'bird_column_level_rbac_assignments_v2_{today}.json'\\n\",\n \" with open(output_path, 'w') as f:\\n\",\n \" json.dump(all_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \" \\n\",\n \" print(\\\"=== Changes Applied ===\\\")\\n\",\n \" display(pd.DataFrame(applied))\\n\",\n \" print(f\\\"\\\\nSaved to: {output_path.name}\\\")\\n\",\n \" \\n\",\n \" # Clear state\\n\",\n \" combined_results = {}\\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"\\\\n[OK] State cleared. Select new database(s) to continue.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No combined role sets to apply. Run Cell 6.4.1 first.\\\")\\n\",\n \" \\n\",\n \" elif use_regenerated:\\n\",\n \" print(\\\"[MODE] Applying ALL NEW roles (no selection made)\\\\n\\\")\\n\",\n \" applied = []\\n\",\n \" \\n\",\n \" for db_name, regen_result in regenerated_results.items():\\n\",\n \" if regen_result[\\\"status\\\"] != \\\"success\\\":\\n\",\n \" continue\\n\",\n \" \\n\",\n \" old_count = len(all_assignments.get('assignments', {}).get(db_name, []))\\n\",\n \" all_assignments['assignments'][db_name] = regen_result[\\\"assignments\\\"]\\n\",\n \" applied.append({\\n\",\n \" \\\"Database\\\": db_name,\\n\",\n \" \\\"Old Roles\\\": old_count,\\n\",\n \" \\\"New Roles\\\": len(regen_result[\\\"assignments\\\"]),\\n\",\n \" \\\"Source\\\": \\\"All NEW\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" if applied:\\n\",\n \" OUTPUT_DIR = project_root / 'outputs'\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = OUTPUT_DIR / f'bird_column_level_rbac_assignments_v2_{today}.json'\\n\",\n \" with open(output_path, 'w') as f:\\n\",\n \" json.dump(all_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \" \\n\",\n \" print(\\\"=== Changes Applied ===\\\")\\n\",\n \" display(pd.DataFrame(applied))\\n\",\n \" print(f\\\"\\\\nSaved to: {output_path.name}\\\")\\n\",\n \" \\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"\\\\n[OK] State cleared. Select new database(s) to continue.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No successful regenerations to apply.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No results to apply. Run Cell 6.3 and optionally Cell 6.4.1 first.\\\")\\n\",\n \"else:\\n\",\n \" has_combined = 'combined_results' in dir() and combined_results\\n\",\n \" has_regenerated = 'regenerated_results' in dir() and regenerated_results\\n\",\n \" \\n\",\n \" if has_combined:\\n\",\n \" print(f\\\"[INFO] {len(combined_results)} database(s) with COMBINED role sets ready\\\")\\n\",\n \" print(\\\"[TIP] These are your custom selections from OLD + NEW\\\")\\n\",\n \" elif has_regenerated:\\n\",\n \" success_count = sum(1 for r in regenerated_results.values() if r[\\\"status\\\"] == \\\"success\\\")\\n\",\n \" print(f\\\"[INFO] {success_count} database(s) with ALL NEW roles ready\\\")\\n\",\n \" print(\\\"[TIP] Run Cell 6.4.1 to select specific roles from OLD/NEW\\\")\\n\",\n \" \\n\",\n \" print(\\\"\\\\n[INFO] APPLY_CHANGES is False. Set to True and re-run to apply.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"f1bd0bc2\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# 6.6 Regenerate RBAC Dataset with Updated Role Assignments\\n\",\n \"# Run this after Cell 6.5 to create the final dataset\\n\",\n \"\\n\",\n \"from datetime import datetime\\n\",\n \"import importlib\\n\",\n \"\\n\",\n \"# IMPORTANT: Reload ALL dependent modules to pick up CTE parsing fix\\n\",\n \"import src.processors.column_extractor\\n\",\n \"import src.processors.column_permission_checker\\n\",\n \"import src.processors.column_level_rbac_generator\\n\",\n \"\\n\",\n \"importlib.reload(src.processors.column_extractor)\\n\",\n \"importlib.reload(src.processors.column_permission_checker)\\n\",\n \"importlib.reload(src.processors.column_level_rbac_generator)\\n\",\n \"\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator\\n\",\n \"\\n\",\n \"# Find the updated role assignments file (v2 if exists, otherwise latest)\\n\",\n \"v2_files = sorted(glob.glob(str(project_root / 'outputs' / 'bird_column_level_rbac_assignments_v2_*.json')))\\n\",\n \"if v2_files:\\n\",\n \" UPDATED_ROLE_PATH = v2_files[-1]\\n\",\n \"else:\\n\",\n \" role_files = sorted(glob.glob(str(project_root / 'outputs' / 'column_level_role_assignments_bird_*.json')))\\n\",\n \" UPDATED_ROLE_PATH = role_files[-1] if role_files else None\\n\",\n \"\\n\",\n \"if UPDATED_ROLE_PATH:\\n\",\n \" print(f\\\"=== Regenerating RBAC Dataset ===\\\")\\n\",\n \" print(f\\\"Role assignments: {Path(UPDATED_ROLE_PATH).name}\\\")\\n\",\n \" print(f\\\"BIRD data: {BIRD_DATA_PATH}\\\")\\n\",\n \" \\n\",\n \" # Initialize generator with updated roles\\n\",\n \" generator = ColumnLevelRBACGenerator(\\n\",\n \" role_assignments_path=UPDATED_ROLE_PATH,\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH),\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" project_root=str(project_root),\\n\",\n \" seed=42\\n\",\n \" )\\n\",\n \" \\n\",\n \" # Generate dataset\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = project_root / 'outputs' / f'column_level_rbac_dataset_bird_{today}.json'\\n\",\n \" \\n\",\n \" full_stats = generator.generate_and_save(\\n\",\n \" output_path=str(output_path),\\n\",\n \" roles_per_db=None,\\n\",\n \" queries_per_role=None\\n\",\n \" )\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n✓ Dataset saved to: {output_path.name}\\\")\\n\",\n \" print(f\\\"\\\\n=== Dataset Statistics ===\\\")\\n\",\n \" for k, v in full_stats.items():\\n\",\n \" if k == 'by_database':\\n\",\n \" print(f\\\"\\\\nPer-database distribution:\\\")\\n\",\n \" for db_id, counts in sorted(v.items()):\\n\",\n \" print(f\\\" {db_id}: allowed={counts['allowed']}, denied={counts['denied']}\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[ERROR] No role assignments file found. Run Cell 6.5 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"a9345c97\",\n \"metadata\": {},\n \"source\": [\n \"## 7. Summary\\n\",\n \"\\n\",\n \"### Generated Files\\n\",\n \"- `outputs/column_level_role_assignments_bird_YYYYMMDD.json` - Role assignments with column-level policies\\n\",\n \"- `outputs/bird_column_level_rbac_assignments_v2_YYYYMMDD.json` - Updated role assignments after regeneration\\n\",\n \"- `outputs/column_level_rbac_dataset_bird_YYYYMMDD.json` - Column-Level RBAC dataset\\n\",\n \"\\n\",\n \"### Workflow\\n\",\n \"\\n\",\n \"| Section | Description |\\n\",\n \"|---------|-------------|\\n\",\n \"| **3** | Batch process all BIRD databases to generate initial roles |\\n\",\n \"| **4** | Generate RBAC dataset from role assignments |\\n\",\n \"| **5** | Quality assessment with configurable thresholds |\\n\",\n \"| **6.1** | Select problematic database(s) and view current state |\\n\",\n \"| **6.2** | Write regeneration hints with problem description |\\n\",\n \"| **6.3** | Execute LLM regeneration with hints |\\n\",\n \"| **6.4** | Preview OLD vs NEW roles with quality comparison |\\n\",\n \"| **6.4.1** | Select & combine best roles from OLD and NEW |\\n\",\n \"| **6.5** | Apply changes (save updated role assignments) |\\n\",\n \"| **6.6** | Regenerate RBAC dataset with updated roles |\\n\",\n \"\\n\",\n \"### Quality Assessment Module (Section 5)\\n\",\n \"\\n\",\n \"```python\\n\",\n \"from src.role_evaluator import RoleQualityEvaluator, RoleQualityVisualizer\\n\",\n \"\\n\",\n \"evaluator = RoleQualityEvaluator(\\n\",\n \" role_assignments_path=\\\"outputs/column_level_role_assignments_bird.json\\\",\\n\",\n \" dataset_path=\\\"outputs/column_level_rbac_dataset_bird.json\\\",\\n\",\n \" schema_dir=\\\"data/Bird/dev_20251106/dev_databases\\\",\\n\",\n \" embed_model=\\\"BAAI/bge-small-en-v1.5\\\" # or \\\"text-embedding-3-small\\\"\\n\",\n \")\\n\",\n \"\\n\",\n \"report = evaluator.evaluate_all()\\n\",\n \"visualizer = RoleQualityVisualizer(evaluator=evaluator, report=report)\\n\",\n \"visualizer.plot_quality_dashboard()\\n\",\n \"```\\n\",\n \"\\n\",\n \"### Quality Thresholds (Configurable)\\n\",\n \"\\n\",\n \"| Metric | Default | Description |\\n\",\n \"|--------|---------|-------------|\\n\",\n \"| `deny_rate_min` | 5% | Warn if deny rate below (roles too permissive) |\\n\",\n \"| `coverage_max` | 90% | Warn if avg coverage above (roles too broad) |\\n\",\n \"| `coverage_std_min` | 0.08 | Warn if coverage std below (low diversity) |\\n\",\n \"| `max_overlap` | 80% | Warn if max overlap above (redundant roles) |\\n\",\n \"\\n\",\n \"### External Modules\\n\",\n \"\\n\",\n \"1. **`src/role_evaluator/quality_metrics.py`** - Quality metrics computation\\n\",\n \" - `RoleQualityEvaluator`: Comprehensive evaluator\\n\",\n \" - `DenyRateMetrics`, `SemanticMetrics`, `OverlapMetrics`: Data classes\\n\",\n \" - `print_quality_report()`: Formatted report output\\n\",\n \"\\n\",\n \"2. **`src/role_evaluator/quality_visualizer.py`** - Visualization suite\\n\",\n \" - `plot_deny_rate_distribution()`: Permission distribution chart\\n\",\n \" - `plot_semantic_alignment()`: Semantic alignment scatter plot\\n\",\n \" - `plot_policy_overlap_bars()`: Policy overlap bar chart\\n\",\n \" - `plot_quality_dashboard()`: Comprehensive dashboard\\n\",\n \"\\n\",\n \"3. **`src/processors/column_level_rbac_generator.py`** - Dataset generation\\n\",\n \" - `ColumnLevelRBACGenerator`: Generate RBAC dataset from role assignments\"\n ]\n }\n ],\n \"metadata\": {\n \"kernelspec\": {\n \"display_name\": \"llm4db\",\n \"language\": \"python\",\n \"name\": \"python3\"\n },\n \"language_info\": {\n \"codemirror_mode\": {\n \"name\": \"ipython\",\n \"version\": 3\n },\n \"file_extension\": \".py\",\n \"mimetype\": \"text/x-python\",\n \"name\": \"python\",\n \"nbconvert_exporter\": \"python\",\n \"pygments_lexer\": \"ipython3\",\n \"version\": \"3.10.18\"\n }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 5\n}\n"}, "371a5f4857980ef1c5e9e5714af4d820697cca97": {"t": 1785753620.205377, "n": 427471, "h": "e6b8a7a78d24465c", "c": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"id\": \"c4bddd25\",\n \"metadata\": {},\n \"source\": [\n \"## 0. Import and Setup\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 1,\n \"id\": \"93292a4c\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Project root: /home/feiy/Role-SQL-benchmark\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Setup and imports\\n\",\n \"import sys\\n\",\n \"import json\\n\",\n \"import sqlite3\\n\",\n \"from pathlib import Path\\n\",\n \"from pprint import pprint\\n\",\n \"\\n\",\n \"# Add project root to path\\n\",\n \"project_root = Path.cwd().parent\\n\",\n \"sys.path.insert(0, str(project_root))\\n\",\n \"\\n\",\n \"print(f\\\"Project root: {project_root}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 2,\n \"id\": \"bce399e4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Available API keys:\\n\",\n \" OPENAI_API_KEY: ✓\\n\",\n \" DEEPSEEK_API_KEY: ✓\\n\",\n \" DEEPINFRA_API_KEY: ✓\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Load environment and initialize Oracle\\n\",\n \"from dotenv import load_dotenv\\n\",\n \"import os\\n\",\n \"\\n\",\n \"load_dotenv(project_root / '.env')\\n\",\n \"\\n\",\n \"# Check available API keys\\n\",\n \"api_keys = {\\n\",\n \" 'OPENAI_API_KEY': bool(os.getenv('OPENAI_API_KEY')),\\n\",\n \" 'DEEPSEEK_API_KEY': bool(os.getenv('DEEPSEEK_API_KEY')),\\n\",\n \" 'DEEPINFRA_API_KEY': bool(os.getenv('DEEPINFRA_API_KEY')),\\n\",\n \"}\\n\",\n \"print(\\\"Available API keys:\\\")\\n\",\n \"for k, v in api_keys.items():\\n\",\n \" print(f\\\" {k}: {'✓' if v else '✗'}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 3,\n \"id\": \"a4d69b7c\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Using model: deepseek-chat\\n\"\n ]\n },\n {\n \"name\": \"stderr\",\n \"output_type\": \"stream\",\n \"text\": [\n \"/home/feiy/anaconda3/envs/llm4db/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\\n\",\n \" from .autonotebook import tqdm as notebook_tqdm\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Initialize LLM Oracle\\n\",\n \"from src.llm_oracle import Oracle\\n\",\n \"\\n\",\n \"# Choose model based on available keys (prefer DeepSeek for cost efficiency)\\n\",\n \"if os.getenv('DEEPSEEK_API_KEY'):\\n\",\n \" MODEL = \\\"deepseek-chat\\\"\\n\",\n \" API_KEY = os.getenv('DEEPSEEK_API_KEY')\\n\",\n \"elif os.getenv('OPENAI_API_KEY'):\\n\",\n \" MODEL = \\\"gpt-4o-mini\\\"\\n\",\n \" API_KEY = os.getenv('OPENAI_API_KEY')\\n\",\n \"else:\\n\",\n \" raise ValueError(\\\"No API key available!\\\")\\n\",\n \"\\n\",\n \"oracle = Oracle(model=MODEL, apikey=API_KEY)\\n\",\n \"print(f\\\"Using model: {MODEL}\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8809cf55\",\n \"metadata\": {},\n \"source\": [\n \"## 1. Load Sample Database Schema\\n\",\n \"\\n\",\n \"We'll use a BIRD database with interesting column structure for testing.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 4,\n \"id\": \"e9dcc61c\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Import shared policy utility functions\\n\",\n \"from src.role_evaluator.quality_metrics import RoleQualityEvaluator\\n\",\n \"\\n\",\n \"# Convenience aliases for the static methods\\n\",\n \"count_policy_columns = RoleQualityEvaluator.count_policy_columns\\n\",\n \"has_wildcard_policy = RoleQualityEvaluator.has_wildcard_policy\\n\",\n \"\\n\",\n \"def get_schema_from_sqlite(db_path: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Extract detailed schema from SQLite database.\\\"\\\"\\\"\\n\",\n \" conn = sqlite3.connect(db_path)\\n\",\n \" cursor = conn.cursor()\\n\",\n \" \\n\",\n \" # Get all tables\\n\",\n \" cursor.execute(\\\"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\\\")\\n\",\n \" tables = [row[0] for row in cursor.fetchall()]\\n\",\n \" \\n\",\n \" schema = {}\\n\",\n \" for table in tables:\\n\",\n \" cursor.execute(f\\\"PRAGMA table_info(`{table}`)\\\")\\n\",\n \" columns = cursor.fetchall()\\n\",\n \" \\n\",\n \" cursor.execute(f\\\"PRAGMA foreign_key_list(`{table}`)\\\")\\n\",\n \" fks = {row[3]: f\\\"{row[2]}.{row[4]}\\\" for row in cursor.fetchall()}\\n\",\n \" \\n\",\n \" schema[table] = {\\n\",\n \" 'columns': [\\n\",\n \" {\\n\",\n \" 'name': col[1],\\n\",\n \" 'type': col[2] or 'TEXT',\\n\",\n \" 'pk': col[5] == 1,\\n\",\n \" 'not_null': col[3] == 1,\\n\",\n \" 'fk': fks.get(col[1])\\n\",\n \" }\\n\",\n \" for col in columns\\n\",\n \" ]\\n\",\n \" }\\n\",\n \" \\n\",\n \" conn.close()\\n\",\n \" return schema\\n\",\n \"\\n\",\n \"def format_schema_for_prompt(schema: dict, db_name: str) -> str:\\n\",\n \" \\\"\\\"\\\"Format schema as text for LLM prompt.\\\"\\\"\\\"\\n\",\n \" lines = [f\\\"Database: {db_name}\\\\n\\\"]\\n\",\n \" \\n\",\n \" for table, info in schema.items():\\n\",\n \" lines.append(f\\\"Table: {table}\\\")\\n\",\n \" lines.append(\\\"Columns:\\\")\\n\",\n \" for col in info['columns']:\\n\",\n \" constraints = []\\n\",\n \" if col['pk']:\\n\",\n \" constraints.append(\\\"PRIMARY KEY\\\")\\n\",\n \" if col['not_null'] and not col['pk']:\\n\",\n \" constraints.append(\\\"NOT NULL\\\")\\n\",\n \" if col['fk']:\\n\",\n \" constraints.append(f\\\"FK -> {col['fk']}\\\")\\n\",\n \" \\n\",\n \" constraint_str = f\\\" [{', '.join(constraints)}]\\\" if constraints else \\\"\\\"\\n\",\n \" lines.append(f\\\" - {col['name']} ({col['type']}){constraint_str}\\\")\\n\",\n \" lines.append(\\\"\\\")\\n\",\n \" \\n\",\n \" return \\\"\\\\n\\\".join(lines)\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 5,\n \"id\": \"705d64b1\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Available BIRD databases (11):\\n\",\n \" - california_schools\\n\",\n \" - card_games\\n\",\n \" - codebase_community\\n\",\n \" - debit_card_specializing\\n\",\n \" - european_football_2\\n\",\n \" - financial\\n\",\n \" - formula_1\\n\",\n \" - student_club\\n\",\n \" - superhero\\n\",\n \" - thrombosis_prediction\\n\",\n \" ...\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Load a sample BIRD database - use the latest 2025 version\\n\",\n \"bird_db_dir = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases'\\n\",\n \"\\n\",\n \"# List available databases\\n\",\n \"available_dbs = [d.name for d in bird_db_dir.iterdir() if d.is_dir()]\\n\",\n \"print(f\\\"Available BIRD databases ({len(available_dbs)}):\\\")\\n\",\n \"for db in sorted(available_dbs)[:10]:\\n\",\n \" print(f\\\" - {db}\\\")\\n\",\n \"print(\\\" ...\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 6,\n \"id\": \"f9e1a86a\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Loading schema from: /home/feiy/Role-SQL-benchmark/data/Bird/dev_20251106/dev_databases/california_schools/california_schools.sqlite\\n\",\n \"\\n\",\n \"Tables: ['frpm', 'satscores', 'schools']\\n\",\n \"\\n\",\n \"frpm: 29 columns\\n\",\n \" - CDSCode (TEXT)\\n\",\n \" - Academic Year (TEXT)\\n\",\n \" - County Code (TEXT)\\n\",\n \" - District Code (INTEGER)\\n\",\n \" - School Code (TEXT)\\n\",\n \" ... and 24 more\\n\",\n \"\\n\",\n \"satscores: 11 columns\\n\",\n \" - cds (TEXT)\\n\",\n \" - rtype (TEXT)\\n\",\n \" - sname (TEXT)\\n\",\n \" - dname (TEXT)\\n\",\n \" - cname (TEXT)\\n\",\n \" ... and 6 more\\n\",\n \"\\n\",\n \"schools: 49 columns\\n\",\n \" - CDSCode (TEXT)\\n\",\n \" - NCESDist (TEXT)\\n\",\n \" - NCESSchool (TEXT)\\n\",\n \" - StatusType (TEXT)\\n\",\n \" - County (TEXT)\\n\",\n \" ... and 44 more\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Select a database with moderate complexity\\n\",\n \"# 'california_schools' has tables with sensitive-looking columns\\n\",\n \"TEST_DB = \\\"california_schools\\\"\\n\",\n \"db_path = bird_db_dir / TEST_DB / f\\\"{TEST_DB}.sqlite\\\"\\n\",\n \"\\n\",\n \"print(f\\\"Loading schema from: {db_path}\\\")\\n\",\n \"schema = get_schema_from_sqlite(str(db_path))\\n\",\n \"\\n\",\n \"print(f\\\"\\\\nTables: {list(schema.keys())}\\\")\\n\",\n \"for table, info in schema.items():\\n\",\n \" print(f\\\"\\\\n{table}: {len(info['columns'])} columns\\\")\\n\",\n \" for col in info['columns'][:5]:\\n\",\n \" print(f\\\" - {col['name']} ({col['type']})\\\")\\n\",\n \" if len(info['columns']) > 5:\\n\",\n \" print(f\\\" ... and {len(info['columns']) - 5} more\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 7,\n \"id\": \"e5fb0114\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Database: california_schools\\n\",\n \"\\n\",\n \"Table: frpm\\n\",\n \"Columns:\\n\",\n \" - CDSCode (TEXT) [PRIMARY KEY, FK -> schools.CDSCode]\\n\",\n \" - Academic Year (TEXT)\\n\",\n \" - County Code (TEXT)\\n\",\n \" - District Code (INTEGER)\\n\",\n \" - School Code (TEXT)\\n\",\n \" - County Name (TEXT)\\n\",\n \" - District Name (TEXT)\\n\",\n \" - School Name (TEXT)\\n\",\n \" - District Type (TEXT)\\n\",\n \" - School Type (TEXT)\\n\",\n \" - Educational Option Type (TEXT)\\n\",\n \" - NSLP Provision Status (TEXT)\\n\",\n \" - Charter School (Y/N) (INTEGER)\\n\",\n \" - Charter School Number (TEXT)\\n\",\n \" - Charter Funding Type (TEXT)\\n\",\n \" - IRC (INTEGER)\\n\",\n \" - Low Grade (TEXT)\\n\",\n \" - High Grade (TEXT)\\n\",\n \" - Enrollment (K-12) (REAL)\\n\",\n \" - Free Meal Count (K-12) (REAL)\\n\",\n \" - Percent (%) Eligible Free (K-12) (REAL)\\n\",\n \" - FRPM Count (K-12) (REAL)\\n\",\n \" - Percent (%) Eligible FRPM (K-12) (REAL)\\n\",\n \" - Enrollment (Ages 5-17) (REAL)\\n\",\n \" - Free Meal Count (Ages 5-17) (REAL)\\n\",\n \" - Percent (%) Eligible Free (Ages 5-17) (REAL)\\n\",\n \" - FRPM Count (Ages 5-17) (REAL)\\n\",\n \" - Percent (%) Eligible FRPM (Ages 5-17) (REAL)\\n\",\n \" - 2013-14 CALPADS Fall 1 Certification Status (INTEGER)\\n\",\n \"\\n\",\n \"Table: satscores\\n\",\n \"Columns:\\n\",\n \" - cds (TEXT) [PRIMARY KEY, FK -> schools.CDSCode]\\n\",\n \" - rtype (TEXT) [NOT NULL]\\n\",\n \" - sname (TEXT)\\n\",\n \" - dname (TEXT)\\n\",\n \" - cname (TEXT)\\n\",\n \" - enroll12 (INTEGER) [NOT NULL]\\n\",\n \" - NumTstTakr (INTEGER) [NOT NULL]\\n\",\n \" - AvgScrRead (INTEGER)\\n\",\n \" - AvgScrMath (INTEGER)\\n\",\n \" - AvgScrWrite (INTEGER)\\n\",\n \" - NumGE1500 (INTEGER)\\n\",\n \"\\n\",\n \"Table: schools\\n\",\n \"Columns:\\n\",\n \" - CDSCode (TEXT) [PRIMARY KEY]\\n\",\n \" - NCESDist (TEXT)\\n\",\n \" - NCESSchool (TEXT)\\n\",\n \" - StatusType (TEXT) [NOT NULL]\\n\",\n \" - County (TEXT) [NOT NULL]\\n\",\n \" - District (TEXT) [NOT NULL]\\n\",\n \" - School (TEXT)\\n\",\n \" - Street (TEXT)\\n\",\n \" - StreetAbr (TEXT)\\n\",\n \" - City (TEXT)\\n\",\n \" - Zip (TEXT)\\n\",\n \" - State (TEXT)\\n\",\n \" - MailStreet (TEXT)\\n\",\n \" - MailStrAbr (TEXT)\\n\",\n \" - MailCity (TEXT)\\n\",\n \" - MailZip (TEXT)\\n\",\n \" - MailState (TEXT)\\n\",\n \" - Phone (TEXT)\\n\",\n \" - Ext (TEXT)\\n\",\n \" - Website (TEXT)\\n\",\n \" - OpenDate (DATE)\\n\",\n \" - ClosedDate (DATE)\\n\",\n \" - Charter (INTEGER)\\n\",\n \" - CharterNum (TEXT)\\n\",\n \" - FundingType (TEXT)\\n\",\n \" - DOC (TEXT) [NOT NULL]\\n\",\n \" - DOCType (TEXT) [NOT NULL]\\n\",\n \" - SOC (TEXT)\\n\",\n \" - SOCType (TEXT)\\n\",\n \" - EdOpsCode (TEXT)\\n\",\n \" \\n\",\n \"\\n\",\n \"... (total 2401 characters)\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Format schema for prompt\\n\",\n \"schema_text = format_schema_for_prompt(schema, TEST_DB)\\n\",\n \"print(schema_text[:2000])\\n\",\n \"print(f\\\"\\\\n... (total {len(schema_text)} characters)\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8e904509\",\n \"metadata\": {},\n \"source\": [\n \"## 2. Load Prompts from External Modules\\n\",\n \"\\n\",\n \"Prompts are now defined in `configs/column_level_prompts.py`.\\n\",\n \"- **JSON format** is used (more structured, requires max_tokens=4000)\\n\",\n \"- Parser auto-detects format and handles both JSON and Plain Text\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 8,\n \"id\": \"1a425645\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"✓ Loaded from external modules:\\n\",\n \" - COLUMN_LEVEL_SYSTEM_PROMPT (2734 chars)\\n\",\n \" - COLUMN_LEVEL_USER_PROMPT_TEMPLATE\\n\",\n \" - DEFAULT_MAX_TOKENS = 4000\\n\",\n \" - parse_column_level_roles (auto-detects JSON/Text)\\n\",\n \" - validate_roles_against_schema\\n\",\n \"\\n\",\n \"--- System Prompt Preview ---\\n\",\n \"You are a database security expert specializing in fine-grained Role-Based Access Control (RBAC).\\n\",\n \"Your job is to read a relational database schema and propose a COHERENT set of roles with COLUMN-LEVEL permissions.\\n\",\n \"\\n\",\n \"Key goals:\\n\",\n \"- Minimize role count while covering real-world workflows (decide number of roles according to real world scenario).\\n\",\n \"- Each role must be clearly related to others (same scenario, complementary duties).\\n\",\n \"- Enforce least-privilege at the COLUMN level (specify which columns eac\\n\",\n \"...\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Reload modules to get latest changes\\n\",\n \"import importlib\\n\",\n \"import configs.column_level_prompts\\n\",\n \"import src.role_parser.column_level_parser\\n\",\n \"importlib.reload(configs.column_level_prompts)\\n\",\n \"importlib.reload(src.role_parser.column_level_parser)\\n\",\n \"\\n\",\n \"# Import prompts and parser from external modules\\n\",\n \"from configs.column_level_prompts import (\\n\",\n \" COLUMN_LEVEL_SYSTEM_PROMPT, \\n\",\n \" COLUMN_LEVEL_USER_PROMPT_TEMPLATE,\\n\",\n \" DEFAULT_MAX_TOKENS\\n\",\n \")\\n\",\n \"from src.role_parser import parse_column_level_roles, validate_roles_against_schema\\n\",\n \"\\n\",\n \"print(\\\"✓ Loaded from external modules:\\\")\\n\",\n \"print(f\\\" - COLUMN_LEVEL_SYSTEM_PROMPT ({len(COLUMN_LEVEL_SYSTEM_PROMPT)} chars)\\\")\\n\",\n \"print(f\\\" - COLUMN_LEVEL_USER_PROMPT_TEMPLATE\\\")\\n\",\n \"print(f\\\" - DEFAULT_MAX_TOKENS = {DEFAULT_MAX_TOKENS}\\\")\\n\",\n \"print(f\\\" - parse_column_level_roles (auto-detects JSON/Text)\\\")\\n\",\n \"print(f\\\" - validate_roles_against_schema\\\")\\n\",\n \"\\n\",\n \"print(\\\"\\\\n--- System Prompt Preview ---\\\")\\n\",\n \"print(COLUMN_LEVEL_SYSTEM_PROMPT[:500])\\n\",\n \"print(\\\"...\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"0acacb6e\",\n \"metadata\": {},\n \"source\": [\n \"## [Archived] Single-Sample Experiments\\n\",\n \"\\n\",\n \"The following code block contains single sample test code for debugging prompt and parser.\\n\",\n \"**Commented out. For production, skip to Section 8 for batch processing.**\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 9,\n \"id\": \"e9700ae1\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"⏭️ This cell is commented out. Skip to Section 8 for batch processing.\\n\"\n ]\n }\n ],\n \"source\": [\n \"# ============================================================\\n\",\n \"# [ARCHIVED] Single sample experiment code - commented out, skip this cell\\n\",\n \"# ============================================================\\n\",\n \"# The following code is for testing and debugging a single database\\n\",\n \"# For production, skip to Section 8 for batch processing\\n\",\n \"\\n\",\n \"'''\\n\",\n \"# --- 3. Generate Column-Level Roles (Single DB) ---\\n\",\n \"print(\\\"Generating column-level roles...\\\")\\n\",\n \"user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=schema_text)\\n\",\n \"response = oracle.query(\\n\",\n \" prompt_sys=COLUMN_LEVEL_SYSTEM_PROMPT,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3, top_p=0.9,\\n\",\n \" max_completion_tokens=DEFAULT_MAX_TOKENS\\n\",\n \")\\n\",\n \"llm_response = response['answer']\\n\",\n \"print(f\\\"Response length: {len(llm_response)} chars\\\")\\n\",\n \"\\n\",\n \"# --- 4. Parse and Validate ---\\n\",\n \"from src.role_parser.column_level_parser import parse_column_level_roles\\n\",\n \"parsed_roles = parse_column_level_roles(llm_response)\\n\",\n \"print(f\\\"Parsed {len(parsed_roles)} roles\\\")\\n\",\n \"\\n\",\n \"validation = validate_roles_against_schema(parsed_roles, str(db_path))\\n\",\n \"print(f\\\"Valid: {validation['valid_roles']}/{validation['total_roles']}\\\")\\n\",\n \"\\n\",\n \"# --- 5. Show Results ---\\n\",\n \"for role in parsed_roles:\\n\",\n \" print(f\\\" - {role['role']}: {len(role.get('columns', {}))} tables\\\")\\n\",\n \"\\n\",\n \"# --- 6. Compare with Table-Level ---\\n\",\n \"from configs.prompts import SYSTEM_PROMPT as TABLE_LEVEL_PROMPT, USER_PROMPT_TEMPLATE\\n\",\n \"table_level_response = oracle.query(\\n\",\n \" prompt_sys=TABLE_LEVEL_PROMPT,\\n\",\n \" prompt_user=USER_PROMPT_TEMPLATE.format(schema_content=schema_text),\\n\",\n \" temp=0.3, top_p=0.9\\n\",\n \")\\n\",\n \"print(\\\"Table-Level Response:\\\", table_level_response['answer'][:500])\\n\",\n \"\\n\",\n \"# --- 7. Save Single DB Result ---\\n\",\n \"def convert_to_assignment_format(parsed_roles, db_name):\\n\",\n \" return {\\\"assignments\\\": {db_name: [\\n\",\n \" {\\\"role\\\": r[\\\"role\\\"], \\\"description\\\": r.get(\\\"description\\\", \\\"\\\"), \\\"policy\\\": r.get(\\\"columns\\\", {})}\\n\",\n \" for r in parsed_roles\\n\",\n \" ]}}\\n\",\n \"\\n\",\n \"column_level_assignments = convert_to_assignment_format(parsed_roles, TEST_DB)\\n\",\n \"output_file = project_root / 'outputs' / 'column_level_role_assignments.json'\\n\",\n \"with open(output_file, 'w') as f:\\n\",\n \" json.dump(column_level_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \"print(f\\\"Saved to: {output_file}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ This cell is commented out. Skip to Section 8 for batch processing.\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8bc8cb95\",\n \"metadata\": {},\n \"source\": [\n \"## 3. Batch Process All BIRD Databases\\n\",\n \"\\n\",\n \"Generate column-level role assignments for all BIRD databases with parallel processing.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 10,\n \"id\": \"fe3f2e81\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Configuration:\\n\",\n \" Workers: 40\\n\",\n \" Databases: 11\\n\",\n \" Max tokens: 4000\\n\",\n \" Model: deepseek-chat\\n\",\n \"\\n\",\n \"Databases to process:\\n\",\n \" - superhero\\n\",\n \" - thrombosis_prediction\\n\",\n \" - student_club\\n\",\n \" - toxicology\\n\",\n \" - california_schools\\n\",\n \" - debit_card_specializing\\n\",\n \" - card_games\\n\",\n \" - codebase_community\\n\",\n \" - formula_1\\n\",\n \" - european_football_2\\n\",\n \" - financial\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Batch processing configuration\\n\",\n \"from concurrent.futures import ThreadPoolExecutor, as_completed\\n\",\n \"from tqdm.notebook import tqdm\\n\",\n \"\\n\",\n \"# Configuration\\n\",\n \"WORKERS = 40\\n\",\n \"SAMPLES = 100 # 0 for all databases\\n\",\n \"MAX_TOKENS = DEFAULT_MAX_TOKENS\\n\",\n \"\\n\",\n \"# Get all BIRD databases\\n\",\n \"all_dbs = [d.name for d in bird_db_dir.iterdir() if d.is_dir()]\\n\",\n \"if SAMPLES > 0:\\n\",\n \" all_dbs = all_dbs[:SAMPLES]\\n\",\n \"\\n\",\n \"print(f\\\"Configuration:\\\")\\n\",\n \"print(f\\\" Workers: {WORKERS}\\\")\\n\",\n \"print(f\\\" Databases: {len(all_dbs)}\\\")\\n\",\n \"print(f\\\" Max tokens: {MAX_TOKENS}\\\")\\n\",\n \"print(f\\\" Model: {MODEL}\\\")\\n\",\n \"print(f\\\"\\\\nDatabases to process:\\\")\\n\",\n \"for db in all_dbs:\\n\",\n \" print(f\\\" - {db}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 11,\n \"id\": \"7b5edd94\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"✓ Helper functions defined\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Helper functions for batch processing\\n\",\n \"\\n\",\n \"def process_single_database(db_name: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Process a single database and return role assignments.\\\"\\\"\\\"\\n\",\n \" db_path = bird_db_dir / db_name / f\\\"{db_name}.sqlite\\\"\\n\",\n \" \\n\",\n \" if not db_path.exists():\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": f\\\"Database not found\\\"}\\n\",\n \" \\n\",\n \" try:\\n\",\n \" # Extract schema\\n\",\n \" db_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" db_schema_text = format_schema_for_prompt(db_schema, db_name)\\n\",\n \" \\n\",\n \" # Generate roles via LLM\\n\",\n \" user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=db_schema_text)\\n\",\n \" \\n\",\n \" response = oracle.query(\\n\",\n \" prompt_sys=COLUMN_LEVEL_SYSTEM_PROMPT,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3,\\n\",\n \" top_p=0.9,\\n\",\n \" max_completion_tokens=MAX_TOKENS\\n\",\n \" )\\n\",\n \" \\n\",\n \" llm_response = response['answer']\\n\",\n \" usage = response.get('usage', {})\\n\",\n \" \\n\",\n \" # Parse response\\n\",\n \" roles = parse_column_level_roles(llm_response)\\n\",\n \" \\n\",\n \" if not roles:\\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"parse_error\\\",\\n\",\n \" \\\"error\\\": \\\"Failed to parse LLM response\\\",\\n\",\n \" \\\"raw_response\\\": llm_response[:300]\\n\",\n \" }\\n\",\n \" \\n\",\n \" # Validate against schema\\n\",\n \" validation = validate_roles_against_schema(roles, str(db_path))\\n\",\n \" \\n\",\n \" # Convert to target format\\n\",\n \" assignments = []\\n\",\n \" for role in roles:\\n\",\n \" assignments.append({\\n\",\n \" \\\"role\\\": role[\\\"role\\\"],\\n\",\n \" \\\"description\\\": role.get(\\\"description\\\", \\\"\\\"),\\n\",\n \" \\\"policy\\\": role.get(\\\"columns\\\", {})\\n\",\n \" })\\n\",\n \" \\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"success\\\",\\n\",\n \" \\\"roles_count\\\": len(assignments),\\n\",\n \" \\\"valid_roles\\\": validation['valid_roles'],\\n\",\n \" \\\"all_valid\\\": validation['all_valid'],\\n\",\n \" \\\"assignments\\\": assignments,\\n\",\n \" \\\"usage\\\": usage\\n\",\n \" }\\n\",\n \" \\n\",\n \" except Exception as e:\\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"error\\\",\\n\",\n \" \\\"error\\\": str(e)\\n\",\n \" }\\n\",\n \"\\n\",\n \"print(\\\"✓ Helper functions defined\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 12,\n \"id\": \"61e61795\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Starting batch processing with 40 workers...\\n\",\n \"============================================================\\n\",\n \"[1/11] ✓ toxicology: 4 roles (4 valid)\\n\",\n \"[2/11] ✓ debit_card_specializing: 4 roles (4 valid)\\n\",\n \"[3/11] ✓ thrombosis_prediction: 4 roles (4 valid)\\n\",\n \"[4/11] ✓ student_club: 5 roles (5 valid)\\n\",\n \"[5/11] ✓ superhero: 5 roles (5 valid)\\n\",\n \"[6/11] ✓ financial: 5 roles (5 valid)\\n\",\n \"[7/11] ✓ card_games: 5 roles (5 valid)\\n\",\n \"[8/11] ✓ codebase_community: 4 roles (4 valid)\\n\",\n \"[9/11] ✓ california_schools: 5 roles (5 valid)\\n\",\n \"[10/11] ✓ formula_1: 5 roles (5 valid)\\n\",\n \"[11/11] ✓ european_football_2: 5 roles (5 valid)\\n\",\n \"============================================================\\n\",\n \"Completed: 11/11 success, 0 errors\\n\",\n \"Total tokens: 25,679\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Execute batch processing with parallel workers\\n\",\n \"print(f\\\"Starting batch processing with {WORKERS} workers...\\\")\\n\",\n \"print(\\\"=\\\" * 60)\\n\",\n \"\\n\",\n \"results = {\\n\",\n \" \\\"metadata\\\": {\\n\",\n \" \\\"model\\\": MODEL,\\n\",\n \" \\\"max_tokens\\\": MAX_TOKENS,\\n\",\n \" \\\"total_databases\\\": len(all_dbs)\\n\",\n \" },\\n\",\n \" \\\"assignments\\\": {},\\n\",\n \" \\\"errors\\\": []\\n\",\n \"}\\n\",\n \"\\n\",\n \"success_count = 0\\n\",\n \"error_count = 0\\n\",\n \"total_tokens = {\\\"prompt\\\": 0, \\\"completion\\\": 0}\\n\",\n \"\\n\",\n \"with ThreadPoolExecutor(max_workers=WORKERS) as executor:\\n\",\n \" futures = {executor.submit(process_single_database, db): db for db in all_dbs}\\n\",\n \" \\n\",\n \" for i, future in enumerate(as_completed(futures), 1):\\n\",\n \" db_name = futures[future]\\n\",\n \" try:\\n\",\n \" result = future.result()\\n\",\n \" \\n\",\n \" if result[\\\"status\\\"] == \\\"success\\\":\\n\",\n \" results[\\\"assignments\\\"][result[\\\"database\\\"]] = result[\\\"assignments\\\"]\\n\",\n \" success_count += 1\\n\",\n \" \\n\",\n \" usage = result.get(\\\"usage\\\", {})\\n\",\n \" total_tokens[\\\"prompt\\\"] += usage.get(\\\"prompt_tokens\\\", 0)\\n\",\n \" total_tokens[\\\"completion\\\"] += usage.get(\\\"completion_tokens\\\", 0)\\n\",\n \" \\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✓ {db_name}: {result['roles_count']} roles ({result['valid_roles']} valid)\\\")\\n\",\n \" else:\\n\",\n \" results[\\\"errors\\\"].append(result)\\n\",\n \" error_count += 1\\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✗ {db_name}: {result.get('error', result['status'])}\\\")\\n\",\n \" \\n\",\n \" except Exception as e:\\n\",\n \" error_count += 1\\n\",\n \" results[\\\"errors\\\"].append({\\\"database\\\": db_name, \\\"error\\\": str(e)})\\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✗ {db_name}: {e}\\\")\\n\",\n \"\\n\",\n \"results[\\\"metadata\\\"][\\\"success_count\\\"] = success_count\\n\",\n \"results[\\\"metadata\\\"][\\\"error_count\\\"] = error_count\\n\",\n \"results[\\\"metadata\\\"][\\\"total_tokens\\\"] = total_tokens\\n\",\n \"\\n\",\n \"print(\\\"=\\\" * 60)\\n\",\n \"print(f\\\"Completed: {success_count}/{len(all_dbs)} success, {error_count} errors\\\")\\n\",\n \"print(f\\\"Total tokens: {total_tokens['prompt'] + total_tokens['completion']:,}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 13,\n \"id\": \"39d38aa3\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"✓ Results saved to: /home/feiy/Role-SQL-benchmark/outputs/column_level_role_assignments_bird.json\\n\",\n \"\\n\",\n \"File structure:\\n\",\n \" - metadata: model, tokens, counts\\n\",\n \" - assignments: 11 databases\\n\",\n \" - errors: 0 entries\\n\",\n \"\\n\",\n \"Sample (toxicology):\\n\",\n \"[\\n\",\n \" {\\n\",\n \" \\\"role\\\": \\\"MolecularResearcher\\\",\\n\",\n \" \\\"description\\\": \\\"Analyzes molecular structures and their components for research purposes\\\",\\n\",\n \" \\\"policy\\\": {\\n\",\n \" \\\"molecule\\\": [\\n\",\n \" \\\"molecule_id\\\",\\n\",\n \" \\\"label\\\"\\n\",\n \" ],\\n\",\n \" \\\"atom\\\": [\\n\",\n \" \\\"atom_id\\\",\\n\",\n \" \\\"molecule_id\\\",\\n\",\n \" \\\"element\\\"\\n\",\n \" ],\\n\",\n \" \\\"bond\\\": [\\n\",\n \" \\\"bond_id\\\",\\n\",\n \" \\\"molecule_id\\\",\\n\",\n \" \\\"bond_type\\\"\\n\",\n \" ],\\n\",\n \" \\\"connected\\\": [\\n\",\n \" \\\"atom_id\\\",\\n\",\n \" \\\"atom_id2\\\",\\n\",\n \" \\\"bond_id\\\"\\n\",\n \" ]\\n\",\n \" }\\n\",\n \" }\\n\",\n \"]\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Save batch results to file\\n\",\n \"output_file = project_root / 'outputs' / 'column_level_role_assignments_bird.json'\\n\",\n \"\\n\",\n \"with open(output_file, 'w', encoding='utf-8') as f:\\n\",\n \" json.dump(results, f, indent=2, ensure_ascii=False)\\n\",\n \"\\n\",\n \"print(f\\\"✓ Results saved to: {output_file}\\\")\\n\",\n \"print(f\\\"\\\\nFile structure:\\\")\\n\",\n \"print(f\\\" - metadata: model, tokens, counts\\\")\\n\",\n \"print(f\\\" - assignments: {len(results['assignments'])} databases\\\")\\n\",\n \"print(f\\\" - errors: {len(results['errors'])} entries\\\")\\n\",\n \"\\n\",\n \"# Show sample\\n\",\n \"if results['assignments']:\\n\",\n \" sample_db = list(results['assignments'].keys())[0]\\n\",\n \" print(f\\\"\\\\nSample ({sample_db}):\\\")\\n\",\n \" print(json.dumps(results['assignments'][sample_db][:1], indent=2, ensure_ascii=False)[:500])\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"9595c46d\",\n \"metadata\": {},\n \"source\": [\n \"## 4. Generate Column-Level RBAC Dataset\\n\",\n \"\\n\",\n \"Based on the generated role policies, combined with BIRD dataset queries, generate the final RBAC training/evaluation dataset.\\n\",\n \"\\n\",\n \"Dataset format (consistent with table-level):\\n\",\n \"- `db_id`: Database name\\n\",\n \"- `instruction`: Full instruction with schema + evidence + role policy\\n\",\n \"- `role`: Role name\\n\",\n \"- `policy`: Column-level permission policy {table: [columns]}\\n\",\n \"- `input`: User question\\n\",\n \"- `output`: SQL or denial message\\n\",\n \"- `difficulty`: Query difficulty level\\n\",\n \"- `metadata`: Contains gold_sql, permission, query_columns, missing_columns, etc.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 45,\n \"id\": \"405783fa\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Dataset Generation Config ===\\n\",\n \"Role assignments: /home/feiy/Role-SQL-benchmark/outputs/column_level_role_assignments_bird.json\\n\",\n \"BIRD data file: /home/feiy/Role-SQL-benchmark/data/Bird/dev_20251106/dev.json\\n\",\n \"BIRD database dir: /home/feiy/Role-SQL-benchmark/data/Bird/dev_20251106/dev_databases\\n\",\n \"\\n\",\n \"✓ All file paths are valid\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 4.1 Import generator and test dependencies\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator, format_policy_text\\n\",\n \"from src.processors.column_extractor import extract_query_columns\\n\",\n \"from src.processors.column_permission_checker import check_column_permission\\n\",\n \"import glob\\n\",\n \"\\n\",\n \"# Configure paths - use 2025 version (dev_20251106) for latest BIRD data\\n\",\n \"ROLE_ASSIGNMENTS_PATH = project_root / 'outputs' / 'column_level_role_assignments_bird.json'\\n\",\n \"BIRD_DATA_PATH = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev.json'\\n\",\n \"BIRD_DB_ROOT = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases'\\n\",\n \"\\n\",\n \"print(\\\"=== Dataset Generation Config ===\\\")\\n\",\n \"print(f\\\"Role assignments: {ROLE_ASSIGNMENTS_PATH}\\\")\\n\",\n \"print(f\\\"BIRD data file: {BIRD_DATA_PATH}\\\")\\n\",\n \"print(f\\\"BIRD database dir: {BIRD_DB_ROOT}\\\")\\n\",\n \"\\n\",\n \"# Verify file existence\\n\",\n \"# assert ROLE_ASSIGNMENTS_PATH.exists(), f\\\"Role assignments file not found: {ROLE_ASSIGNMENTS_PATH}\\\"\\n\",\n \"assert BIRD_DATA_PATH.exists(), f\\\"BIRD data file not found: {BIRD_DATA_PATH}\\\"\\n\",\n \"assert BIRD_DB_ROOT.exists(), f\\\"BIRD database dir not found: {BIRD_DB_ROOT}\\\"\\n\",\n \"print(\\\"\\\\n✓ All file paths are valid\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"4b80dc65\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== SQL Column Extraction Test ===\\n\",\n \"Tables: {'song', 'singer'}\\n\",\n \"Columns: {'song': {'singer_id', 'title'}, 'singer': {'id', 'name', 'country', 'age'}}\\n\",\n \"Has *: False\\n\",\n \"\\n\",\n \"=== Permission Check Test (Allow) ===\\n\",\n \"Allowed: True\\n\",\n \"Reason: All required columns are permitted\\n\",\n \"\\n\",\n \"=== Permission Check Test (Deny) ===\\n\",\n \"Allowed: False\\n\",\n \"Reason: Missing column permissions: song: singer_id; singer: age, country, id\\n\",\n \"Missing: {'song': {'singer_id'}, 'singer': {'id', 'country', 'age'}}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.2 Test SQL column extraction and permission checking\\n\",\n \"# Verified working, archived to simplify notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"test_sql = \\\"\\\"\\\"\\n\",\n \"SELECT singer.name, singer.age, song.title\\n\",\n \"FROM singer\\n\",\n \"JOIN song ON singer.id = song.singer_id\\n\",\n \"WHERE singer.country = 'USA'\\n\",\n \"\\\"\\\"\\\"\\n\",\n \"\\n\",\n \"print(\\\"=== SQL Column Extraction Test ===\\\")\\n\",\n \"info = extract_query_columns(test_sql)\\n\",\n \"print(f\\\"Tables: {info.tables}\\\")\\n\",\n \"print(f\\\"Columns: {info.columns}\\\")\\n\",\n \"print(f\\\"Has *: {info.has_star}\\\")\\n\",\n \"\\n\",\n \"# Test permission checking\\n\",\n \"policy_allow = {\\n\",\n \" 'singer': ['name', 'age', 'country', 'id'],\\n\",\n \" 'song': ['title', 'singer_id']\\n\",\n \"}\\n\",\n \"policy_deny = {\\n\",\n \" 'singer': ['name'], # Missing 'age', 'country', 'id'\\n\",\n \" 'song': ['title'] # Missing 'singer_id'\\n\",\n \"}\\n\",\n \"\\n\",\n \"print(\\\"\\\\n=== Permission Check Test (Allow) ===\\\")\\n\",\n \"result1 = check_column_permission(test_sql, policy_allow)\\n\",\n \"print(f\\\"Allowed: {result1.allowed}\\\")\\n\",\n \"print(f\\\"Reason: {result1.reason}\\\")\\n\",\n \"\\n\",\n \"print(\\\"\\\\n=== Permission Check Test (Deny) ===\\\")\\n\",\n \"result2 = check_column_permission(test_sql, policy_deny)\\n\",\n \"print(f\\\"Allowed: {result2.allowed}\\\")\\n\",\n \"print(f\\\"Reason: {result2.reason}\\\")\\n\",\n \"print(f\\\"Missing: {result2.missing_columns}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Column extraction/permission tests - verified\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 16,\n \"id\": \"0a018208\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Generator Initialized ===\\n\",\n \"Total BIRD queries: 1534\\n\",\n \"Databases with role assignments: 11\\n\",\n \"Databases with queries: 11\\n\",\n \"\\n\",\n \"Queries per database:\\n\",\n \" california_schools: 89\\n\",\n \" card_games: 191\\n\",\n \" codebase_community: 186\\n\",\n \" debit_card_specializing: 64\\n\",\n \" european_football_2: 129\\n\",\n \" financial: 106\\n\",\n \" formula_1: 174\\n\",\n \" student_club: 158\\n\",\n \" superhero: 129\\n\",\n \" thrombosis_prediction: 163\\n\",\n \" toxicology: 145\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 4.3 Initialize generator\\n\",\n \"generator = ColumnLevelRBACGenerator(\\n\",\n \" role_assignments_path=str(ROLE_ASSIGNMENTS_PATH),\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH),\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" project_root=str(project_root),\\n\",\n \" seed=42\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"=== Generator Initialized ===\\\")\\n\",\n \"print(f\\\"Total BIRD queries: {len(generator.bird_data)}\\\")\\n\",\n \"print(f\\\"Databases with role assignments: {len(generator.role_assignments.get('assignments', {}))}\\\")\\n\",\n \"print(f\\\"Databases with queries: {len(generator.queries_by_db)}\\\")\\n\",\n \"\\n\",\n \"# Show query counts per database\\n\",\n \"print(\\\"\\\\nQueries per database:\\\")\\n\",\n \"for db_id, queries in sorted(generator.queries_by_db.items()):\\n\",\n \" print(f\\\" {db_id}: {len(queries)}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"2a267246\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Generating Sample Dataset ===\\n\",\n \"Generating column-level RBAC dataset...\\n\",\n \" Roles per DB: 1\\n\",\n \" Queries per role: 3\\n\",\n \"Current ratio (allowed): 60.61%\\n\",\n \" Allowed: 20, Denied: 13\\n\",\n \"\\n\",\n \"Dataset saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_sample.json\\n\",\n \"Metadata saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_sample_metadata.json\\n\",\n \" Total entries: 33\\n\",\n \" Allowed: 20 (60.6%)\\n\",\n \" Denied: 13 (39.4%)\\n\",\n \"\\n\",\n \"Statistics:\\n\",\n \" total_entries: 33\\n\",\n \" allowed: 20\\n\",\n \" denied: 13\\n\",\n \" allowed_ratio: 0.6060606060606061\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.4 Generate sample dataset (for format validation)\\n\",\n \"# Sample dataset used for debugging, production uses 4.5\\n\",\n \"\\n\",\n \"'''\\n\",\n \"print(\\\"=== Generating Sample Dataset ===\\\")\\n\",\n \"sample_stats = generator.generate_and_save(\\n\",\n \" output_path=str(project_root / 'outputs' / 'column_level_rbac_dataset_bird_sample.json'),\\n\",\n \" roles_per_db=1, # Use only 1 role per database\\n\",\n \" queries_per_role=3 # Use only 3 queries per role\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"\\\\\\\\nStatistics:\\\")\\n\",\n \"for k, v in sample_stats.items():\\n\",\n \" if k != 'by_database':\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Sample dataset generation - production uses 4.5\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"4851fde0\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Data Format Validation ===\\n\",\n \"Total entries: 33\\n\",\n \"\\n\",\n \"--- Allowed Entry Example ---\\n\",\n \"db_id: toxicology\\n\",\n \"role: MolecularResearcher\\n\",\n \"policy: {\\\"molecule\\\": [\\\"molecule_id\\\", \\\"label\\\"], \\\"atom\\\": [\\\"atom_id\\\", \\\"molecule_id\\\", \\\"element\\\"], \\\"bond\\\": [\\\"bond...\\n\",\n \"input: What are the atom IDs of the bond TR000_2_5?...\\n\",\n \"output: SELECT T.atom_id FROM connected AS T WHERE T.bond_id = 'TR000_2_5'...\\n\",\n \"difficulty: simple\\n\",\n \"instruction length: 1887 chars\\n\",\n \"\\n\",\n \"--- Denied Entry Example ---\\n\",\n \"db_id: thrombosis_prediction\\n\",\n \"role: ClinicalResearcher\\n\",\n \"input: How old was the patient who had the highest hemoglobin count on the date of labo...\\n\",\n \"output: Sorry, I cannot answer.\\n\",\n \"missing_columns: {'patient': ['Diagnosis']}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.5-4.6 Validate generated data format & Show instruction example\\n\",\n \"# Data format verified, archived to simplify notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"sample_file = project_root / 'outputs' / 'column_level_rbac_dataset_bird_sample.json'\\n\",\n \"with open(sample_file, 'r') as f:\\n\",\n \" sample_data = json.load(f)\\n\",\n \"\\n\",\n \"print(\\\"=== Data Format Validation ===\\\")\\n\",\n \"print(f\\\"Total entries: {len(sample_data)}\\\")\\n\",\n \"\\n\",\n \"allowed_entry = next((e for e in sample_data if e['metadata']['permission'] == 'allowed'), None)\\n\",\n \"denied_entry = next((e for e in sample_data if e['metadata']['permission'] == 'denied'), None)\\n\",\n \"\\n\",\n \"if allowed_entry:\\n\",\n \" print(\\\"\\\\\\\\n--- Allowed Entry Example ---\\\")\\n\",\n \" print(f\\\"db_id: {allowed_entry['db_id']}\\\")\\n\",\n \" print(f\\\"role: {allowed_entry['role']}\\\")\\n\",\n \" print(f\\\"policy: {json.dumps(allowed_entry['policy'])[:100]}...\\\")\\n\",\n \" print(f\\\"input: {allowed_entry['input'][:80]}...\\\")\\n\",\n \" print(f\\\"output: {allowed_entry['output'][:80]}...\\\")\\n\",\n \"\\n\",\n \"if denied_entry:\\n\",\n \" print(\\\"\\\\\\\\n--- Denied Entry Example ---\\\")\\n\",\n \" print(f\\\"db_id: {denied_entry['db_id']}\\\")\\n\",\n \" print(f\\\"role: {denied_entry['role']}\\\")\\n\",\n \" print(f\\\"output: {denied_entry['output']}\\\")\\n\",\n \" print(f\\\"missing_columns: {denied_entry['metadata']['missing_columns']}\\\")\\n\",\n \"\\n\",\n \"# Show full instruction\\n\",\n \"print(\\\"\\\\\\\\n=== Full Instruction Example ===\\\")\\n\",\n \"if allowed_entry:\\n\",\n \" print(allowed_entry['instruction'][:2500])\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Data format validation - verified\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"c05e8787\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Generating Full Column-Level RBAC Dataset ===\\n\",\n \"Output: /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_20251224.json\\n\",\n \"Note: Using updated column_extractor with CTE fix\\n\",\n \"Generating column-level RBAC dataset...\\n\",\n \" Roles per DB: None\\n\",\n \" Queries per role: all\\n\",\n \"Current ratio (allowed): 61.18%\\n\",\n \" Allowed: 4351, Denied: 2761\\n\",\n \"\\n\",\n \"Dataset saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_20251224.json\\n\",\n \"Metadata saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_20251224_metadata.json\\n\",\n \" Total entries: 7112\\n\",\n \" Allowed: 4351 (61.2%)\\n\",\n \" Denied: 2761 (38.8%)\\n\",\n \"\\n\",\n \"Full dataset statistics:\\n\",\n \" total_entries: 7112\\n\",\n \" allowed: 4351\\n\",\n \" denied: 2761\\n\",\n \" allowed_ratio: 0.6117829021372329\\n\",\n \"\\n\",\n \"Per-database distribution:\\n\",\n \" toxicology: allowed=580, denied=0\\n\",\n \" debit_card_specializing: allowed=223, denied=33\\n\",\n \" thrombosis_prediction: allowed=432, denied=220\\n\",\n \" student_club: allowed=396, denied=394\\n\",\n \" superhero: allowed=328, denied=317\\n\",\n \" financial: allowed=212, denied=318\\n\",\n \" card_games: allowed=507, denied=448\\n\",\n \" codebase_community: allowed=669, denied=75\\n\",\n \" california_schools: allowed=173, denied=272\\n\",\n \" formula_1: allowed=553, denied=317\\n\",\n \" european_football_2: allowed=278, denied=367\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 4.5 Generate Full Column-Level RBAC Dataset\\n\",\n \"from datetime import datetime\\n\",\n \"\\n\",\n \"# Reload the modules to ensure we use the latest CTE fix\\n\",\n \"import importlib\\n\",\n \"import src.processors.column_extractor\\n\",\n \"import src.processors.column_permission_checker\\n\",\n \"import src.processors.column_level_rbac_generator\\n\",\n \"\\n\",\n \"importlib.reload(src.processors.column_extractor)\\n\",\n \"importlib.reload(src.processors.column_permission_checker)\\n\",\n \"importlib.reload(src.processors.column_level_rbac_generator)\\n\",\n \"\\n\",\n \"# Re-import with fresh code\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator\\n\",\n \"\\n\",\n \"# Re-create generator with fresh modules\\n\",\n \"generator = ColumnLevelRBACGenerator(\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" role_assignments_path=str(ROLE_ASSIGNMENTS_PATH),\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH)\\n\",\n \")\\n\",\n \"\\n\",\n \"# Output with date suffix\\n\",\n \"today = datetime.now().strftime('%Y%m%d')\\n\",\n \"output_path = project_root / 'outputs' / f'column_level_rbac_dataset_bird_{today}.json'\\n\",\n \"\\n\",\n \"print(\\\"=== Generating Full Column-Level RBAC Dataset ===\\\")\\n\",\n \"print(f\\\"Output: {output_path}\\\")\\n\",\n \"\\n\",\n \"full_stats = generator.generate_and_save(\\n\",\n \" output_path=str(output_path),\\n\",\n \" roles_per_db=None, # Use all roles (number varies per db based on LLM generation)\\n\",\n \" queries_per_role=None # Use all queries\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"\\\\nFull dataset statistics:\\\")\\n\",\n \"for k, v in full_stats.items():\\n\",\n \" if k == 'by_database':\\n\",\n \" print(f\\\"\\\\nPer-database distribution:\\\")\\n\",\n \" for db_id, counts in v.items():\\n\",\n \" print(f\\\" {db_id}: allowed={counts['allowed']}, denied={counts['denied']}\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" {k}: {v}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"2142ac6e\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Comparison with Table-Level Data Format ===\\n\",\n \"\\n\",\n \"--- Table-Level Entry Fields ---\\n\",\n \"['db_id', 'difficulty', 'input', 'instruction', 'metadata', 'output', 'role', 'tables']\\n\",\n \"\\n\",\n \"--- Column-Level Entry Fields ---\\n\",\n \"['db_id', 'difficulty', 'input', 'instruction', 'metadata', 'output', 'policy', 'role']\\n\",\n \"\\n\",\n \"Common fields: {'output', 'role', 'metadata', 'difficulty', 'db_id', 'input', 'instruction'}\\n\",\n \"Table-Level only: {'tables'}\\n\",\n \"Column-Level only: {'policy'}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.6 Compare with Table-Level format\\n\",\n \"# Format comparison completed, archived to simplify notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"print(\\\"=== Comparison with Table-Level Data Format ===\\\")\\n\",\n \"\\n\",\n \"table_level_file = project_root / 'data' / 'selected' / 'bird' / 'bird_dev_with_role_rbac.json'\\n\",\n \"if table_level_file.exists():\\n\",\n \" with open(table_level_file, 'r') as f:\\n\",\n \" table_level_data = json.load(f)\\n\",\n \" \\n\",\n \" print(\\\"\\\\\\\\n--- Table-Level Entry Fields ---\\\")\\n\",\n \" print(sorted(table_level_data[0].keys()))\\n\",\n \" \\n\",\n \" print(\\\"\\\\\\\\n--- Column-Level Entry Fields ---\\\") \\n\",\n \" print(sorted(sample_data[0].keys()))\\n\",\n \" \\n\",\n \" tl_keys = set(table_level_data[0].keys())\\n\",\n \" cl_keys = set(sample_data[0].keys())\\n\",\n \" \\n\",\n \" print(f\\\"\\\\\\\\nCommon fields: {tl_keys & cl_keys}\\\")\\n\",\n \" print(f\\\"Table-Level only: {tl_keys - cl_keys}\\\")\\n\",\n \" print(f\\\"Column-Level only: {cl_keys - tl_keys}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Table-level comparison - format verified compatible\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"7392ebc1\",\n \"metadata\": {},\n \"source\": [\n \"## 5. Role Generation Quality Assessment\\n\",\n \"\\n\",\n \"Automated quality checks for generated role files and RBAC datasets.\\n\",\n \"\\n\",\n \"### Quality Metrics\\n\",\n \"\\n\",\n \"| Metric | Healthy Range | Issue Description |\\n\",\n \"|--------|---------------|-------------------|\\n\",\n \"| **Deny Rate** | 5% - 90% | <5% too permissive, >90% too restrictive |\\n\",\n \"| **All-Allow Queries** | <50% | High value indicates poor role differentiation |\\n\",\n \"| **Role-Schema Similarity** | >0.6 | <0.6 indicates poor semantic alignment |\\n\",\n \"| **Policy Overlap** | <70% | >70% indicates redundant roles |\\n\",\n \"| **Coverage Diversity** | std >0.1 | Low std indicates roles have similar permissions |\\n\",\n \"\\n\",\n \"### Evaluation Pipeline\\n\",\n \"1. **Deny Rate Analysis**: Check permission distribution balance\\n\",\n \"2. **Semantic Quality**: Embedding-based role-schema alignment\\n\",\n \"3. **Policy Overlap**: Jaccard similarity to detect redundant roles\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 73,\n \"id\": \"71f8f60f\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Quality Thresholds (Configurable for BIRD) ===\\n\",\n \" deny_rate_min: 5.0\\n\",\n \" coverage_max: 0.85\\n\",\n \" coverage_std_min: 0.065\\n\",\n \" max_overlap: 0.8\\n\",\n \" embed_model: text-embedding-3-small\\n\",\n \" embed_backend: auto\\n\",\n \"\\n\",\n \"Role Assignments: bird_column_level_rbac_assignments_v2_20251230.json\\n\",\n \"Dataset: column_level_rbac_dataset_bird_20251230.json\\n\",\n \"Schema Dir: /home/feiy/Role-SQL-benchmark/data/Bird/dev_20251106/dev_databases\\n\",\n \"\\n\",\n \"✓ Quality evaluator initialized with custom thresholds\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 5.1 Setup Quality Evaluator (force reload all modules)\\n\",\n \"import importlib\\n\",\n \"import sys\\n\",\n \"from pathlib import Path\\n\",\n \"import glob\\n\",\n \"\\n\",\n \"# Force reload the entire module tree\\n\",\n \"if 'src.role_evaluator.quality_metrics' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator.quality_metrics']\\n\",\n \"if 'src.role_evaluator.quality_visualizer' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator.quality_visualizer']\\n\",\n \"if 'src.role_evaluator' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator']\\n\",\n \"\\n\",\n \"from src.role_evaluator import RoleQualityEvaluator, RoleQualityVisualizer, print_quality_report\\n\",\n \"\\n\",\n \"# Configure paths\\n\",\n \"project_root = Path.cwd().parent if Path.cwd().name == 'notebooks' else Path.cwd()\\n\",\n \"\\n\",\n \"# Find most recent dataset and role assignments files\\n\",\n \"role_files = sorted(glob.glob(str(project_root / 'outputs' / 'column_level_role_assignments_bird_*.json')))\\n\",\n \"ROLE_ASSIGNMENTS_PATH = role_files[-1] if role_files else str(project_root / 'outputs' / 'column_level_role_assignments_bird.json')\\n\",\n \"\\n\",\n \"# Check for v2 role assignments (after regeneration)\\n\",\n \"role_files_v2 = sorted([f for f in glob.glob(str(project_root / 'outputs' / 'bird_column_level_rbac_assignments_*.json'))\\n\",\n \" if 'v2' in f])\\n\",\n \"if role_files_v2:\\n\",\n \" ROLE_ASSIGNMENTS_PATH = role_files_v2[-1]\\n\",\n \"\\n\",\n \"dataset_files = sorted([f for f in glob.glob(str(project_root / 'outputs' / 'column_level_rbac_dataset_bird_*.json')) \\n\",\n \" if 'metadata' not in f])\\n\",\n \"if not dataset_files:\\n\",\n \" raise FileNotFoundError(\\\"No RBAC dataset found in outputs/\\\")\\n\",\n \"DATASET_PATH = dataset_files[-1]\\n\",\n \"\\n\",\n \"SCHEMA_DIR = str(project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases')\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# CONFIGURABLE QUALITY THRESHOLDS\\n\",\n \"# Adjust based on dataset characteristics\\n\",\n \"# BIRD has larger DBs with more columns, so thresholds differ from Spider\\n\",\n \"# ============================================================\\n\",\n \"QUALITY_THRESHOLDS = {\\n\",\n \" \\\"deny_rate_min\\\": 5.0, # Warn if deny rate < this (roles too permissive)\\n\",\n \" \\\"coverage_max\\\": 0.85, # Warn if avg coverage > this (roles too broad)\\n\",\n \" \\\"coverage_std_min\\\": 0.065, # Warn if coverage std < this (low diversity)\\n\",\n \" \\\"max_overlap\\\": 0.80, # Warn if max overlap > this (redundant roles)\\n\",\n \"}\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EMBEDDING MODEL CONFIGURATION\\n\",\n \"# Backend auto-detected based on model name, or set explicitly\\n\",\n \"# ============================================================\\n\",\n \"# OpenAI models (requires OPENAI_API_KEY):\\n\",\n \"# - \\\"text-embedding-3-small\\\" (default, cost-effective)\\n\",\n \"# - \\\"text-embedding-3-large\\\" (higher accuracy)\\n\",\n \"# - \\\"text-embedding-ada-002\\\" (legacy)\\n\",\n \"# \\n\",\n \"# BAAI/fastembed models (local, no API key needed):\\n\",\n \"# - \\\"BAAI/bge-small-en-v1.5\\\" (fast, lightweight)\\n\",\n \"# - \\\"BAAI/bge-base-en-v1.5\\\" (balanced)\\n\",\n \"# - \\\"BAAI/bge-large-en-v1.5\\\" (best accuracy)\\n\",\n \"# ============================================================\\n\",\n \"# EMBED_MODEL = \\\"BAAI/bge-small-en-v1.5\\\"\\n\",\n \"EMBED_MODEL = \\\"text-embedding-3-small\\\"\\n\",\n \"EMBED_BACKEND = None # None = auto-detect, or \\\"openai\\\" / \\\"fastembed\\\"\\n\",\n \"\\n\",\n \"print(\\\"=== Quality Thresholds (Configurable for BIRD) ===\\\")\\n\",\n \"for k, v in QUALITY_THRESHOLDS.items():\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"print(f\\\" embed_model: {EMBED_MODEL}\\\")\\n\",\n \"print(f\\\" embed_backend: {EMBED_BACKEND or 'auto'}\\\")\\n\",\n \"\\n\",\n \"print(f\\\"\\\\nRole Assignments: {Path(ROLE_ASSIGNMENTS_PATH).name if ROLE_ASSIGNMENTS_PATH else 'Not found'}\\\")\\n\",\n \"print(f\\\"Dataset: {Path(DATASET_PATH).name if DATASET_PATH else 'Not found'}\\\")\\n\",\n \"print(f\\\"Schema Dir: {SCHEMA_DIR}\\\")\\n\",\n \"\\n\",\n \"if ROLE_ASSIGNMENTS_PATH and DATASET_PATH:\\n\",\n \" evaluator = RoleQualityEvaluator(\\n\",\n \" role_assignments_path=ROLE_ASSIGNMENTS_PATH,\\n\",\n \" dataset_path=DATASET_PATH,\\n\",\n \" schema_dir=SCHEMA_DIR,\\n\",\n \" embed_model=EMBED_MODEL,\\n\",\n \" embed_backend=EMBED_BACKEND,\\n\",\n \" thresholds=QUALITY_THRESHOLDS\\n\",\n \" )\\n\",\n \" print(\\\"\\\\n✓ Quality evaluator initialized with custom thresholds\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"\\\\n Missing files - generate roles and dataset first\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 74,\n \"id\": \"8135e2e3\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[embedding_utils] Using OpenAI embeddings (text-embedding-3-small) for 11 texts\\n\",\n \"[embedding_utils] Using OpenAI embeddings (text-embedding-3-small) for 47 texts\\n\",\n \"Overall: 11 databases, 53 roles, 7252 entries\\n\",\n \"Pass Rate: 100.0% (11/11 pass)\\n\",\n \"\\n\",\n \"Using Thresholds: deny_rate_min=5.0%, max_overlap=80%, coverage_std_min=0.065\\n\",\n \"\\n\",\n \"=== Deny Rate Analysis ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Deny%</th>\\n\",\n \" <th>AllAllow%</th>\\n\",\n \" <th>AllDeny</th>\\n\",\n \" <th>Roles</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>73.5%</td>\\n\",\n \" <td>1.1%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>46.9%</td>\\n\",\n \" <td>2.1%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>37.8%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>54.7%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>57.1%</td>\\n\",\n \" <td>1.6%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>70.6%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>44.7%</td>\\n\",\n \" <td>8.7%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>49.9%</td>\\n\",\n \" <td>1.3%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>49.1%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>33.7%</td>\\n\",\n \" <td>2.5%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>47.4%</td>\\n\",\n \" <td>11.7%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Deny% AllAllow% AllDeny Roles Status\\n\",\n \"0 california_schools 73.5% 1.1% 0 5 OK\\n\",\n \"1 card_games 46.9% 2.1% 0 5 OK\\n\",\n \"2 codebase_community 37.8% 0.0% 0 5 OK\\n\",\n \"3 debit_card_specializing 54.7% 0.0% 0 6 OK\\n\",\n \"4 european_football_2 57.1% 1.6% 0 5 OK\\n\",\n \"5 financial 70.6% 0.0% 0 5 OK\\n\",\n \"6 formula_1 44.7% 8.7% 0 4 OK\\n\",\n \"7 student_club 49.9% 1.3% 0 5 OK\\n\",\n \"8 superhero 49.1% 0.0% 0 5 OK\\n\",\n \"9 thrombosis_prediction 33.7% 2.5% 0 4 OK\\n\",\n \"10 toxicology 47.4% 11.7% 0 4 OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Semantic Quality (Similarity: role-schema alignment, Coverage: column access, Diversity: coverage std) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Similarity</th>\\n\",\n \" <th>Coverage</th>\\n\",\n \" <th>Diversity</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>0.6902</td>\\n\",\n \" <td>34.8%</td>\\n\",\n \" <td>0.0996</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>0.6843</td>\\n\",\n \" <td>50.0%</td>\\n\",\n \" <td>0.2965</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>0.6332</td>\\n\",\n \" <td>60.6%</td>\\n\",\n \" <td>0.2888</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>0.6264</td>\\n\",\n \" <td>63.8%</td>\\n\",\n \" <td>0.1299</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>0.6799</td>\\n\",\n \" <td>31.3%</td>\\n\",\n \" <td>0.0984</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>0.6410</td>\\n\",\n \" <td>46.8%</td>\\n\",\n \" <td>0.1288</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>0.6599</td>\\n\",\n \" <td>44.0%</td>\\n\",\n \" <td>0.1480</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>0.6780</td>\\n\",\n \" <td>50.5%</td>\\n\",\n \" <td>0.1701</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>0.6835</td>\\n\",\n \" <td>61.3%</td>\\n\",\n \" <td>0.1895</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>0.6607</td>\\n\",\n \" <td>69.3%</td>\\n\",\n \" <td>0.3798</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>0.6698</td>\\n\",\n \" <td>69.7%</td>\\n\",\n \" <td>0.1134</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Similarity Coverage Diversity Status\\n\",\n \"0 california_schools 0.6902 34.8% 0.0996 OK\\n\",\n \"1 card_games 0.6843 50.0% 0.2965 OK\\n\",\n \"2 codebase_community 0.6332 60.6% 0.2888 OK\\n\",\n \"3 debit_card_specializing 0.6264 63.8% 0.1299 OK\\n\",\n \"4 european_football_2 0.6799 31.3% 0.0984 OK\\n\",\n \"5 financial 0.6410 46.8% 0.1288 OK\\n\",\n \"6 formula_1 0.6599 44.0% 0.1480 OK\\n\",\n \"7 student_club 0.6780 50.5% 0.1701 OK\\n\",\n \"8 superhero 0.6835 61.3% 0.1895 OK\\n\",\n \"9 thrombosis_prediction 0.6607 69.3% 0.3798 OK\\n\",\n \"10 toxicology 0.6698 69.7% 0.1134 OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Policy Overlap Analysis ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>AvgOverlap</th>\\n\",\n \" <th>MaxOverlap</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>30.7%</td>\\n\",\n \" <td>57.1%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>11.9%</td>\\n\",\n \" <td>23.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>28.0%</td>\\n\",\n \" <td>57.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>47.0%</td>\\n\",\n \" <td>65.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>14.4%</td>\\n\",\n \" <td>23.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>21.5%</td>\\n\",\n \" <td>37.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>29.3%</td>\\n\",\n \" <td>32.9%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>30.2%</td>\\n\",\n \" <td>45.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>33.0%</td>\\n\",\n \" <td>52.6%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>18.1%</td>\\n\",\n \" <td>38.5%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>53.7%</td>\\n\",\n \" <td>66.7%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database AvgOverlap MaxOverlap Status\\n\",\n \"0 california_schools 30.7% 57.1% OK\\n\",\n \"1 card_games 11.9% 23.8% OK\\n\",\n \"2 codebase_community 28.0% 57.8% OK\\n\",\n \"3 debit_card_specializing 47.0% 65.0% OK\\n\",\n \"4 european_football_2 14.4% 23.8% OK\\n\",\n \"5 financial 21.5% 37.0% OK\\n\",\n \"6 formula_1 29.3% 32.9% OK\\n\",\n \"7 student_club 30.2% 45.0% OK\\n\",\n \"8 superhero 33.0% 52.6% OK\\n\",\n \"9 thrombosis_prediction 18.1% 38.5% OK\\n\",\n \"10 toxicology 53.7% 66.7% OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n }\n ],\n \"source\": [\n \"# 5.2 Run Quality Evaluations and Display as DataFrame\\n\",\n \"import pandas as pd\\n\",\n \"import importlib\\n\",\n \"\\n\",\n \"# Force reload modules to use latest code\\n\",\n \"import src.role_evaluator.quality_metrics\\n\",\n \"import src.role_evaluator.quality_visualizer\\n\",\n \"importlib.reload(src.role_evaluator.quality_metrics)\\n\",\n \"importlib.reload(src.role_evaluator.quality_visualizer)\\n\",\n \"\\n\",\n \"from src.role_evaluator.quality_metrics import print_quality_report\\n\",\n \"\\n\",\n \"# Set display options for full content (no truncation)\\n\",\n \"pd.set_option('display.max_colwidth', None)\\n\",\n \"pd.set_option('display.max_rows', None)\\n\",\n \"pd.set_option('display.width', None)\\n\",\n \"\\n\",\n \"if 'evaluator' in dir():\\n\",\n \" report = evaluator.evaluate_all()\\n\",\n \" \\n\",\n \" # Summary\\n\",\n \" print(f\\\"Overall: {report.total_databases} databases, {report.total_roles} roles, {report.total_entries} entries\\\")\\n\",\n \" print(f\\\"Pass Rate: {report.pass_rate:.1%} ({report.total_databases - len(report.databases_with_issues)}/{report.total_databases} pass)\\\")\\n\",\n \" print(f\\\"\\\\nUsing Thresholds: deny_rate_min={QUALITY_THRESHOLDS['deny_rate_min']}%, \\\"\\n\",\n \" f\\\"max_overlap={QUALITY_THRESHOLDS['max_overlap']:.0%}, \\\"\\n\",\n \" f\\\"coverage_std_min={QUALITY_THRESHOLDS['coverage_std_min']}\\\\n\\\")\\n\",\n \" \\n\",\n \" # Deny Rate DataFrame - Full display\\n\",\n \" if report.deny_rate_metrics:\\n\",\n \" deny_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"Deny%\\\": f\\\"{m.deny_rate:.1f}%\\\",\\n\",\n \" \\\"AllAllow%\\\": f\\\"{m.all_allow_pct:.1f}%\\\",\\n\",\n \" \\\"AllDeny\\\": m.all_deny_queries,\\n\",\n \" \\\"Roles\\\": len(m.roles),\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.deny_rate_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"=== Deny Rate Analysis ===\\\")\\n\",\n \" display(deny_df)\\n\",\n \"\\n\",\n \" # Semantic Quality DataFrame \\n\",\n \" if report.semantic_metrics:\\n\",\n \" sem_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"Similarity\\\": f\\\"{m.avg_similarity:.4f}\\\",\\n\",\n \" \\\"Coverage\\\": f\\\"{m.avg_coverage:.1%}\\\",\\n\",\n \" \\\"Diversity\\\": f\\\"{m.coverage_std:.4f}\\\",\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.semantic_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Semantic Quality (Similarity: role-schema alignment, Coverage: column access, Diversity: coverage std) ===\\\")\\n\",\n \" display(sem_df)\\n\",\n \"\\n\",\n \" # Policy Overlap DataFrame\\n\",\n \" if report.overlap_metrics:\\n\",\n \" ovl_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"AvgOverlap\\\": f\\\"{m.avg_overlap:.1%}\\\",\\n\",\n \" \\\"MaxOverlap\\\": f\\\"{m.max_overlap:.1%}\\\",\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.overlap_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Policy Overlap Analysis ===\\\")\\n\",\n \" display(ovl_df)\\n\",\n \"else:\\n\",\n \" print(\\\" Evaluator not initialized\\\")\\n\",\n \"\\n\",\n \"pd.reset_option('display.max_colwidth')\\n\",\n \"pd.reset_option('display.max_rows')\\n\",\n \"pd.reset_option('display.width')\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 75,\n \"id\": \"89f843e7\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# # 5.3 Quality Dashboard Visualization\\n\",\n \"# import matplotlib.pyplot as plt\\n\",\n \"# from src.role_evaluator.quality_visualizer import RoleQualityVisualizer\\n\",\n \"\\n\",\n \"# visualizer = RoleQualityVisualizer(evaluator=evaluator, report=report)\\n\",\n \"# fig = visualizer.plot_quality_dashboard()\\n\",\n \"# plt.show()\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 76,\n \"id\": \"86eb26de\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Total roles: 53, SystemManager: 11\\n\"\n ]\n },\n {\n \"name\": \"stderr\",\n \"output_type\": \"stream\",\n \"text\": [\n \"/tmp/ipykernel_1782890/140258372.py:110: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.\\n\",\n \" plt.tight_layout(rect=[0,0,0.95,0.95])\\n\"\n ]\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Saved: /home/feiy/Role-SQL-benchmark/plot/column_level_rbac_semantic.png\\n\"\n ]\n },\n {\n \"data\": {\n \"image/png\": \"iVBORw0KGgoAAAANSUhEUgAABtcAAAOzCAYAAADDRVknAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAXEgAAFxIBZ5/SUgABAABJREFUeJzs3XdYU9f/B/B3QtgblK1MweLAWdyg4t6tde9arbXVWq211jrraB1122pb96rWva3iFve2OBG3IsqQTXJ/f/AjX+O9gQCBgL5fz5NHc3LuuSfr5HI/93yOTBAEAURERERERERERERERESUK7mhO0BERERERERERERERERUUjC4RkRERERERERERERERKQjBteIiIiIiIiIiIiIiIiIdMTgGhEREREREREREREREZGOGFwjIiIiIiIiIiIiIiIi0hGDa0REREREREREREREREQ6Uhi6A0RERERERERERERUMgmCAEEQDN0NIqJ8k8lkkMlkedqGwTUiIiIiIiIiIiIi0plSqURsbCwSExORnp5u6O4QERWYiYkJrK2t4ejoCCMjo1zrywReVkBEREREREREREREOlAqlbh//z5SU1MN3RUiIr0zMzND2bJlcw2wceYaEREREREREREREekkNjYWqampMDIygrOzMywtLSGXyw3dLSKifFOpVEhKSsKzZ8+QmpqK2NhYODk55bgNg2tEREREREREREREpJPExEQAgLOzM2xtbQ3cGyKigpPL5erx7PHjx0hMTGRwjYiIiIiIiIiIiIgKThAE9RprlpaWhbafzLQ0XFi6BKpMJar3HwiFmVmh7YuIKFv2uJaeng5BECCTybTWZXCNiIiIiIiIiIiIiHIlCIL6/4WZCvL6xvU4MnkiAMDY3BzVPh1QaPsiIsr25riWW3CNyXCJiIiIiIiIiIiIqFhQZWbi9MJ56vsR82YjPem1AXtERCTG4BoRERERERERERERFQs3tm9B/P1omEAGC5kcKbEvcOGvPwzdLSIiDQyuEREREREREREREZHBCSoVTs2fAwDwMjZDORMLAMCZ3xYgNT7ekF0jItLA4BoRERERERERERERGdztvbsRe/MGFJDB09gMbkYmsJIZIS0hHmd/X2jo7hERqTG4RkREREREREREREQGJQgCTs2bDQDwNDaDsUwOmUwGfxNzAMD5P39H8osYA/YQ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3LkTW7Zswb59+9RrrQH5+w6sW7cOH374ISpWrKjTc9CFTBAEQW+tEREREREREREREdE7SaVS4caNGwCAgIAAUWq5/EhNTcWPP/6onh2kMjFBmp83Ml1dIBgbQ5aRAePHT2FyJysVJAA0atQIEydOLLQ1xyjL9OnTMXr0aNy9e1ed6o+oKIWEhOD69euIiYnJdxunT59GcHAwVq5ciR49euRYNy9jHINrRERERERERERERJSrwgiuAYBSqcT69euxbt06jTWu3ubu7o7OnTujS5cuets3aZeSkoJy5cqhffv2mD9/vqG7Q++wp0+fwsXFRaNs48aN+OSTT9C3b1/89ddf+W67Xbt2iIqKwsWLF3MdN/IyxjEtJBEREREREREREREZjJGREbp164bOnTvj5MmT2Lp1Kx49eoTk5GRYWFjA3d0d7dq1Q+3atYtsjTXKWgts+fLlOHv2LARBgEwmM3SX6B3Vs2dPJCQkoHr16rCwsMCVK1ewb98+ODo6Yty4cfluNy0tDdWrV8fo0aP1HpDnzDUiIiIiIiIiIiIiylVhzVwjovfbH3/8gSVLluDWrVtITExEqVKl0KRJE4wfPx4+Pj5F1g+mhSQiIiIiIiIiIiIivWJwjYjeZXkZ4zj6EREREREREREREREREemIwTUiIiIiIiIiIiIiIiIiHTG4RkRERERERERERES5kslk6v+rVCoD9oSISP/eHNfeHO+kMLhGRERERERERERERLmSyWQwMTEBACQlJRm4N0RE+pU9rpmYmOQaXFMURYeIiIiIiIiIiIiIqOSztrZGbGwsnj17BgCwtLSEXM45HERUcqlUKiQlJanHNWtr61y3kQmCIBR2x4iIiIiIiIiIiIio5FMqlbh//z5SU1MN3RUiIr0zMzND2bJlYWRklGM9BteIiIiIiIiIiIiISGdKpRKxsbFITExEenq6obtDRFRgJiYmsLa2hqOjY66BNYDBNSIiIiIiIiIiIiLKJ0EQwFPMRFSSyWSyXNdYE23D4BoRERERERERERERERGRbrjSJBEREREREREREREREZGOGFwjIiIiIiIiIiIiIiIi0hGDa0REREREREREREREREQ6YnCNiIiIiIiIiIiIiIiISEcMrhERERERERERERERERHpiME1IiIiIiIiIiIiIiIiIh0xuEZERERERERERERERESkIwbXiIiIiIiIiIiIiIiIiHTE4BoRERERERERERERERGRjhhcIyIiIiIiIiIiIiIiItIRg2tEREREREREREREREREOmJwjYiIiIiIiIiIiIiIiEhHDK4RERERERERERERERER6YjBNSIiIiIiIiIiIiIiIiIdMbhGREREREREREREREREpCMG14iIiIiIiIiIiIiIiIh0xOAaERERERERERERERERkY4YXCMiIiIiIiIiIiIiIiLSEYNrRERERERERERERERERDpicI2IiIiIiIiIiIiIiIhIRwyuEREREREREREREREREemIwTUiIiIiIiIiIiIiIiIiHTG4RkRERERERERERERERKQjBteIiIiIiIiIiIiIiIiIdMTgGhEREREREREREREREZGOGFwjIiIiIiIiIiIiIiIi0hGDa0REREREREREREREREQ6YnCNiIiIiIiIiIiIiIiISEcMrhERERERERERERERERHpiME1IiIiIiIiIiIiIiIiIh0xuEZERERERERERERERESkIwbXiIiIiIiIiIiIiIiIiHTE4BoRERERERERERERERGRjhhcIyIiIiIiIiIiIiIiItIRg2tEREREREREREREREREOmJwjYiIiIiIiIiIiIiIiEhHDK4RERERERERERERERER6YjBNSIiIiIiIiIiIiIiIiIdMbhGREREREREREREREREpCMG14iIiIiIiIiIiIiIiIh0xOAaERERERERERERERERkY4YXCMiIiIiIiIiIiIiIiLSEYNrRERERERERERERERERDpicI2IiIiIiIiIiIiIiIhIRwyuEREREREREREREREREemIwTUiIiIiIiIiIiIiIiIiHSkM3QEiorxwcXFBUlISypYta+iuEBERERERERERFYn79+/D0tIST58+LXBbbdu2xZ07d/TQK/3z9fXFtm3bDN0NolwxuEZEJUpSUhIyMjIM3Q0iIiIiIiIiIqIik5GRgaSkJL20defOHdy+/R/8vIz10p6+3L7Hc35UcjC4RkQlSvaMtWvXrhm4J4UjMzMTCgWHZiLSH44rRKRvHFeISJ84phCRvr2r40qFChX02p6flzGuHPbUa5sFVSkk2tBdINIZ11wjIiomBEFAdHQ0BEEwdFeI6B3BcYWI9I3jChHpE8cUItI3jitEVFTevRA+EVEJJZPJ4Ovra+huENE7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qv92yAKAVRRteRWu80JaSREKzp3Dho8+RK+H/sb33g343mPuAXzvVacx771dB/bCOqgFyv77ne96Go0GWk3Fn05mi6XKCUvVrWcym2u8NplWq4Xmv7+L+q5nNJlsvzMx0h+bN2xH7249APC9Vx1Xeu8x9/5H6vfe8dPH4Ne+YnvlBgPEGtqj9Pr/nThpMFS/TwECPPT/nUZRFGEor5oVQMVsKR4e+v+uZkV5eXm166kEFXT/PfFStFpRbrRfTx8iYPf+nejcqYvk771Lly7Bq5kVFosFJmP1eabWaKDV/jf3zBaYa8k9TeV6pprzTKPVQqP5b+6ZzLDYrWcA/vtyanVaW16YTCZYzPZ5ofEtw759+9CqVStFv/eYe1Ux9/jeq89779iRveh/7/+y2mq11vhcVSo1dDrtf+uzwmT633oawYDK+FWr1dBqK9er+XucWn1D7plryb3K74W1fI/TaOxzr0VzC37bkoxjxwbYrdeQ954jWEVln1hGpGScFpIU7fovW2+++SZ27tyJlStX4sCBA8jNzUVycjL+9re/OWTfb775Jo4cOYItW7Zg9+7ddh1nixYtsg22rV69Grt27bIt+7//+z+kpKRg1apVtqkYK3322We1zq8cFxeHY8eOYfv27di8eXONF6vX6XTYtm0btm/fjj///BMjR460LcvIyLCb5vHVV1+1HaiOjo5GWloaNm7ciPXr1+P48ePw9fUFUPFl+Z133qn1NXnjjTeQlpaG33//Hampqbjnnntw9OhR/PTTT7Z1EhIScPLkSaxfvx6pqal2029evHix1sHFm50oiigoKOA0S+Q2LCaTrWstxtMX6gZOeq8WBMR4+gBmEzJ+/jcsJl57raGYK0T1U15ejrz8a1D5e8ldSr2o/L2QX1hQ48EhR2KukCu5dPkivJu51vXWKnkFeeDqteoHAZrKaDRC0FY94UkWIqC71rpe00ICgEor1nggm4hcm9VqRW7uZUSEN+1wtSgC5y53VNy0kBHNVci9cqXay8YQkWsSRP5VRAo2fvx4W8dUYmIinnjiCSQmJqJt27a2M5muFx0dbTtLatCgQdi6davd8usvvP7AAw/YXTPs+sf6+/vj0qVLdgNq9957r901zXbt2oW+ffvisccewyeffGJXs+66qcsuXrxoN/j2r3/9C1OnTgVQcc21r776yrbshx9+wF/+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K6+EKIrQ6/W45JJL0LdvX0RHR6OmpgZpaWm2ebWaK05YrVZceumlGD58OPR6PXbv3t0gvtaqP77xhRdeiAEDBkAul+OOO+7A5MmTW70uuVyOefPm4Z577rE99sgjj+Ddd99F7969UVlZid27d2PChAm4+uqrAQBDhw7Fjh07AAApKSno1asX4uLisG/fPuTn57d620258MILMXToUNtndPjwYSQkJGDQoEFITU21K0p2xKJFi/DII48gODgYvXv3hr+/P8rKyuzmXgOaH0969uzZWLZsGQIDA5GSkmI3t999991nK4xdeeWVuOCCC/DHH38AAFauXIlu3bph4MCB8Pb2xpkzZ3Do0CG71wNAYGAgnnjiCbzyyisAaq9qmTp1KhITExEfH4/CwkLs3bsXDzzwACZMmNDie+7s766+++67z26ewrCwMLz//vt4//337ZabNm0apk2bJvn261xwwQUYOXKkrXPv0KFD6NmzJwYOHIgjR440mJPtfNdc1xqAdp2cjdR4IbO6wta91qcVc68R0flBEIRWzwXhbOFdu0L9R+cPE6iOiYHqrwPQV9dA1LV/btJzCVax4VxYErNWVsOnsAQXdXCOWFcRHByMfvEJ2JN5Gur4CGeHYyNXKBDo74+CshIIfroWlxdyCqEJ9YOiFctKRbSKMG9KxaO33mU3jLdMJkNkRCTOFlRA3dWvmTV0kABkaytbXq6VjAUV6BbZrU1DYxOR53CnYxUicg/MK0TkKJL8gpk0aRIefPBBu8d2796N3377DRqNBldeeWWjr5s8eTI+/PBDu5MRhw4dwurVq7Fp0yZbYQ1ofr6nq666ClqtFjt27GhQWJs5c2aDzqbmTJ061a57LTs7Gz/99BOWL1+O48ePt3o9de6++27MmTPH7sdi3XCBv/32W4N55F5++WW7Lru0tDSsXbsW+fn5eOmll9q8/cZ89dVXdpNyFhYWYuPGjThx4gRuueUWSbZRp6CgANu3b8dPP/2ErVu32hW4pk+fjsGDBzf6uqFDh2L48OE4ePAgtm7davc5DRs2zK7YKggCVq5cabeu0tJSbN26FatXr8bu3btt263/o33u3Ll2xU+gdljDX375BSkpKXbdaC1xxHd3rrqOwzrZ2dlYvnx5g1v9oT87w5dffmk3gWtBQQE2btyI3Nxc3HfffXbLqlSqTo/HVbXUtQa0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ykrmzZvH+PHj693u2Vx++eWO/7fb7dx8883k5eU5nlNVle+//97xtzwzptdee82xvN1u5/HHH3d6/fR9qLWJiYlx/P/q1av5/fffAdi1axcvvviiR2Ly8/Nzenzs2LF6lx0xYgS9evVyPN64caPj/2+55RbXB+djFEWhY8eOLikiuLJrrYZ0rwlPyMvLw2b0O/eCXsxm9HP6nnMnV+YVIcQfdDodit27TvwVmx2druVH/feVvLLnwF5Msa45RqqPoii069+R4i2FLbqdWtvVa6h0YXGtYEseV4yd1Or/5sI7+UpOEUJ4D8krQgh38ao51wCefPJJ5s+fT2Vl9d19X375JYsWLaJPnz7odDq2bNmCxWLhhhtuYMKECfTr148tW7YAsGrVKlJSUujVqxd79+51mrOsW7duXHPNNUD18HS33347r7zyClBdLBo8eDCDBg2ioKCA7du31xtfVFQU9957r+O96enppKSkMGDAAKKioigoKGD37t2OrqPrrruu3nUdPXqUHj16MGDAAA4ePEh6errjteDgYG699Vageo4xPz8/KioqAHj55ZdZvXo1AQEBrF271vF8XWbNmsW9995LVFQU3bp1IywsjKKiolpdUDVzM0yYMIGBAwc6ioI//PADHTt2pG/fvgQGBpKTk8OuXbscf5+meOCBB/jkk08c844tW7aM5ORk+vTpQ2BgIDt27CA7O5uCggL8/f3p3bs3kydP5ocffgBg//79pKam0r9/fw4ePMiBAwcc627Xrh133HFHk2PztJEjR/L5558D1XPnde/endjYWLKysjx2x82Z83YMHTqUPn36oNVqufnmm52KpQB33303//d//+f0XHx8PKNGjWrxWH2BN3at1ZC514QnmM1mVBftw56iajQeGxYSXJNXhBDOAgICUCot517QjZQqq9vmF/GFvHIg8wDBAzu2+HYi+7bn+L8OYr8oCo2h5YdqB9DoNFSVuqa4ZquyUrm/nIvuvMgl6xOiLr6QU4QQ3kXyihDCHbzualXPnj357rvvnOapOnXqFL/88guLFy92uvNbo9Ewd+5cevbs6Xju+PHjLF682Kmw1rlzZ3766SenIfeefvppLrjgAsfjiooKVqxYwfbt2+nZsye9e/euN8aZM2fy5z//2fHYarWybt06fvrpJ1avXu00nN/Z5rq68cYbKSoqYunSpU6FNY1Gw4cffkh0dDRQPQnnE0884fTedevWsWzZMnQ6HQ8++GC926hx8uRJfv31V+bOncuKFSucimPXXXcd/fr1A6rv7vjhhx8cjwEKCwtZsWIF8+bNY9OmTY73njm3WEPFxMSwYMEC2rdv73iuuLiYVatW8fPPPzu6+U43e/ZsRowY4Xicn5/PkiVLnApr0dHRzJs3r1UPV/LII484dYpZrVaOHj2Kqqr85S9/8UhMV111lVNMhw4dYu7cuXz//fdOv/8aU6dOrfU3mDZtWpP3l7ZEVVWOHTvW7IPAluhaqyHda8LdrFYrdlr3HYd2lFpd6O7iqrwihHDWsWNHrHnFXvNvS1VVbHkljmHlW3pbrT2vlJaWUlZVjr4F51urofPTE9o5ipLddY+M0hIUrYJdtWO325u9rrydJxnaf4jbCrei7fGFnCKE8C6SVxpJ9bIfIVoRr7zaPX78ePbt28fjjz9O//79CQkJQafTER0dzfDhw5k+fbpj2bi4ODZu3Mg777zDyJEjCQ8PR6fTERYWxtChQ3nttdfYunUrycnJTtvw9/dn+fLlPPLIIyQkJGAwGEhISOCBBx5gzZo1Z50bSqvV8umnn/LLL79www030KlTJ/z8/NDr9cTExDBixAgef/xxtm3bxvDhw+tdz6233sqSJUu46KKLCAoKIiAggIsuuojly5c7uuxqPProo/z73/+mR48e6PV6IiIimDJlCps2baJbt271buPNN9/kH//4B0OHDqVjx45OcY4ePZpPPvmEL7/80uk9sbGxrFu3jtmzZ3PZZZcRExODXq/HZDKRmJjI+PHjef311zly5Ei92z2XgQMHsmfPHl566SWGDRvm+LtFRkZywQUX8NhjjzkNfxkcHMwvv/zC559/ztixY2nXrh06nY6goCD69+/P008/zZ49e5wKpq1Rt27dWLlyJZdccgkBAQEEBAQwfPhwFixYwKOPPuqRmOLj41m8eDGjRo0iODj4nMv7+fk5ui6humB78803t2SIPkNRFBITE5s1dEFLda3VkLnXhLvpdDo0rfwIW4PqdIOPO7kirwghajOZTESEhGErLvd0KADYisoJDwnDaGz5YpEv5JUjR45gjPZ32/baXdCRks2FqG4cSlSja37XtGpXKdtSxISxTZsOQIiG8IWcIoTwLpJXhBDuoqhSxhdCuNitt97Kxx9/DMDF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9OhRjh49yty5c7n33nt544033BG6wz/+8Q+nImCN/fv3s3//fv7973/zwQcf1CrInW7Lli289tpr5zzRq6ysZPTo0axcudLxXFVVFZ999hmbN29my5YtmEymBsX96aef8uWXX9Z6vuZ3v3jxYubOnct3332HRtP4WvKyZcsYN24cFovF6fns7Gyys7NZv349e/bsYfTo0Y7XKioquOGGG5g7d67TeyorKykpKeHAgQP06tWL3r1717nNr776io8//hibzeZ4bt26dYwbN46lS5dy0UUXOS3f1L/dqlWruPLKKzl16pTT88ePH2f+/PnMnz+fCRMm8N1332E0Nuzg/9Zbb3UUVGsUFxdTXFxMRkYGCxYsIDExkRtvvLFB62trVFUlMzOTpKQkOQgUQriE5JXWrbFdazWke020JF/NKwcPHsQU4+/pMBrEFOvPwYMH6dmzp6dD8YiKigpWb1jJoPujPR2Kk7h+Iax+YwU33Xgzfn5+ng6n1WhOTjkvpTsrdi1t1vYNBgOVpe677/xUNiQkNOw7Of+YkWEjk1o4IiF8j68eqwghvI8U15pp9+7dTJkyhYqKCsdzERER9O7dG61Wy+bNm52GZQTw9/enS5cuhIeHExAQQH5+Plu3bqWsrAyoLsBdd911DBo0qFmxTZkyhfLychYuXOh4rmvXrnTr1g2AAQMGAPDOO+84FWcMBgMDBw6ksrKSzZs3o6oqZrOZ2267jc6dOzN06NA6tzd/fvVdeklJSaSmppKRkVHncrm5ueTm5hIfH0/nzp1Zv36947Pv3buXr7/++qxFvDMZDAa6du1KREQEQUFBFBcXs23bNgoLCwH473//y7fffst1113X4HXWeOmllxyFNa1Wy6BBgwgPD+fYsWMcPHiwzkmr//rXvzoV1hRFoWfPnsTHx3P06FF27tx51m1++OGHBAQEcMEFF3Dw4EEOHToEgM1m45lnnnEqrjX1b5ednc2kSZMcvyOA1NRUOnbsyMaNGykuLgZg3rx53Hfffbz33nvn/F1lZWU5FdYiIyMZMGAANpuNI0eOkJmZidnc8LsK65tfLyMjg7i4OHbt2lXrNa1W69i/bTYbe/furXMdBoOB1NRUAMxmc73dfX5+fqSkpADVJ/Hp6el1LhcQEEBSUvVJT2lpKQcPHqxzueDgYBISEoDqouPhw4drLWO328nOziYu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CxYs0L59++zL8fHx9rhd7eKhNi6c0+udd95xiPtqvffee/r+++9VWvrbF21/f3/dcMMNGjZsmEO7smET+/bt67B++vTpWr9+vcO6gwcPmlJ0q8rfrk+fPvb1BQUFDsXC48eP680337Qve3t7q3v37leMJzc3V9OnT1dKSop9XVhYmOLj43XbbbfZ1+Xn5yszM9PZ06xTLBaLGjZsyNAFAExDXql9zOy1VobeazCTp+aVa5tfq6LM2lEQKczMV2zza10dhkuUlJTIy+re157FamEo/EowI6f4+vpq7KNPa+NSQ0WFV/dDzuCgYJUWWVVUZO4PQYsLDW1aalHvP4bK6nPlczyWUqistDD16N7L1DiAusRT71WqhSHJZrjXiw6HqEUorlXRpEmTHIpNCxcuVIsWLdS9e3f17t1bjRs31qOPPipJ6ty5s71dfn6+rr/+evXv319t27bVlClTXJL0rVarnn/+eftyYWGhunbtqvj4eN16660aOXKkfZuXl5cmTZpU4zFeyoWfpyR16dJF/fr1080336y//OUvpnyey5Yt05133qnw8HDddtttGjhwoHr16qUOHTo4tGvZsqUklSu8nT17VgkJCWrfvr3uvvtudezYUa1bt9bWrVurHFtV/nZ//etfFRISYl9+5ZVXdP3116tnz56Ki4vTsWPH7NseeughtWjR4orxFBQUaMKECWrZsqViY2PVs2dP3XPPPbr11lv1j3/8w96uQYMGpvUq9DSGYSgjI4OhCwCYhrxSu5jda60MvddgJk/NK7ExsbL9WjsKIsbJEsVeG3vlhh7IarXKVo1D95nBKDFktVpdHUatYVZOad26tW6/9S79+OXVFcktXhaFhUXofI6503T89KWhVjcGqum1V54nsLjIprVLLBo7ZoLb/KgZqI089V4FgPuhuFZF7dq105IlS1S/fn37uuzsbH333Xf65ptvlJWVZV8/duxYNW3a1L588uRJffnll9q3b5/+/Oc/Oz30ntnGjRunxx9/3L5cWFio9evX66effrL/j8jHx0dvv/22unXr5pIYKzJs2DDdeOON9uWzZ8/qq6++0q5du9SrVy917drVtGPl5ORo06ZN+uyzz/Ttt986zLHWrVs3DRgwwL48b9489e/f375sGIZ2796tL774Qjt27DB1SMSr/dtFRUVpxYoVDvOi7dmzR6tXr9aZ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y7WZO3euQ5sZM2aUa5Obm+vQ5r333ivX5nLi4+Md2j744IMO23v06OGw3cfHx9i6dWuF+zp9+rRzH8j/Wrx4scO+R48efcm2eXl5RnFxcaViT01NLfdZ3HzzzYbNZrO3ef3118u1GThwoMN+xo8fX+66vdjF19rkyZMdtq9du7bccVJTU537oP7X1V5HTz755CX36UxclY194sSJDm379u3rsL1Dhw4O2z/88MNKfQ4AAABAbUbPNQAAAADldOnSRT/++KN69eoli8Vy2bZZWVl64IEHtGzZMof169atc1j++eef5e3tLYvFYn9ZrVYdPnzYod369esve7xevXqpYcOG9uWYmJhybYYMGSI/Pz/7cqtWrcq1qaiHXEpKiiZOnKhu3bqpcePGCggIsMfaqVOncu2PHTt22ViDgoI0YsQIh3Vt2rRxKhaz2Gy2cp/poEGDKjwfSZXuudahQweHa2TevHnq1auXJkyYoHfffVebN2+2DwsaEBAgq7XqU3/ff//9Dses6BoYPny4w/LF10B1fuZmX0c+Pj5VGsr0aowZM8bhb7Vq1SodPXpUknTw4EHt2LHDvq1+/foaNGhQjcYHAAAAuBLFNQAAAAAViouL06pVq3To0CG99dZbGjZsmFq2bHnJ9pMnT3ZYPn78+FUdNz09/bLbLx56rl69euXaXFxsCQgIKNembKi9MvPmzVObNm00bdo0bdy4URkZGSooKLhsLLm5uZfdHh0d7VDkczYWM2VnZ6uwsNBhXceOHU3bf8uWLR3m6LLZbPr22281ffp0jRo1Sl27dlWDBg3Uo0cPrVmzxpRjmnENXDyHmFmq4zqKiopSWFiYmWFeUdOmTXXvvffal202m31+uIuHhBw6dGiF1zUAAADgqSiuAQAAALis6OhojR49Wh9++KEOHDig9PR0vfbaa+UepicnJ+vs2bNVPl5+fv5lt1/cs8rLq/zXmsr2vvrll1/06KOPVrrIZRjGZbdXVBDx9vau1DFqg9mzZ2v58uXq27dvhfOqFRcXa82aNerZs2e5Ho5XozquATNU13XUpEmTqoR11caNG+ew/O6778pms2nx4sUO60eNGlWTYQEAAAAuV/XxOAAAAADUKZGRkRo/frwyMjI0ffp0h23nz59XSEiIpN8KAnv27LFv69Gjh+bNm3fF/V/c06smLFmyxKEnk5eXl55+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\",\n \"text/plain\": [\n \"<IPython.core.display.Image object>\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n }\n ],\n \"source\": [\n \"# 5.4 Semantic Alignment Visualization (Column-Level RBAC)\\n\",\n \"# Circle size = column coverage, Red triangles = SystemManager\\n\",\n \"import random\\n\",\n \"import math\\n\",\n \"import matplotlib.pyplot as plt\\n\",\n \"from matplotlib.colors import PowerNorm\\n\",\n \"from matplotlib import colormaps as cm\\n\",\n \"from collections import defaultdict\\n\",\n \"from matplotlib.lines import Line2D\\n\",\n \"import numpy as np\\n\",\n \"\\n\",\n \"sys.path.insert(0, str(project_root / \\\"plot\\\"))\\n\",\n \"from embedding_utils import cosine_similarity\\n\",\n \"\\n\",\n \"# Prepare records (recompute)\\n\",\n \"role_records = evaluator._prepare_role_records()\\n\",\n \"evaluator._compute_embeddings()\\n\",\n \"\\n\",\n \"# Build vis_records with is_system_manager\\n\",\n \"vis_records = []\\n\",\n \"for spec in role_records:\\n\",\n \" schema_emb = evaluator._schema_emb_dict.get(spec[\\\"schema_text\\\"])\\n\",\n \" role_emb = evaluator._role_emb_dict.get(spec[\\\"role_text\\\"])\\n\",\n \" \\n\",\n \" if schema_emb is not None and role_emb is not None:\\n\",\n \" raw_sim = cosine_similarity(role_emb, schema_emb)\\n\",\n \" similarity = max(0.0, min(1.0, (raw_sim + 1.0) / 2.0))\\n\",\n \" else:\\n\",\n \" similarity = 0.5\\n\",\n \" \\n\",\n \" total_cols = spec.get(\\\"total_columns\\\", 1)\\n\",\n \" accessible_cols = spec.get(\\\"accessible_columns\\\", 0)\\n\",\n \" col_coverage = accessible_cols / total_cols if total_cols > 0 else 0\\n\",\n \" \\n\",\n \" vis_records.append({\\n\",\n \" \\\"db_id\\\": spec[\\\"db_id\\\"],\\n\",\n \" \\\"role\\\": spec[\\\"role\\\"],\\n\",\n \" \\\"similarity\\\": similarity,\\n\",\n \" \\\"is_system_manager\\\": spec.get(\\\"is_system_manager\\\", False),\\n\",\n \" \\\"column_coverage\\\": col_coverage,\\n\",\n \" })\\n\",\n \"\\n\",\n \"sm_count = sum(1 for r in vis_records if r[\\\"is_system_manager\\\"])\\n\",\n \"print(f\\\"Total roles: {len(vis_records)}, SystemManager: {sm_count}\\\")\\n\",\n \"\\n\",\n \"# Plot function\\n\",\n \"def plot_semantic(records, output_path, title, dbs_per_panel=12):\\n\",\n \" db_to_entries = defaultdict(list)\\n\",\n \" sims = []\\n\",\n \" for rec in records:\\n\",\n \" sims.append(rec[\\\"similarity\\\"])\\n\",\n \" size = 90.0 + rec[\\\"column_coverage\\\"] * 220.0\\n\",\n \" db_to_entries[rec[\\\"db_id\\\"]].append({\\n\",\n \" \\\"sim\\\": rec[\\\"similarity\\\"], \\\"size\\\": size,\\n\",\n \" \\\"is_sm\\\": rec[\\\"is_system_manager\\\"],\\n\",\n \" })\\n\",\n \" \\n\",\n \" db_ids = sorted(db_to_entries.keys())\\n\",\n \" panels = [db_ids[i:i+dbs_per_panel] for i in range(0, len(db_ids), dbs_per_panel)]\\n\",\n \" \\n\",\n \" min_s, max_s = min(sims), max(sims)\\n\",\n \" if math.isclose(min_s, max_s): max_s = min_s + 1e-6\\n\",\n \" span = max_s - min_s\\n\",\n \" norm = PowerNorm(gamma=0.55, vmin=min_s, vmax=max_s)\\n\",\n \" cmap = cm[\\\"viridis\\\"]\\n\",\n \" \\n\",\n \" fig = plt.figure(figsize=(11, sum(0.35*len(p)+0.6 for p in panels)+2))\\n\",\n \" gs = fig.add_gridspec(len(panels), 1, height_ratios=[0.35*len(p)+0.6 for p in panels], hspace=0.18)\\n\",\n \" axes = []\\n\",\n \" \\n\",\n \" random.seed(42)\\n\",\n \" sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)\\n\",\n \" sm.set_array([])\\n\",\n \" \\n\",\n \" for pi, chunk in enumerate(panels):\\n\",\n \" ax = fig.add_subplot(gs[pi, 0])\\n\",\n \" axes.append(ax)\\n\",\n \" for ri, db in enumerate(chunk):\\n\",\n \" y = float(ri)\\n\",\n \" ax.axhline(y, color=\\\"#d0d0d0\\\", linestyle=\\\"--\\\", lw=0.9, zorder=0)\\n\",\n \" for e in db_to_entries[db]:\\n\",\n \" x = (e[\\\"sim\\\"] - min_s) / span + random.uniform(-0.012, 0.012)\\n\",\n \" if e[\\\"is_sm\\\"]:\\n\",\n \" ax.scatter(x, y, s=250, c=\\\"#d62728\\\", alpha=0.95,\\n\",\n \" edgecolors=\\\"#8c1c15\\\", lw=1.0, marker=\\\"^\\\", zorder=4)\\n\",\n \" else:\\n\",\n \" ax.scatter(x, y + random.uniform(-0.16, 0.16), s=e[\\\"size\\\"]*2.5,\\n\",\n \" c=[e[\\\"sim\\\"]], cmap=cmap, norm=norm, alpha=0.82,\\n\",\n \" edgecolors=\\\"#333\\\", lw=0.45, marker=\\\"o\\\")\\n\",\n \" ax.set_yticks(range(len(chunk)))\\n\",\n \" ax.set_yticklabels(chunk, fontsize=10, weight='bold')\\n\",\n \" ax.set_xlim(-0.05, 1.05)\\n\",\n \" ax.set_ylim(-0.6, len(chunk)-0.4)\\n\",\n \" ax.set_xticks(np.linspace(0, 1, 5))\\n\",\n \" ax.set_xticklabels([f\\\"{min_s + p*span:.2f}\\\" for p in np.linspace(0,1,5)], fontsize=9)\\n\",\n \" ax.grid(axis=\\\"x\\\", ls=\\\":\\\", lw=0.5, alpha=0.45)\\n\",\n \" \\n\",\n \" axes[-1].set_xlabel(\\\"Semantic similarity\\\", fontsize=12, weight='bold')\\n\",\n \" fig.suptitle(f\\\"{title}: Role-Schema Alignment\\\", fontsize=14, y=0.98, weight='bold')\\n\",\n \" cbar = fig.colorbar(sm, ax=axes, fraction=0.03, pad=0.015)\\n\",\n \" cbar.set_label(\\\"Similarity\\\", fontsize=10)\\n\",\n \" \\n\",\n \" legend = [\\n\",\n \" Line2D([0],[0], marker='^', color='w', markerfacecolor='#d62728',\\n\",\n \" markeredgecolor='#8c1c15', markersize=12, label='SystemManager'),\\n\",\n \" Line2D([0],[0], marker='o', color='w', markerfacecolor='#21918c',\\n\",\n \" markeredgecolor='#333', markersize=8, label='Other (size=col coverage)'),\\n\",\n \" ]\\n\",\n \" fig.legend(handles=legend, loc='upper right', fontsize=9)\\n\",\n \" plt.tight_layout(rect=[0,0,0.95,0.95])\\n\",\n \" fig.savefig(output_path, dpi=150, bbox_inches='tight')\\n\",\n \" plt.close(fig)\\n\",\n \" print(f\\\"Saved: {output_path}\\\")\\n\",\n \"\\n\",\n \"output_path = project_root / \\\"plot\\\" / \\\"column_level_rbac_semantic.png\\\"\\n\",\n \"plot_semantic(vis_records, output_path, \\\"Column-Level RBAC (BIRD)\\\")\\n\",\n \"from IPython.display import Image, display\\n\",\n \"display(Image(filename=str(output_path)))\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 13,\n \"id\": \"2dd807b4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Databases Requiring Review (1/11) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Deny%</th>\\n\",\n \" <th>AllAllow%</th>\\n\",\n \" <th>Coverage</th>\\n\",\n \" <th>CovStd</th>\\n\",\n \" <th>MaxOverlap</th>\\n\",\n \" <th>Issues</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>63.1%</td>\\n\",\n \" <td>5.6%</td>\\n\",\n \" <td>50.8%</td>\\n\",\n \" <td>0.059</td>\\n\",\n \" <td>72.3%</td>\\n\",\n \" <td>all-deny queries found (1) - SystemManager coverage broken; Low role diversity</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Deny% AllAllow% Coverage CovStd MaxOverlap \\\\\\n\",\n \"0 california_schools 63.1% 5.6% 50.8% 0.059 72.3% \\n\",\n \"\\n\",\n \" Issues \\n\",\n \"0 all-deny queries found (1) - SystemManager coverage broken; Low role diversity \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"Issue Thresholds:\\n\",\n \" - Deny% < 5%: roles too permissive\\n\",\n \" - Coverage > 85%: most roles have near-full access\\n\",\n \" - CovStd < 0.065: roles have similar coverage (low diversity)\\n\",\n \" - MaxOverlap >80%: redundant roles with similar policies\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 5.5 Export Problematic Databases as DataFrame\\n\",\n \"if report.databases_with_issues:\\n\",\n \" issues_data = []\\n\",\n \" for db_id in report.databases_with_issues:\\n\",\n \" row = {\\\"Database\\\": db_id}\\n\",\n \" \\n\",\n \" if db_id in report.deny_rate_metrics:\\n\",\n \" m = report.deny_rate_metrics[db_id]\\n\",\n \" row[\\\"Deny%\\\"] = f\\\"{m.deny_rate:.1f}%\\\"\\n\",\n \" row[\\\"AllAllow%\\\"] = f\\\"{m.all_allow_pct:.1f}%\\\"\\n\",\n \" \\n\",\n \" if db_id in report.semantic_metrics:\\n\",\n \" m = report.semantic_metrics[db_id]\\n\",\n \" row[\\\"Coverage\\\"] = f\\\"{m.avg_coverage:.1%}\\\" # Source of \\\"Most roles too permissive\\\" (>90%)\\n\",\n \" row[\\\"CovStd\\\"] = f\\\"{m.coverage_std:.3f}\\\" # Source of \\\"Low role diversity\\\" (<0.1)\\n\",\n \" \\n\",\n \" if db_id in report.overlap_metrics:\\n\",\n \" m = report.overlap_metrics[db_id]\\n\",\n \" row[\\\"MaxOverlap\\\"] = f\\\"{m.max_overlap:.1%}\\\" # Source of \\\"High max overlap\\\" (>70%)\\n\",\n \" \\n\",\n \" row[\\\"Issues\\\"] = \\\"; \\\".join(report.all_issues.get(db_id, []))\\n\",\n \" issues_data.append(row)\\n\",\n \" \\n\",\n \" issues_df = pd.DataFrame(issues_data)\\n\",\n \" print(f\\\"=== Databases Requiring Review ({len(issues_df)}/{report.total_databases}) ===\\\")\\n\",\n \" \\n\",\n \" # Display with full column width for Issues column\\n\",\n \" pd.set_option('display.max_colwidth', None)\\n\",\n \" pd.set_option('display.max_rows', None)\\n\",\n \" display(issues_df)\\n\",\n \" pd.reset_option('display.max_colwidth')\\n\",\n \" pd.reset_option('display.max_rows')\\n\",\n \" \\n\",\n \" print(\\\"\\\\nIssue Thresholds:\\\")\\n\",\n \" print(\\\" - Deny% < 5%: roles too permissive\\\")\\n\",\n \" print(\\\" - Coverage > 85%: most roles have near-full access\\\")\\n\",\n \" print(\\\" - CovStd < 0.065: roles have similar coverage (low diversity)\\\")\\n\",\n \" print(\\\" - MaxOverlap >80%: redundant roles with similar policies\\\")\\n\",\n \"\\n\",\n \"else:\\n\",\n \" print(\\\"[OK] All databases pass quality checks!\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"7586c455\",\n \"metadata\": {},\n \"source\": [\n \"## 6. Regenerate Roles for Problematic Databases\\n\",\n \"\\n\",\n \"Human-in-the-loop workflow for fixing quality issues:\\n\",\n \"\\n\",\n \"| Step | Cell | Description |\\n\",\n \"|------|------|-------------|\\n\",\n \"| 1 | 6.1 | Select database, view schema and current roles |\\n\",\n \"| 2 | 6.2 | Write regeneration hints (edit manually) |\\n\",\n \"| 3 | 6.3 | Execute regeneration with LLM |\\n\",\n \"| 4 | 6.4 | Preview and compare OLD vs NEW |\\n\",\n \"| 5 | 6.4.1 | Select & combine roles from OLD and NEW |\\n\",\n \"| 6 | 6.5 | Apply changes if satisfied |\\n\",\n \"| 7 | 6.6 | Regenerate RBAC Dataset with updated roles |\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 32,\n \"id\": \"70f25858\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"Database: formula_1\\n\",\n \"======================================================================\\n\",\n \"\\n\",\n \"=== Schema (13 tables, 94 columns) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Table</th>\\n\",\n \" <th>Column</th>\\n\",\n \" <th>Type</th>\\n\",\n \" <th>PK</th>\\n\",\n \" <th>FK</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>circuitId</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td>Yes</td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>circuitRef</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>name</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>location</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>country</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>...</th>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>89</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLap</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>90</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>rank</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>91</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLapTime</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>92</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLapSpeed</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>93</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>statusId</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td>status.statusId</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"<p>94 rows × 5 columns</p>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Table Column Type PK FK\\n\",\n \"0 circuits circuitId INTEGER Yes \\n\",\n \"1 circuits circuitRef TEXT \\n\",\n \"2 circuits name TEXT \\n\",\n \"3 circuits location TEXT \\n\",\n \"4 circuits country TEXT \\n\",\n \".. ... ... ... ... ...\\n\",\n \"89 results fastestLap INTEGER \\n\",\n \"90 results rank INTEGER \\n\",\n \"91 results fastestLapTime TEXT \\n\",\n \"92 results fastestLapSpeed TEXT \\n\",\n \"93 results statusId INTEGER status.statusId\\n\",\n \"\\n\",\n \"[94 rows x 5 columns]\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Current Roles (5) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Columns</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap times, pit stops, and qualifying results</td>\\n\",\n \" <td>9</td>\\n\",\n \" <td>65 (*)</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date, time]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>StandingsManager</td>\\n\",\n \" <td>Manages championship standings and constructor/driver points calculations</td>\\n\",\n \" <td>7</td>\\n\",\n \" <td>43 (*)</td>\\n\",\n \" <td>constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; results: [resultId, raceId, driverId, constructorId, position, positionOrder, points]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>MediaCoordinator</td>\\n\",\n \" <td>Provides public information for media and fan engagement</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>33 (*)</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, constructorRef, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [*]; races: [raceId, year, round, circuitId, name, date, time, url]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal details including date of birth</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>32 (*)</td>\\n\",\n \" <td>drivers: [*]; races: [raceId, year, round, name, date]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, statusId]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintenance and data integrity</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>94 (*)</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*]</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Role \\\\\\n\",\n \"0 RaceAnalyst \\n\",\n \"1 StandingsManager \\n\",\n \"2 MediaCoordinator \\n\",\n \"3 DriverManager \\n\",\n \"4 SystemManager \\n\",\n \"\\n\",\n \" Description \\\\\\n\",\n \"0 Analyzes race performance data including lap times, pit stops, and qualifying results \\n\",\n \"1 Manages championship standings and constructor/driver points calculations \\n\",\n \"2 Provides public information for media and fan engagement \\n\",\n \"3 Manages driver information and personal details including date of birth \\n\",\n \"4 Full administrative access for system maintenance and data integrity \\n\",\n \"\\n\",\n \" Tables Columns \\\\\\n\",\n \"0 9 65 (*) \\n\",\n \"1 7 43 (*) \\n\",\n \"2 6 33 (*) \\n\",\n \"3 4 32 (*) \\n\",\n \"4 13 94 (*) \\n\",\n \"\\n\",\n \" Policy \\n\",\n \"0 circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date, time]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]; status: [statusId, status] \\n\",\n \"1 constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; results: [resultId, raceId, driverId, constructorId, position, positionOrder, points] \\n\",\n \"2 circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, constructorRef, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [*]; races: [raceId, year, round, circuitId, name, date, time, url]; status: [statusId, status] \\n\",\n \"3 drivers: [*]; races: [raceId, year, round, name, date]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, statusId]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins] \\n\",\n \"4 circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*] \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Role x Table Matrix (column counts, * expanded) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<style type=\\\"text/css\\\">\\n\",\n \"#T_3c946_row0_col0, #T_3c946_row0_col1, #T_3c946_row0_col2, #T_3c946_row0_col3, #T_3c946_row0_col6, #T_3c946_row0_col7, #T_3c946_row0_col8, #T_3c946_row0_col11, #T_3c946_row0_col12, #T_3c946_row1_col1, #T_3c946_row1_col2, #T_3c946_row1_col4, #T_3c946_row1_col6, #T_3c946_row1_col7, #T_3c946_row1_col8, #T_3c946_row1_col10, #T_3c946_row2_col1, #T_3c946_row2_col2, #T_3c946_row2_col4, #T_3c946_row2_col11, #T_3c946_row3_col0, #T_3c946_row3_col6, #T_3c946_row3_col7, #T_3c946_row3_col8, #T_3c946_row3_col11, #T_3c946_row3_col12 {\\n\",\n \" background-color: #f7fbff;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col4, #T_3c946_row2_col5, #T_3c946_row2_col7, #T_3c946_row2_col9, #T_3c946_row3_col2, #T_3c946_row3_col4, #T_3c946_row3_col5, #T_3c946_row3_col10, #T_3c946_row4_col2, #T_3c946_row4_col4, #T_3c946_row4_col7 {\\n\",\n \" background-color: #99c7e0;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col5, #T_3c946_row2_col8, #T_3c946_row4_col0, #T_3c946_row4_col5, #T_3c946_row4_col8 {\\n\",\n \" background-color: #6aaed6;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col9, #T_3c946_row1_col3, #T_3c946_row3_col1, #T_3c946_row4_col1, #T_3c946_row4_col3 {\\n\",\n \" background-color: #bdd7ec;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col10 {\\n\",\n \" background-color: #4695c8;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row1_col0, #T_3c946_row1_col5, #T_3c946_row1_col9, #T_3c946_row4_col9 {\\n\",\n \" background-color: #82bbdb;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row1_col11, #T_3c946_row1_col12, #T_3c946_row2_col12, #T_3c946_row4_col11, #T_3c946_row4_col12 {\\n\",\n \" background-color: #e1edf8;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col0, #T_3c946_row2_col6, #T_3c946_row3_col9, #T_3c946_row4_col6 {\\n\",\n \" background-color: #abd0e6;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col3, #T_3c946_row3_col3 {\\n\",\n \" background-color: #ccdff1;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col10 {\\n\",\n \" background-color: #083e81;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row4_col10 {\\n\",\n \" background-color: #08306b;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"</style>\\n\",\n \"<table id=\\\"T_3c946\\\">\\n\",\n \" <thead>\\n\",\n \" <tr>\\n\",\n \" <th class=\\\"index_name level0\\\" >Table</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col0\\\" class=\\\"col_heading level0 col0\\\" >circuits</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col1\\\" class=\\\"col_heading level0 col1\\\" >constructorResults</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col2\\\" class=\\\"col_heading level0 col2\\\" >constructorStandings</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col3\\\" class=\\\"col_heading level0 col3\\\" >constructors</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col4\\\" class=\\\"col_heading level0 col4\\\" >driverStandings</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col5\\\" class=\\\"col_heading level0 col5\\\" >drivers</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col6\\\" class=\\\"col_heading level0 col6\\\" >lapTimes</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col7\\\" class=\\\"col_heading level0 col7\\\" >pitStops</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col8\\\" class=\\\"col_heading level0 col8\\\" >qualifying</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col9\\\" class=\\\"col_heading level0 col9\\\" >races</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col10\\\" class=\\\"col_heading level0 col10\\\" >results</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col11\\\" class=\\\"col_heading level0 col11\\\" >seasons</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col12\\\" class=\\\"col_heading level0 col12\\\" >status</th>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th class=\\\"index_name level0\\\" >Role</th>\\n\",\n \" <th class=\\\"blank col0\\\" > </th>\\n\",\n \" <th class=\\\"blank col1\\\" > </th>\\n\",\n \" <th class=\\\"blank col2\\\" > </th>\\n\",\n \" <th class=\\\"blank col3\\\" > </th>\\n\",\n \" <th class=\\\"blank col4\\\" > </th>\\n\",\n \" <th class=\\\"blank col5\\\" > </th>\\n\",\n \" <th class=\\\"blank col6\\\" > </th>\\n\",\n \" <th class=\\\"blank col7\\\" > </th>\\n\",\n \" <th class=\\\"blank col8\\\" > </th>\\n\",\n \" <th class=\\\"blank col9\\\" > </th>\\n\",\n \" <th class=\\\"blank col10\\\" > </th>\\n\",\n \" <th class=\\\"blank col11\\\" > </th>\\n\",\n \" <th class=\\\"blank col12\\\" > </th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row0\\\" class=\\\"row_heading level0 row0\\\" >DriverManager</th>\\n\",\n \" <td id=\\\"T_3c946_row0_col0\\\" class=\\\"data row0 col0\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col1\\\" class=\\\"data row0 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col2\\\" class=\\\"data row0 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col3\\\" class=\\\"data row0 col3\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col4\\\" class=\\\"data row0 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col5\\\" class=\\\"data row0 col5\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col6\\\" class=\\\"data row0 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col7\\\" class=\\\"data row0 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col8\\\" class=\\\"data row0 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col9\\\" class=\\\"data row0 col9\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col10\\\" class=\\\"data row0 col10\\\" >11.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col11\\\" class=\\\"data row0 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col12\\\" class=\\\"data row0 col12\\\" >0.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row1\\\" class=\\\"row_heading level0 row1\\\" >MediaCoordinator</th>\\n\",\n \" <td id=\\\"T_3c946_row1_col0\\\" class=\\\"data row1 col0\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col1\\\" class=\\\"data row1 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col2\\\" class=\\\"data row1 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col3\\\" class=\\\"data row1 col3\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col4\\\" class=\\\"data row1 col4\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col5\\\" class=\\\"data row1 col5\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col6\\\" class=\\\"data row1 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col7\\\" class=\\\"data row1 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col8\\\" class=\\\"data row1 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col9\\\" class=\\\"data row1 col9\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col10\\\" class=\\\"data row1 col10\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col11\\\" class=\\\"data row1 col11\\\" >2.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col12\\\" class=\\\"data row1 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row2\\\" class=\\\"row_heading level0 row2\\\" >RaceAnalyst</th>\\n\",\n \" <td id=\\\"T_3c946_row2_col0\\\" class=\\\"data row2 col0\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col1\\\" class=\\\"data row2 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col2\\\" class=\\\"data row2 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col3\\\" class=\\\"data row2 col3\\\" >4.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col4\\\" class=\\\"data row2 col4\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col5\\\" class=\\\"data row2 col5\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col6\\\" class=\\\"data row2 col6\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col7\\\" class=\\\"data row2 col7\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col8\\\" class=\\\"data row2 col8\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col9\\\" class=\\\"data row2 col9\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col10\\\" class=\\\"data row2 col10\\\" >17.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col11\\\" class=\\\"data row2 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col12\\\" class=\\\"data row2 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row3\\\" class=\\\"row_heading level0 row3\\\" >StandingsManager</th>\\n\",\n \" <td id=\\\"T_3c946_row3_col0\\\" class=\\\"data row3 col0\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col1\\\" class=\\\"data row3 col1\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col2\\\" class=\\\"data row3 col2\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col3\\\" class=\\\"data row3 col3\\\" >4.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col4\\\" class=\\\"data row3 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col5\\\" class=\\\"data row3 col5\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col6\\\" class=\\\"data row3 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col7\\\" class=\\\"data row3 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col8\\\" class=\\\"data row3 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col9\\\" class=\\\"data row3 col9\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col10\\\" class=\\\"data row3 col10\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col11\\\" class=\\\"data row3 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col12\\\" class=\\\"data row3 col12\\\" >0.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row4\\\" class=\\\"row_heading level0 row4\\\" >SystemManager</th>\\n\",\n \" <td id=\\\"T_3c946_row4_col0\\\" class=\\\"data row4 col0\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col1\\\" class=\\\"data row4 col1\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col2\\\" class=\\\"data row4 col2\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col3\\\" class=\\\"data row4 col3\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col4\\\" class=\\\"data row4 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col5\\\" class=\\\"data row4 col5\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col6\\\" class=\\\"data row4 col6\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col7\\\" class=\\\"data row4 col7\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col8\\\" class=\\\"data row4 col8\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col9\\\" class=\\\"data row4 col9\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col10\\\" class=\\\"data row4 col10\\\" >18.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col11\\\" class=\\\"data row4 col11\\\" >2.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col12\\\" class=\\\"data row4 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\"\n ],\n \"text/plain\": [\n \"<pandas.io.formats.style.Styler at 0x7f5045bdae00>\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"[SUMMARY] 1 database(s) selected: ['formula_1']\\n\",\n \"[NEXT] Edit hints in Cell 6.2, then run Cell 6.3\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.1 Select Database(s) & View Current State\\n\",\n \"# Supports single db (str) or multiple dbs (list)\\n\",\n \"\\n\",\n \"TARGET_DBS = [\\\"formula_1\\\"] # Single: \\\"toxicology\\\" or Multiple: [\\\"toxicology\\\", \\\"california_schools\\\"]\\n\",\n \"\\n\",\n \"# Normalize to list\\n\",\n \"if isinstance(TARGET_DBS, str):\\n\",\n \" TARGET_DBS = [TARGET_DBS]\\n\",\n \"\\n\",\n \"# Load current role assignments\\n\",\n \"with open(ROLE_ASSIGNMENTS_PATH, 'r') as f:\\n\",\n \" all_assignments = json.load(f)\\n\",\n \"\\n\",\n \"available_dbs = set(all_assignments.get('assignments', {}).keys())\\n\",\n \"valid_target_dbs = [db for db in TARGET_DBS if db in available_dbs]\\n\",\n \"invalid_dbs = [db for db in TARGET_DBS if db not in available_dbs]\\n\",\n \"\\n\",\n \"if invalid_dbs:\\n\",\n \" print(f\\\"[WARN] Databases not found: {invalid_dbs}\\\")\\n\",\n \" print(f\\\"Available: {sorted(available_dbs)}\\\\n\\\")\\n\",\n \"\\n\",\n \"# Store schemas for later use\\n\",\n \"target_schemas = {}\\n\",\n \"\\n\",\n \"for TARGET_DB in valid_target_dbs:\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"Database: {TARGET_DB}\\\")\\n\",\n \" print(f\\\"{'='*70}\\\")\\n\",\n \" \\n\",\n \" # --- Schema Summary ---\\n\",\n \" db_path = bird_db_dir / TARGET_DB / f\\\"{TARGET_DB}.sqlite\\\"\\n\",\n \" if db_path.exists():\\n\",\n \" target_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" target_schemas[TARGET_DB] = target_schema\\n\",\n \" \\n\",\n \" schema_rows = []\\n\",\n \" for table, info in target_schema.items():\\n\",\n \" for col in info['columns']:\\n\",\n \" schema_rows.append({\\n\",\n \" \\\"Table\\\": table,\\n\",\n \" \\\"Column\\\": col['name'],\\n\",\n \" \\\"Type\\\": col['type'],\\n\",\n \" \\\"PK\\\": \\\"Yes\\\" if col['pk'] else \\\"\\\",\\n\",\n \" \\\"FK\\\": col.get('fk', '') or \\\"\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" schema_df = pd.DataFrame(schema_rows)\\n\",\n \" print(f\\\"\\\\n=== Schema ({len(target_schema)} tables, {len(schema_df)} columns) ===\\\")\\n\",\n \" display(schema_df)\\n\",\n \" else:\\n\",\n \" print(f\\\"[ERROR] Database not found: {db_path}\\\")\\n\",\n \" target_schemas[TARGET_DB] = None\\n\",\n \" continue\\n\",\n \"\\n\",\n \" # --- Current Roles ---\\n\",\n \" if TARGET_DB in all_assignments.get('assignments', {}):\\n\",\n \" current_roles = all_assignments['assignments'][TARGET_DB]\\n\",\n \" \\n\",\n \" # Set display options for full content\\n\",\n \" pd.set_option('display.max_colwidth', None)\\n\",\n \" \\n\",\n \" roles_rows = []\\n\",\n \" for role_info in current_roles:\\n\",\n \" policy = role_info.get('policy', {})\\n\",\n \" total_access = count_policy_columns(policy, target_schema)\\n\",\n \" has_wildcard = has_wildcard_policy(policy)\\n\",\n \" cols_display = f\\\"{total_access}\\\" + (\\\" (*)\\\" if has_wildcard else \\\"\\\")\\n\",\n \" # Full policy string\\n\",\n \" policy_str = \\\"; \\\".join([\\n\",\n \" f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" \\n\",\n \" for t, c in policy.items()\\n\",\n \" ])\\n\",\n \" roles_rows.append({\\n\",\n \" \\\"Role\\\": role_info['role'],\\n\",\n \" \\\"Description\\\": role_info.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Columns\\\": cols_display,\\n\",\n \" \\\"Policy\\\": policy_str\\n\",\n \" })\\n\",\n \" \\n\",\n \" roles_df = pd.DataFrame(roles_rows)\\n\",\n \" print(f\\\"\\\\n=== Current Roles ({len(current_roles)}) ===\\\")\\n\",\n \" display(roles_df)\\n\",\n \" \\n\",\n \" pd.reset_option('display.max_colwidth')\\n\",\n \" \\n\",\n \" # Policy detail matrix\\n\",\n \" policy_rows = []\\n\",\n \" for role_info in current_roles:\\n\",\n \" policy = role_info.get('policy', {})\\n\",\n \" for table, cols in policy.items():\\n\",\n \" num_cols = count_policy_columns({table: cols}, target_schema)\\n\",\n \" policy_rows.append({\\n\",\n \" \\\"Role\\\": role_info['role'],\\n\",\n \" \\\"Table\\\": table,\\n\",\n \" \\\"NumCols\\\": num_cols\\n\",\n \" })\\n\",\n \" \\n\",\n \" if policy_rows:\\n\",\n \" policy_df = pd.DataFrame(policy_rows)\\n\",\n \" pivot = policy_df.pivot_table(index='Role', columns='Table', values='NumCols', fill_value=0)\\n\",\n \" print(\\\"\\\\n=== Role x Table Matrix (column counts, * expanded) ===\\\")\\n\",\n \" display(pivot.style.background_gradient(cmap='Blues', axis=None))\\n\",\n \" \\n\",\n \" # Quality issues\\n\",\n \" if 'report' in dir() and TARGET_DB in report.all_issues:\\n\",\n \" print(f\\\"\\\\n=== Quality Issues ===\\\")\\n\",\n \" issues = report.all_issues[TARGET_DB]\\n\",\n \" if TARGET_DB in report.deny_rate_metrics:\\n\",\n \" m = report.deny_rate_metrics[TARGET_DB]\\n\",\n \" issues_df = pd.DataFrame([{\\n\",\n \" \\\"Deny Rate\\\": f\\\"{m.deny_rate:.1f}%\\\",\\n\",\n \" \\\"All-Allow %\\\": f\\\"{m.all_allow_pct:.1f}%\\\",\\n\",\n \" \\\"Issues\\\": \\\"; \\\".join(issues)\\n\",\n \" }])\\n\",\n \" display(issues_df)\\n\",\n \" else:\\n\",\n \" print(f\\\"[ERROR] No roles found for {TARGET_DB}\\\")\\n\",\n \"\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"[SUMMARY] {len(valid_target_dbs)} database(s) selected: {valid_target_dbs}\\\")\\n\",\n \" print(\\\"[NEXT] Edit hints in Cell 6.2, then run Cell 6.3\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[INFO] No databases selected. Edit TARGET_DBS above and re-run.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 34,\n \"id\": \"f8416bc4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Applying shared hint to 1 database(s) ===\\n\",\n \"\\n\",\n \" ✓ formula_1: 13 tables, 94 columns\\n\",\n \"\\n\",\n \"[NEXT] Run Cell 6.3 to regenerate all 1 database(s)\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.2 Write Regeneration Hints (Human-in-the-Loop)\\n\",\n \"# Write ONE shared hint for all selected databases (they have similar issues)\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EDIT HERE: Shared problem description for all selected DBs\\n\",\n \"# Supports {db}, {num_tables}, {num_cols} placeholders\\n\",\n \"# ============================================================\\n\",\n \"# PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"# [PROBLEM]\\n\",\n \"# Low deny rate - almost all queries are allowed by all roles (roles too permissive)\\n\",\n \"# Low diversity - all roles have similar access patterns (coverage_std too low)\\n\",\n \"\\n\",\n \"\\n\",\n \"# [ROOT CAUSE]\\n\",\n \"# - Most roles can access most/all columns, so very few queries get denied\\n\",\n \"# - This defeats the purpose of RBAC - no meaningful access control\\n\",\n \"\\n\",\n \"# [DIRECTION]\\n\",\n \"# - Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"# - Create restrictive roles that only access specific subsets of data\\n\",\n \"# - Each role should have a clear, limited scope\\n\",\n \"# - Some roles should be restricted to fewer tables\\n\",\n \"# - Do not frequently giving non-admin roles \\\"*\\\" (all columns) access\\n\",\n \"# - Create clear functional boundaries between roles\\n\",\n \"# \\\"\\\"\\\"\\n\",\n \"\\n\",\n \"# All-denied cases - even SystemManager role cannot access some columns\\n\",\n \"# - Ensure SystemManager role has full access to all tables and columns\\n\",\n \"\\n\",\n \"\\n\",\n \"PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"[PROBLEM]\\n\",\n \"Low semantic alignment - roles do not align well with schema\\n\",\n \"All-denied cases - even SystemManager role cannot access some columns\\n\",\n \"\\n\",\n \"[ROOT CAUSE]\\n\",\n \"- This defeats the purpose of RBAC - no meaningful access control\\n\",\n \"\\n\",\n \"[DIRECTION]\\n\",\n \"- Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"- Ensure SystemManager role has full access to all tables and columns\\n\",\n \"- Review other roles to ensure they align with specific functional areas\\n\",\n \"- Each role should have a clear, limited scope\\n\",\n \"- Create meaningful semantic role names and descriptions that reflect their access patterns\\n\",\n \"\\\"\\\"\\\"\\n\",\n \"\\n\",\n \"\\n\",\n \"# PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"# [PROBLEM]\\n\",\n \"# Low diversity - all roles have similar access patterns (coverage_std too low)\\n\",\n \"\\n\",\n \"# [ROOT CAUSE]\\n\",\n \"# - This defeats the purpose of RBAC - no meaningful access control differentiation\\n\",\n \"\\n\",\n \"# [DIRECTION]\\n\",\n \"# - Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"# - Create restrictive roles that only access specific subsets of data\\n\",\n \"# - Each role should have a clear, limited scope\\n\",\n \"# - Some roles should be restricted to fewer tables\\n\",\n \"# - Do not frequently giving non-admin roles \\\"*\\\" (all columns) access\\n\",\n \"# - Create clear functional boundaries between roles\\n\",\n \"# \\\"\\\"\\\"\\n\",\n \"\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# Auto-apply to all selected databases (no need to edit below)\\n\",\n \"# ============================================================\\n\",\n \"REGENERATION_HINTS = {}\\n\",\n \"\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"=== Applying shared hint to {len(valid_target_dbs)} database(s) ===\\\\n\\\")\\n\",\n \" for db in valid_target_dbs:\\n\",\n \" schema = target_schemas.get(db, {})\\n\",\n \" num_tables = len(schema)\\n\",\n \" num_cols = sum(len(t['columns']) for t in schema.values())\\n\",\n \" \\n\",\n \" # Fill placeholders\\n\",\n \" REGENERATION_HINTS[db] = PROBLEM_TO_FIX.format(\\n\",\n \" db=db, num_tables=num_tables, num_cols=num_cols\\n\",\n \" )\\n\",\n \" print(f\\\" ✓ {db}: {num_tables} tables, {num_cols} columns\\\")\\n\",\n \"\\n\",\n \" print(f\\\"\\\\n[NEXT] Run Cell 6.3 to regenerate all {len(valid_target_dbs)} database(s)\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[INFO] No databases selected. Run Cell 6.1 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 68,\n \"id\": \"91109a7c\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Regenerating roles for 1 database(s)...\\n\",\n \"============================================================\\n\",\n \"\\n\",\n \"[formula_1] Generating...\\n\",\n \" ✓ 5 roles (5 valid)\\n\",\n \"\\n\",\n \"============================================================\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Status</th>\\n\",\n \" <th>Roles</th>\\n\",\n \" <th>Valid</th>\\n\",\n \" <th>Tokens</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>success</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>3058</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Status Roles Valid Tokens\\n\",\n \"0 formula_1 success 5 5 3058\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"[OK] All databases regenerated. Run Cell 6.4 to preview.\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.3 Execute Regeneration with Hints (supports multiple databases)\\n\",\n \"\\n\",\n \"def regenerate_single_database(db_name: str, hint: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Regenerate roles for a single database with guidance hints.\\\"\\\"\\\"\\n\",\n \" db_path = bird_db_dir / db_name / f\\\"{db_name}.sqlite\\\"\\n\",\n \" \\n\",\n \" if not db_path.exists():\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": \\\"Database not found\\\"}\\n\",\n \" \\n\",\n \" try:\\n\",\n \" db_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" db_schema_text = format_schema_for_prompt(db_schema, db_name)\\n\",\n \" \\n\",\n \" enhanced_system_prompt = f\\\"{COLUMN_LEVEL_SYSTEM_PROMPT}\\\\n\\\\n[OPTIMIZATION GUIDANCE]\\\\n{hint}\\\"\\n\",\n \" user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=db_schema_text)\\n\",\n \" \\n\",\n \" response = oracle.query(\\n\",\n \" prompt_sys=enhanced_system_prompt,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3,\\n\",\n \" top_p=0.9,\\n\",\n \" max_completion_tokens=DEFAULT_MAX_TOKENS\\n\",\n \" )\\n\",\n \" \\n\",\n \" llm_response = response['answer']\\n\",\n \" usage = response.get('usage', {})\\n\",\n \" roles = parse_column_level_roles(llm_response)\\n\",\n \" \\n\",\n \" if not roles:\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"parse_error\\\", \\n\",\n \" \\\"error\\\": \\\"Failed to parse LLM response\\\", \\\"raw_response\\\": llm_response[:500]}\\n\",\n \" \\n\",\n \" validation = validate_roles_against_schema(roles, str(db_path))\\n\",\n \" assignments = [{\\\"role\\\": r[\\\"role\\\"], \\\"description\\\": r.get(\\\"description\\\", \\\"\\\"), \\n\",\n \" \\\"policy\\\": r.get(\\\"columns\\\", {})} for r in roles]\\n\",\n \" \\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"success\\\", \\\"roles_count\\\": len(assignments),\\n\",\n \" \\\"valid_roles\\\": validation['valid_roles'], \\\"all_valid\\\": validation['all_valid'],\\n\",\n \" \\\"assignments\\\": assignments, \\\"usage\\\": usage, \\\"raw_response\\\": llm_response}\\n\",\n \" except Exception as e:\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": str(e)}\\n\",\n \"\\n\",\n \"# Execute for all target databases\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"Regenerating roles for {len(valid_target_dbs)} database(s)...\\\")\\n\",\n \" print(\\\"=\\\"*60)\\n\",\n \"\\n\",\n \" regenerated_results = {} # Store all results\\n\",\n \" all_success = True\\n\",\n \"\\n\",\n \" for db_name in valid_target_dbs:\\n\",\n \" hint = REGENERATION_HINTS.get(db_name, PROBLEM_TO_FIX)\\n\",\n \" print(f\\\"\\\\n[{db_name}] Generating...\\\")\\n\",\n \" \\n\",\n \" result = regenerate_single_database(db_name, hint)\\n\",\n \" regenerated_results[db_name] = result\\n\",\n \" \\n\",\n \" if result[\\\"status\\\"] == \\\"success\\\":\\n\",\n \" print(f\\\" ✓ {result['roles_count']} roles ({result['valid_roles']} valid)\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" ✗ {result.get('error', result['status'])}\\\")\\n\",\n \" all_success = False\\n\",\n \"\\n\",\n \" # Summary table\\n\",\n \" print(\\\"\\\\n\\\" + \\\"=\\\"*60)\\n\",\n \" result_df = pd.DataFrame([{\\n\",\n \" \\\"Database\\\": r[\\\"database\\\"],\\n\",\n \" \\\"Status\\\": r[\\\"status\\\"],\\n\",\n \" \\\"Roles\\\": r.get(\\\"roles_count\\\", \\\"N/A\\\"),\\n\",\n \" \\\"Valid\\\": r.get(\\\"valid_roles\\\", \\\"N/A\\\"),\\n\",\n \" \\\"Tokens\\\": r.get(\\\"usage\\\", {}).get(\\\"total_tokens\\\", \\\"N/A\\\")\\n\",\n \" } for r in regenerated_results.values()])\\n\",\n \" display(result_df)\\n\",\n \"\\n\",\n \" if all_success:\\n\",\n \" print(\\\"\\\\n[OK] All databases regenerated. Run Cell 6.4 to preview.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"\\\\n[WARN] Some databases failed. Check errors above.\\\")\\n\",\n \"else:\\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"[INFO] No databases selected. Run Cell 6.1 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 69,\n \"id\": \"a2b0ba98\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"Database: formula_1\\n\",\n \"======================================================================\\n\",\n \"\\n\",\n \"=== NEW Roles (5) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Cols</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintenance and data management</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>94 (*)</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap times, pit stops, and qualifying results</td>\\n\",\n \" <td>8</td>\\n\",\n \" <td>61</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; lapTimes: [raceId, driverId, lap, position, time, milliseconds]; pitStops: [raceId, driverId, stop, lap, time, duration, milliseconds]; qualifying: [qualifyId, raceId, driverId, constructorId, number, position, q1, q2, q3]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>StandingsManager</td>\\n\",\n \" <td>Manages and monitors championship standings for drivers and constructors</td>\\n\",\n \" <td>7</td>\\n\",\n \" <td>42</td>\\n\",\n \" <td>constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, name, date]; constructorResults: [constructorResultsId, raceId, constructorId, points, status]; constructorStandings: [constructorStandingsId, raceId, constructorId, points, position, positionText, wins]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, points, position, positionText, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>PublicRelations</td>\\n\",\n \" <td>Accesses public information about circuits, drivers, constructors, and race schedules</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>31</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [year, url]; races: [raceId, year, round, circuitId, name, date, url]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal details including date of birth</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>32</td>\\n\",\n \" <td>drivers: [driverId, driverRef, number, code, forename, surname, dob, nationality]; races: [raceId, year, round, name, date]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionText, positionOrder, points, laps, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Role \\\\\\n\",\n \"0 SystemManager \\n\",\n \"1 RaceAnalyst \\n\",\n \"2 StandingsManager \\n\",\n \"3 PublicRelations \\n\",\n \"4 DriverManager \\n\",\n \"\\n\",\n \" Description \\\\\\n\",\n \"0 Full administrative access for system maintenance and data management \\n\",\n \"1 Analyzes race performance data including lap times, pit stops, and qualifying results \\n\",\n \"2 Manages and monitors championship standings for drivers and constructors \\n\",\n \"3 Accesses public information about circuits, drivers, constructors, and race schedules \\n\",\n \"4 Manages driver information and personal details including date of birth \\n\",\n \"\\n\",\n \" Tables Cols \\\\\\n\",\n \"0 13 94 (*) \\n\",\n \"1 8 61 \\n\",\n \"2 7 42 \\n\",\n \"3 6 31 \\n\",\n \"4 4 32 \\n\",\n \"\\n\",\n \" Policy \\n\",\n \"0 circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*] \\n\",\n \"1 circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; lapTimes: [raceId, driverId, lap, position, time, milliseconds]; pitStops: [raceId, driverId, stop, lap, time, duration, milliseconds]; qualifying: [qualifyId, raceId, driverId, constructorId, number, position, q1, q2, q3]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId] \\n\",\n \"2 constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, name, date]; constructorResults: [constructorResultsId, raceId, constructorId, points, status]; constructorStandings: [constructorStandingsId, raceId, constructorId, points, position, positionText, wins]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, points, position, positionText, statusId] \\n\",\n \"3 circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [year, url]; races: [raceId, year, round, circuitId, name, date, url]; status: [statusId, status] \\n\",\n \"4 drivers: [driverId, driverRef, number, code, forename, surname, dob, nationality]; races: [raceId, year, round, name, date]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionText, positionOrder, points, laps, statusId] \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Quality Comparison ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Metric</th>\\n\",\n \" <th>OLD</th>\\n\",\n \" <th>NEW</th>\\n\",\n \" <th>Threshold</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>Roles</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>NaN</td>\\n\",\n \" <td>NaN</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>Avg Coverage</td>\\n\",\n \" <td>65.5%</td>\\n\",\n \" <td>55.3%</td>\\n\",\n \" <td><85%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>Coverage Std</td>\\n\",\n \" <td>0.183</td>\\n\",\n \" <td>0.251</td>\\n\",\n \" <td>>0.065</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>Max Overlap</td>\\n\",\n \" <td>87.3%</td>\\n\",\n \" <td>57.4%</td>\\n\",\n \" <td><80%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Metric OLD NEW Threshold Status\\n\",\n \"0 Roles 5 5 NaN NaN\\n\",\n \"1 Avg Coverage 65.5% 55.3% <85% OK\\n\",\n \"2 Coverage Std 0.183 0.251 >0.065 OK\\n\",\n \"3 Max Overlap 87.3% 57.4% <80% OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[OK] formula_1: All quality checks passed\\n\",\n \"\\n\",\n \"======================================================================\\n\",\n \"[DECISION] Satisfied? Run Cell 6.5 to apply. Otherwise edit Cell 6.2 and re-run 6.3.\\n\",\n \"[TIP] Use Cell 6.4.1 to select specific roles from OLD/NEW\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.4 Preview Regenerated Roles (Compare OLD vs NEW + Quality Check)\\n\",\n \"# Supports multiple databases\\n\",\n \"import numpy as np\\n\",\n \"from src.role_evaluator.quality_metrics import RoleQualityEvaluator\\n\",\n \"\\n\",\n \"pd.set_option('display.max_colwidth', None)\\n\",\n \"pd.set_option('display.max_rows', None)\\n\",\n \"pd.set_option('display.width', None)\\n\",\n \"\\n\",\n \"if 'regenerated_results' in dir() and regenerated_results:\\n\",\n \" for db_name, regen_result in regenerated_results.items():\\n\",\n \" if regen_result[\\\"status\\\"] != \\\"success\\\":\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"[SKIP] {db_name}: {regen_result.get('error', 'failed')}\\\")\\n\",\n \" continue\\n\",\n \" \\n\",\n \" regenerated_assignments = regen_result[\\\"assignments\\\"]\\n\",\n \" target_schema = target_schemas.get(db_name)\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"Database: {db_name}\\\")\\n\",\n \" print(f\\\"{'='*70}\\\")\\n\",\n \" \\n\",\n \" # --- NEW Role Summary ---\\n\",\n \" new_summary = []\\n\",\n \" for r in regenerated_assignments:\\n\",\n \" policy = r.get('policy', {})\\n\",\n \" total_cols = count_policy_columns(policy, target_schema)\\n\",\n \" has_wc = has_wildcard_policy(policy)\\n\",\n \" cols_display = f\\\"{total_cols}\\\" + (\\\" (*)\\\" if has_wc else \\\"\\\")\\n\",\n \" policy_str = \\\"; \\\".join([\\n\",\n \" f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" \\n\",\n \" for t, c in policy.items()\\n\",\n \" ])\\n\",\n \" new_summary.append({\\n\",\n \" \\\"Role\\\": r['role'],\\n\",\n \" \\\"Description\\\": r.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Cols\\\": cols_display,\\n\",\n \" \\\"Policy\\\": policy_str\\n\",\n \" })\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n=== NEW Roles ({len(regenerated_assignments)}) ===\\\")\\n\",\n \" display(pd.DataFrame(new_summary))\\n\",\n \" \\n\",\n \" # --- Quality Metrics ---\\n\",\n \" if target_schema:\\n\",\n \" total_schema_cols = sum(len(t['columns']) for t in target_schema.values())\\n\",\n \" else:\\n\",\n \" total_schema_cols = 10\\n\",\n \" \\n\",\n \" # NEW metrics\\n\",\n \" new_coverages = [count_policy_columns(r.get('policy', {}), target_schema) / total_schema_cols \\n\",\n \" for r in regenerated_assignments]\\n\",\n \" new_avg_cov = np.mean(new_coverages)\\n\",\n \" new_cov_std = np.std(new_coverages)\\n\",\n \" \\n\",\n \" new_max_overlap = 0.0\\n\",\n \" non_admin_new = [r for r in regenerated_assignments if 'admin' not in r['role'].lower()]\\n\",\n \" for i, r1 in enumerate(non_admin_new):\\n\",\n \" set1 = RoleQualityEvaluator.policy_to_column_set(r1.get('policy', {}))\\n\",\n \" for r2 in non_admin_new[i+1:]:\\n\",\n \" set2 = RoleQualityEvaluator.policy_to_column_set(r2.get('policy', {}))\\n\",\n \" new_max_overlap = max(new_max_overlap, RoleQualityEvaluator.jaccard_similarity(set1, set2))\\n\",\n \" \\n\",\n \" # OLD metrics\\n\",\n \" old_roles = all_assignments.get('assignments', {}).get(db_name, [])\\n\",\n \" if old_roles:\\n\",\n \" old_coverages = [count_policy_columns(r.get('policy', {}), target_schema) / total_schema_cols \\n\",\n \" for r in old_roles]\\n\",\n \" old_avg_cov = np.mean(old_coverages)\\n\",\n \" old_cov_std = np.std(old_coverages)\\n\",\n \" \\n\",\n \" old_max_overlap = 0.0\\n\",\n \" non_admin_old = [r for r in old_roles if 'admin' not in r['role'].lower()]\\n\",\n \" for i, r1 in enumerate(non_admin_old):\\n\",\n \" set1 = RoleQualityEvaluator.policy_to_column_set(r1.get('policy', {}))\\n\",\n \" for r2 in non_admin_old[i+1:]:\\n\",\n \" set2 = RoleQualityEvaluator.policy_to_column_set(r2.get('policy', {}))\\n\",\n \" old_max_overlap = max(old_max_overlap, RoleQualityEvaluator.jaccard_similarity(set1, set2))\\n\",\n \" else:\\n\",\n \" old_avg_cov, old_cov_std, old_max_overlap = 0, 0, 0\\n\",\n \" \\n\",\n \" # Comparison table (using QUALITY_THRESHOLDS from Cell 5.1)\\n\",\n \" def status(metric, val):\\n\",\n \" if metric == \\\"Coverage\\\": \\n\",\n \" return \\\"WARN\\\" if val > QUALITY_THRESHOLDS.get(\\\"coverage_max\\\", 0.90) else \\\"OK\\\"\\n\",\n \" if metric == \\\"CovStd\\\": \\n\",\n \" return \\\"WARN\\\" if val < QUALITY_THRESHOLDS.get(\\\"coverage_std_min\\\", 0.08) else \\\"OK\\\"\\n\",\n \" if metric == \\\"MaxOverlap\\\": \\n\",\n \" return \\\"WARN\\\" if val > QUALITY_THRESHOLDS.get(\\\"max_overlap\\\", 0.80) else \\\"OK\\\"\\n\",\n \" return \\\"OK\\\"\\n\",\n \" \\n\",\n \" quality_df = pd.DataFrame([\\n\",\n \" {\\\"Metric\\\": \\\"Roles\\\", \\\"OLD\\\": len(old_roles), \\\"NEW\\\": len(regenerated_assignments)},\\n\",\n \" {\\\"Metric\\\": \\\"Avg Coverage\\\", \\\"OLD\\\": f\\\"{old_avg_cov:.1%}\\\", \\\"NEW\\\": f\\\"{new_avg_cov:.1%}\\\", \\n\",\n \" \\\"Threshold\\\": f\\\"<{QUALITY_THRESHOLDS.get('coverage_max', 0.90):.0%}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"Coverage\\\", new_avg_cov)},\\n\",\n \" {\\\"Metric\\\": \\\"Coverage Std\\\", \\\"OLD\\\": f\\\"{old_cov_std:.3f}\\\", \\\"NEW\\\": f\\\"{new_cov_std:.3f}\\\",\\n\",\n \" \\\"Threshold\\\": f\\\">{QUALITY_THRESHOLDS.get('coverage_std_min', 0.08)}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"CovStd\\\", new_cov_std)},\\n\",\n \" {\\\"Metric\\\": \\\"Max Overlap\\\", \\\"OLD\\\": f\\\"{old_max_overlap:.1%}\\\", \\\"NEW\\\": f\\\"{new_max_overlap:.1%}\\\",\\n\",\n \" \\\"Threshold\\\": f\\\"<{QUALITY_THRESHOLDS.get('max_overlap', 0.80):.0%}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"MaxOverlap\\\", new_max_overlap)},\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Quality Comparison ===\\\")\\n\",\n \" display(quality_df)\\n\",\n \" \\n\",\n \" warn_count = quality_df['Status'].eq('WARN').sum() if 'Status' in quality_df.columns else 0\\n\",\n \" if warn_count == 0:\\n\",\n \" print(f\\\"[OK] {db_name}: All quality checks passed\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\"[WARN] {db_name}: {warn_count} issue(s)\\\")\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(\\\"[DECISION] Satisfied? Run Cell 6.5 to apply. Otherwise edit Cell 6.2 and re-run 6.3.\\\")\\n\",\n \" print(\\\"[TIP] Use Cell 6.4.1 to select specific roles from OLD/NEW\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[WARN] No regenerated results. Run Cell 6.3 first.\\\")\\n\",\n \"\\n\",\n \"pd.reset_option('display.max_colwidth')\\n\",\n \"pd.reset_option('display.max_rows')\\n\",\n \"pd.reset_option('display.width')\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 70,\n \"id\": \"f003a014\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Role Selection Summary ===\\n\",\n \"\\n\",\n \"============================================================\\n\",\n \"Database: formula_1\\n\",\n \" From OLD (0): (none)\\n\",\n \" From NEW (4): ['SystemManager', 'RaceAnalyst', 'PublicRelations', 'DriverManager']\\n\",\n \" Combined: 4 roles\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Source</th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintena...</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap t...</td>\\n\",\n \" <td>8</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country,...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>PublicRelations</td>\\n\",\n \" <td>Accesses public information about circuits, dr...</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country,...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal detail...</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>drivers: [driverId, driverRef, number, code, f...</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Source Role Description \\\\\\n\",\n \"0 NEW SystemManager Full administrative access for system maintena... \\n\",\n \"1 NEW RaceAnalyst Analyzes race performance data including lap t... \\n\",\n \"2 NEW PublicRelations Accesses public information about circuits, dr... \\n\",\n \"3 NEW DriverManager Manages driver information and personal detail... \\n\",\n \"\\n\",\n \" Tables Policy \\n\",\n \"0 13 circuits: [*]; constructors: [*]; drivers: [*]... \\n\",\n \"1 8 circuits: [circuitId, name, location, country,... \\n\",\n \"2 6 circuits: [circuitId, name, location, country,... \\n\",\n \"3 4 drivers: [driverId, driverRef, number, code, f... \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"============================================================\\n\",\n \"[NEXT] Run Cell 6.5 to apply combined role sets\\n\",\n \"[TIP] Edit ROLE_SELECTION above to customize, then re-run this cell\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.4.1 Select & Combine Roles from OLD and NEW\\n\",\n \"# Human expert picks best roles from both versions to create final role set\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EDIT HERE: Specify which roles to keep from each version\\n\",\n \"# Format: {\\\"db_name\\\": {\\\"old\\\": [role_names], \\\"new\\\": [role_names]}}\\n\",\n \"# Leave empty [] to skip a version, or use [\\\"*\\\"] to keep all from that version\\n\",\n \"# ============================================================\\n\",\n \"ROLE_SELECTION = {\\n\",\n \" \\\"formula_1\\\": {\\n\",\n \" \\\"old\\\": [], # e.g., [\\\"DataAnalyst\\\", \\\"Researcher\\\"]\\n\",\n \" \\\"new\\\": [\\\"SystemManager\\\", \\\"RaceAnalyst\\\", \\\"PublicRelations\\\", \\\"DriverManager\\\"], # keep all new roles\\n\",\n \" },\\n\",\n \" # \\\"financial\\\": {\\n\",\n \" # \\\"old\\\": [],\\n\",\n \" # \\\"new\\\": [\\\"*\\\"],\\n\",\n \" # },\\n\",\n \"}\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# Process selections and create combined role sets\\n\",\n \"# ============================================================\\n\",\n \"combined_results = {}\\n\",\n \"\\n\",\n \"if 'regenerated_results' in dir() and regenerated_results:\\n\",\n \" print(\\\"=== Role Selection Summary ===\\\\n\\\")\\n\",\n \" \\n\",\n \" for db_name in valid_target_dbs:\\n\",\n \" selection = ROLE_SELECTION.get(db_name, {\\\"old\\\": [], \\\"new\\\": [\\\"*\\\"]})\\n\",\n \" old_keep = selection.get(\\\"old\\\", [])\\n\",\n \" new_keep = selection.get(\\\"new\\\", [\\\"*\\\"])\\n\",\n \" \\n\",\n \" # Get OLD roles\\n\",\n \" old_roles = all_assignments.get('assignments', {}).get(db_name, [])\\n\",\n \" old_role_dict = {r['role']: r for r in old_roles}\\n\",\n \" \\n\",\n \" # Get NEW roles\\n\",\n \" regen_result = regenerated_results.get(db_name, {})\\n\",\n \" if regen_result.get(\\\"status\\\") != \\\"success\\\":\\n\",\n \" print(f\\\"[SKIP] {db_name}: regeneration failed\\\")\\n\",\n \" continue\\n\",\n \" new_roles = regen_result[\\\"assignments\\\"]\\n\",\n \" new_role_dict = {r['role']: r for r in new_roles}\\n\",\n \" \\n\",\n \" # Build combined role set\\n\",\n \" combined_roles = []\\n\",\n \" selected_from_old = []\\n\",\n \" selected_from_new = []\\n\",\n \" \\n\",\n \" # Select from OLD\\n\",\n \" if old_keep == [\\\"*\\\"]:\\n\",\n \" combined_roles.extend(old_roles)\\n\",\n \" selected_from_old = [r['role'] for r in old_roles]\\n\",\n \" else:\\n\",\n \" for role_name in old_keep:\\n\",\n \" if role_name in old_role_dict:\\n\",\n \" combined_roles.append(old_role_dict[role_name])\\n\",\n \" selected_from_old.append(role_name)\\n\",\n \" else:\\n\",\n \" print(f\\\" [WARN] {db_name}: OLD role '{role_name}' not found\\\")\\n\",\n \" \\n\",\n \" # Select from NEW\\n\",\n \" if new_keep == [\\\"*\\\"]:\\n\",\n \" combined_roles.extend(new_roles)\\n\",\n \" selected_from_new = [r['role'] for r in new_roles]\\n\",\n \" else:\\n\",\n \" for role_name in new_keep:\\n\",\n \" if role_name in new_role_dict:\\n\",\n \" combined_roles.append(new_role_dict[role_name])\\n\",\n \" selected_from_new.append(role_name)\\n\",\n \" else:\\n\",\n \" print(f\\\" [WARN] {db_name}: NEW role '{role_name}' not found\\\")\\n\",\n \" \\n\",\n \" combined_results[db_name] = combined_roles\\n\",\n \" \\n\",\n \" # Display summary\\n\",\n \" print(f\\\"{'='*60}\\\")\\n\",\n \" print(f\\\"Database: {db_name}\\\")\\n\",\n \" print(f\\\" From OLD ({len(selected_from_old)}): {selected_from_old if selected_from_old else '(none)'}\\\")\\n\",\n \" print(f\\\" From NEW ({len(selected_from_new)}): {selected_from_new if selected_from_new else '(none)'}\\\")\\n\",\n \" print(f\\\" Combined: {len(combined_roles)} roles\\\")\\n\",\n \" \\n\",\n \" # Show combined roles table\\n\",\n \" target_schema = target_schemas.get(db_name)\\n\",\n \" combined_summary = []\\n\",\n \" for r in combined_roles:\\n\",\n \" policy = r.get('policy', {})\\n\",\n \" source = \\\"OLD\\\" if r['role'] in old_role_dict and r['role'] not in [nr['role'] for nr in new_roles] else \\\"NEW\\\"\\n\",\n \" if r['role'] in old_role_dict and r['role'] in [nr['role'] for nr in new_roles]:\\n\",\n \" source = \\\"NEW\\\" # Prefer NEW if same name exists in both\\n\",\n \" combined_summary.append({\\n\",\n \" \\\"Source\\\": source,\\n\",\n \" \\\"Role\\\": r['role'],\\n\",\n \" \\\"Description\\\": r.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Policy\\\": \\\"; \\\".join([f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" for t, c in policy.items()])\\n\",\n \" })\\n\",\n \" \\n\",\n \" display(pd.DataFrame(combined_summary))\\n\",\n \" print()\\n\",\n \" \\n\",\n \" print(\\\"=\\\"*60)\\n\",\n \" print(f\\\"[NEXT] Run Cell 6.5 to apply combined role sets\\\")\\n\",\n \" print(\\\"[TIP] Edit ROLE_SELECTION above to customize, then re-run this cell\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[WARN] No regenerated results. Run Cell 6.3 first.\\\")\\n\",\n \" print(\\\"\\\\n[HINT] Available roles to select from:\\\")\\n\",\n \" print(\\\" OLD: roles from current assignments\\\")\\n\",\n \" print(\\\" NEW: roles from regeneration\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 71,\n \"id\": \"6cb24c7d\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[MODE] Applying COMBINED role sets (from Cell 6.4.1 selection)\\n\",\n \"\\n\",\n \"=== Changes Applied ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Old Roles</th>\\n\",\n \" <th>New Roles</th>\\n\",\n \" <th>Source</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>Combined (OLD+NEW)</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Old Roles New Roles Source\\n\",\n \"0 formula_1 5 4 Combined (OLD+NEW)\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"Saved to: bird_column_level_rbac_assignments_v2_20251230.json\\n\",\n \"\\n\",\n \"[OK] State cleared. Select new database(s) to continue.\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.5 Apply Changes (Save Combined Role Sets)\\n\",\n \"# Uses combined_results from Cell 6.4.1 (selected from OLD + NEW)\\n\",\n \"from datetime import datetime\\n\",\n \"\\n\",\n \"APPLY_CHANGES = True # Set to True to confirm\\n\",\n \"\\n\",\n \"if APPLY_CHANGES:\\n\",\n \" # Prefer combined_results (from selection), fallback to regenerated_results\\n\",\n \" use_combined = 'combined_results' in dir() and combined_results\\n\",\n \" use_regenerated = 'regenerated_results' in dir() and regenerated_results\\n\",\n \" \\n\",\n \" if use_combined:\\n\",\n \" print(\\\"[MODE] Applying COMBINED role sets (from Cell 6.4.1 selection)\\\\n\\\")\\n\",\n \" applied = []\\n\",\n \" \\n\",\n \" for db_name, combined_roles in combined_results.items():\\n\",\n \" if not combined_roles:\\n\",\n \" continue\\n\",\n \" \\n\",\n \" old_count = len(all_assignments.get('assignments', {}).get(db_name, []))\\n\",\n \" all_assignments['assignments'][db_name] = combined_roles\\n\",\n \" applied.append({\\n\",\n \" \\\"Database\\\": db_name,\\n\",\n \" \\\"Old Roles\\\": old_count,\\n\",\n \" \\\"New Roles\\\": len(combined_roles),\\n\",\n \" \\\"Source\\\": \\\"Combined (OLD+NEW)\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" if applied:\\n\",\n \" OUTPUT_DIR = project_root / 'outputs'\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = OUTPUT_DIR / f'bird_column_level_rbac_assignments_v2_{today}.json'\\n\",\n \" with open(output_path, 'w') as f:\\n\",\n \" json.dump(all_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \" \\n\",\n \" print(\\\"=== Changes Applied ===\\\")\\n\",\n \" display(pd.DataFrame(applied))\\n\",\n \" print(f\\\"\\\\nSaved to: {output_path.name}\\\")\\n\",\n \" \\n\",\n \" # Clear state\\n\",\n \" combined_results = {}\\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"\\\\n[OK] State cleared. Select new database(s) to continue.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No combined role sets to apply. Run Cell 6.4.1 first.\\\")\\n\",\n \" \\n\",\n \" elif use_regenerated:\\n\",\n \" print(\\\"[MODE] Applying ALL NEW roles (no selection made)\\\\n\\\")\\n\",\n \" applied = []\\n\",\n \" \\n\",\n \" for db_name, regen_result in regenerated_results.items():\\n\",\n \" if regen_result[\\\"status\\\"] != \\\"success\\\":\\n\",\n \" continue\\n\",\n \" \\n\",\n \" old_count = len(all_assignments.get('assignments', {}).get(db_name, []))\\n\",\n \" all_assignments['assignments'][db_name] = regen_result[\\\"assignments\\\"]\\n\",\n \" applied.append({\\n\",\n \" \\\"Database\\\": db_name,\\n\",\n \" \\\"Old Roles\\\": old_count,\\n\",\n \" \\\"New Roles\\\": len(regen_result[\\\"assignments\\\"]),\\n\",\n \" \\\"Source\\\": \\\"All NEW\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" if applied:\\n\",\n \" OUTPUT_DIR = project_root / 'outputs'\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = OUTPUT_DIR / f'bird_column_level_rbac_assignments_v2_{today}.json'\\n\",\n \" with open(output_path, 'w') as f:\\n\",\n \" json.dump(all_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \" \\n\",\n \" print(\\\"=== Changes Applied ===\\\")\\n\",\n \" display(pd.DataFrame(applied))\\n\",\n \" print(f\\\"\\\\nSaved to: {output_path.name}\\\")\\n\",\n \" \\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"\\\\n[OK] State cleared. Select new database(s) to continue.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No successful regenerations to apply.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No results to apply. Run Cell 6.3 and optionally Cell 6.4.1 first.\\\")\\n\",\n \"else:\\n\",\n \" has_combined = 'combined_results' in dir() and combined_results\\n\",\n \" has_regenerated = 'regenerated_results' in dir() and regenerated_results\\n\",\n \" \\n\",\n \" if has_combined:\\n\",\n \" print(f\\\"[INFO] {len(combined_results)} database(s) with COMBINED role sets ready\\\")\\n\",\n \" print(\\\"[TIP] These are your custom selections from OLD + NEW\\\")\\n\",\n \" elif has_regenerated:\\n\",\n \" success_count = sum(1 for r in regenerated_results.values() if r[\\\"status\\\"] == \\\"success\\\")\\n\",\n \" print(f\\\"[INFO] {success_count} database(s) with ALL NEW roles ready\\\")\\n\",\n \" print(\\\"[TIP] Run Cell 6.4.1 to select specific roles from OLD/NEW\\\")\\n\",\n \" \\n\",\n \" print(\\\"\\\\n[INFO] APPLY_CHANGES is False. Set to True and re-run to apply.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 72,\n \"id\": \"f1bd0bc2\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Regenerating RBAC Dataset ===\\n\",\n \"Role assignments: bird_column_level_rbac_assignments_v2_20251230.json\\n\",\n \"BIRD data: /home/feiy/Role-SQL-benchmark/data/Bird/dev_20251106/dev.json\\n\",\n \"Generating column-level RBAC dataset...\\n\",\n \" Roles per DB: None\\n\",\n \" Queries per role: all\\n\",\n \"Current ratio (allowed): 50.41%\\n\",\n \" Allowed: 3656, Denied: 3596\\n\",\n \"\\n\",\n \"Dataset saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_20251230.json\\n\",\n \"Metadata saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_20251230_metadata.json\\n\",\n \" Total entries: 7252\\n\",\n \" Allowed: 3656 (50.4%)\\n\",\n \" Denied: 3596 (49.6%)\\n\",\n \"\\n\",\n \"✓ Dataset saved to: column_level_rbac_dataset_bird_20251230.json\\n\",\n \"\\n\",\n \"=== Dataset Statistics ===\\n\",\n \" total_entries: 7252\\n\",\n \" allowed: 3656\\n\",\n \" denied: 3596\\n\",\n \" allowed_ratio: 0.5041367898510756\\n\",\n \"\\n\",\n \"Per-database distribution:\\n\",\n \" california_schools: allowed=118, denied=327\\n\",\n \" card_games: allowed=507, denied=448\\n\",\n \" codebase_community: allowed=578, denied=352\\n\",\n \" debit_card_specializing: allowed=174, denied=210\\n\",\n \" european_football_2: allowed=277, denied=368\\n\",\n \" financial: allowed=156, denied=374\\n\",\n \" formula_1: allowed=385, denied=311\\n\",\n \" student_club: allowed=396, denied=394\\n\",\n \" superhero: allowed=328, denied=317\\n\",\n \" thrombosis_prediction: allowed=432, denied=220\\n\",\n \" toxicology: allowed=305, denied=275\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.6 Regenerate RBAC Dataset with Updated Role Assignments\\n\",\n \"# Run this after Cell 6.5 to create the final dataset\\n\",\n \"\\n\",\n \"from datetime import datetime\\n\",\n \"import importlib\\n\",\n \"\\n\",\n \"# IMPORTANT: Reload ALL dependent modules to pick up CTE parsing fix\\n\",\n \"import src.processors.column_extractor\\n\",\n \"import src.processors.column_permission_checker\\n\",\n \"import src.processors.column_level_rbac_generator\\n\",\n \"\\n\",\n \"importlib.reload(src.processors.column_extractor)\\n\",\n \"importlib.reload(src.processors.column_permission_checker)\\n\",\n \"importlib.reload(src.processors.column_level_rbac_generator)\\n\",\n \"\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator\\n\",\n \"\\n\",\n \"# Find the updated role assignments file (v2 if exists, otherwise latest)\\n\",\n \"v2_files = sorted(glob.glob(str(project_root / 'outputs' / 'bird_column_level_rbac_assignments_v2_*.json')))\\n\",\n \"if v2_files:\\n\",\n \" UPDATED_ROLE_PATH = v2_files[-1]\\n\",\n \"else:\\n\",\n \" role_files = sorted(glob.glob(str(project_root / 'outputs' / 'column_level_role_assignments_bird_*.json')))\\n\",\n \" UPDATED_ROLE_PATH = role_files[-1] if role_files else None\\n\",\n \"\\n\",\n \"if UPDATED_ROLE_PATH:\\n\",\n \" print(f\\\"=== Regenerating RBAC Dataset ===\\\")\\n\",\n \" print(f\\\"Role assignments: {Path(UPDATED_ROLE_PATH).name}\\\")\\n\",\n \" print(f\\\"BIRD data: {BIRD_DATA_PATH}\\\")\\n\",\n \" \\n\",\n \" # Initialize generator with updated roles\\n\",\n \" generator = ColumnLevelRBACGenerator(\\n\",\n \" role_assignments_path=UPDATED_ROLE_PATH,\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH),\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" project_root=str(project_root),\\n\",\n \" seed=42\\n\",\n \" )\\n\",\n \" \\n\",\n \" # Generate dataset\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = project_root / 'outputs' / f'column_level_rbac_dataset_bird_{today}.json'\\n\",\n \" \\n\",\n \" full_stats = generator.generate_and_save(\\n\",\n \" output_path=str(output_path),\\n\",\n \" roles_per_db=None,\\n\",\n \" queries_per_role=None\\n\",\n \" )\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n✓ Dataset saved to: {output_path.name}\\\")\\n\",\n \" print(f\\\"\\\\n=== Dataset Statistics ===\\\")\\n\",\n \" for k, v in full_stats.items():\\n\",\n \" if k == 'by_database':\\n\",\n \" print(f\\\"\\\\nPer-database distribution:\\\")\\n\",\n \" for db_id, counts in sorted(v.items()):\\n\",\n \" print(f\\\" {db_id}: allowed={counts['allowed']}, denied={counts['denied']}\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[ERROR] No role assignments file found. Run Cell 6.5 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"a9345c97\",\n \"metadata\": {},\n \"source\": [\n \"## 7. Summary\\n\",\n \"\\n\",\n \"### Generated Files\\n\",\n \"- `outputs/column_level_role_assignments_bird_YYYYMMDD.json` - Role assignments with column-level policies\\n\",\n \"- `outputs/bird_column_level_rbac_assignments_v2_YYYYMMDD.json` - Updated role assignments after regeneration\\n\",\n \"- `outputs/column_level_rbac_dataset_bird_YYYYMMDD.json` - Column-Level RBAC dataset\\n\",\n \"\\n\",\n \"### Workflow\\n\",\n \"\\n\",\n \"| Section | Description |\\n\",\n \"|---------|-------------|\\n\",\n \"| **3** | Batch process all BIRD databases to generate initial roles |\\n\",\n \"| **4** | Generate RBAC dataset from role assignments |\\n\",\n \"| **5** | Quality assessment with configurable thresholds |\\n\",\n \"| **6.1** | Select problematic database(s) and view current state |\\n\",\n \"| **6.2** | Write regeneration hints with problem description |\\n\",\n \"| **6.3** | Execute LLM regeneration with hints |\\n\",\n \"| **6.4** | Preview OLD vs NEW roles with quality comparison |\\n\",\n \"| **6.4.1** | Select & combine best roles from OLD and NEW |\\n\",\n \"| **6.5** | Apply changes (save updated role assignments) |\\n\",\n \"| **6.6** | Regenerate RBAC dataset with updated roles |\\n\",\n \"\\n\",\n \"### Quality Assessment Module (Section 5)\\n\",\n \"\\n\",\n \"```python\\n\",\n \"from src.role_evaluator import RoleQualityEvaluator, RoleQualityVisualizer\\n\",\n \"\\n\",\n \"evaluator = RoleQualityEvaluator(\\n\",\n \" role_assignments_path=\\\"outputs/column_level_role_assignments_bird.json\\\",\\n\",\n \" dataset_path=\\\"outputs/column_level_rbac_dataset_bird.json\\\",\\n\",\n \" schema_dir=\\\"data/Bird/dev_20251106/dev_databases\\\",\\n\",\n \" embed_model=\\\"BAAI/bge-small-en-v1.5\\\" # or \\\"text-embedding-3-small\\\"\\n\",\n \")\\n\",\n \"\\n\",\n \"report = evaluator.evaluate_all()\\n\",\n \"visualizer = RoleQualityVisualizer(evaluator=evaluator, report=report)\\n\",\n \"visualizer.plot_quality_dashboard()\\n\",\n \"```\\n\",\n \"\\n\",\n \"### Quality Thresholds (Configurable)\\n\",\n \"\\n\",\n \"| Metric | Default | Description |\\n\",\n \"|--------|---------|-------------|\\n\",\n \"| `deny_rate_min` | 5% | Warn if deny rate below (roles too permissive) |\\n\",\n \"| `coverage_max` | 90% | Warn if avg coverage above (roles too broad) |\\n\",\n \"| `coverage_std_min` | 0.08 | Warn if coverage std below (low diversity) |\\n\",\n \"| `max_overlap` | 80% | Warn if max overlap above (redundant roles) |\\n\",\n \"\\n\",\n \"### External Modules\\n\",\n \"\\n\",\n \"1. **`src/role_evaluator/quality_metrics.py`** - Quality metrics computation\\n\",\n \" - `RoleQualityEvaluator`: Comprehensive evaluator\\n\",\n \" - `DenyRateMetrics`, `SemanticMetrics`, `OverlapMetrics`: Data classes\\n\",\n \" - `print_quality_report()`: Formatted report output\\n\",\n \"\\n\",\n \"2. **`src/role_evaluator/quality_visualizer.py`** - Visualization suite\\n\",\n \" - `plot_deny_rate_distribution()`: Permission distribution chart\\n\",\n \" - `plot_semantic_alignment()`: Semantic alignment scatter plot\\n\",\n \" - `plot_policy_overlap_bars()`: Policy overlap bar chart\\n\",\n \" - `plot_quality_dashboard()`: Comprehensive dashboard\\n\",\n \"\\n\",\n \"3. **`src/processors/column_level_rbac_generator.py`** - Dataset generation\\n\",\n \" - `ColumnLevelRBACGenerator`: Generate RBAC dataset from role assignments\"\n ]\n }\n ],\n \"metadata\": {\n \"kernelspec\": {\n \"display_name\": \"llm4db\",\n \"language\": \"python\",\n \"name\": \"python3\"\n },\n \"language_info\": {\n \"codemirror_mode\": {\n \"name\": \"ipython\",\n \"version\": 3\n },\n \"file_extension\": \".py\",\n \"mimetype\": \"text/x-python\",\n \"name\": \"python\",\n \"nbconvert_exporter\": \"python\",\n \"pygments_lexer\": \"ipython3\",\n \"version\": \"3.10.18\"\n }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 5\n}\n"}, "2a15af7efb2b5d335e0f468a13312eb587847e2a": {"t": 1785753623.9373732, "n": 427061, "h": "c4e7f848d2377d92", "c": "{\n \"cells\": [\n {\n \"cell_type\": \"markdown\",\n \"id\": \"c4bddd25\",\n \"metadata\": {},\n \"source\": [\n \"## 0. Import and Setup\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 1,\n \"id\": \"93292a4c\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Project root: /home/feiy/Role-SQL-benchmark\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Setup and imports\\n\",\n \"import sys\\n\",\n \"import json\\n\",\n \"import sqlite3\\n\",\n \"from pathlib import Path\\n\",\n \"from pprint import pprint\\n\",\n \"\\n\",\n \"# Add project root to path\\n\",\n \"project_root = Path.cwd().parent\\n\",\n \"sys.path.insert(0, str(project_root))\\n\",\n \"\\n\",\n \"print(f\\\"Project root: {project_root}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 2,\n \"id\": \"bce399e4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Available API keys:\\n\",\n \" OPENAI_API_KEY: ✓\\n\",\n \" DEEPSEEK_API_KEY: ✓\\n\",\n \" DEEPINFRA_API_KEY: ✓\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Load environment and initialize Oracle\\n\",\n \"from dotenv import load_dotenv\\n\",\n \"import os\\n\",\n \"\\n\",\n \"load_dotenv(project_root / '.env')\\n\",\n \"\\n\",\n \"# Check available API keys\\n\",\n \"api_keys = {\\n\",\n \" 'OPENAI_API_KEY': bool(os.getenv('OPENAI_API_KEY')),\\n\",\n \" 'DEEPSEEK_API_KEY': bool(os.getenv('DEEPSEEK_API_KEY')),\\n\",\n \" 'DEEPINFRA_API_KEY': bool(os.getenv('DEEPINFRA_API_KEY')),\\n\",\n \"}\\n\",\n \"print(\\\"Available API keys:\\\")\\n\",\n \"for k, v in api_keys.items():\\n\",\n \" print(f\\\" {k}: {'✓' if v else '✗'}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 3,\n \"id\": \"a4d69b7c\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Using model: deepseek-chat\\n\"\n ]\n },\n {\n \"name\": \"stderr\",\n \"output_type\": \"stream\",\n \"text\": [\n \"/home/feiy/anaconda3/envs/llm4db/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\\n\",\n \" from .autonotebook import tqdm as notebook_tqdm\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Initialize LLM Oracle\\n\",\n \"from src.llm_oracle import Oracle\\n\",\n \"\\n\",\n \"# Choose model based on available keys (prefer DeepSeek for cost efficiency)\\n\",\n \"if os.getenv('DEEPSEEK_API_KEY'):\\n\",\n \" MODEL = \\\"deepseek-chat\\\"\\n\",\n \" API_KEY = os.getenv('DEEPSEEK_API_KEY')\\n\",\n \"elif os.getenv('OPENAI_API_KEY'):\\n\",\n \" MODEL = \\\"gpt-4o-mini\\\"\\n\",\n \" API_KEY = os.getenv('OPENAI_API_KEY')\\n\",\n \"else:\\n\",\n \" raise ValueError(\\\"No API key available!\\\")\\n\",\n \"\\n\",\n \"oracle = Oracle(model=MODEL, apikey=API_KEY)\\n\",\n \"print(f\\\"Using model: {MODEL}\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8809cf55\",\n \"metadata\": {},\n \"source\": [\n \"## 1. Load Sample Database Schema\\n\",\n \"\\n\",\n \"We'll use a BIRD database with interesting column structure for testing.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 4,\n \"id\": \"e9dcc61c\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# Import shared policy utility functions\\n\",\n \"from src.role_evaluator.quality_metrics import RoleQualityEvaluator\\n\",\n \"\\n\",\n \"# Convenience aliases for the static methods\\n\",\n \"count_policy_columns = RoleQualityEvaluator.count_policy_columns\\n\",\n \"has_wildcard_policy = RoleQualityEvaluator.has_wildcard_policy\\n\",\n \"\\n\",\n \"def get_schema_from_sqlite(db_path: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Extract detailed schema from SQLite database.\\\"\\\"\\\"\\n\",\n \" conn = sqlite3.connect(db_path)\\n\",\n \" cursor = conn.cursor()\\n\",\n \" \\n\",\n \" # Get all tables\\n\",\n \" cursor.execute(\\\"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\\\")\\n\",\n \" tables = [row[0] for row in cursor.fetchall()]\\n\",\n \" \\n\",\n \" schema = {}\\n\",\n \" for table in tables:\\n\",\n \" cursor.execute(f\\\"PRAGMA table_info(`{table}`)\\\")\\n\",\n \" columns = cursor.fetchall()\\n\",\n \" \\n\",\n \" cursor.execute(f\\\"PRAGMA foreign_key_list(`{table}`)\\\")\\n\",\n \" fks = {row[3]: f\\\"{row[2]}.{row[4]}\\\" for row in cursor.fetchall()}\\n\",\n \" \\n\",\n \" schema[table] = {\\n\",\n \" 'columns': [\\n\",\n \" {\\n\",\n \" 'name': col[1],\\n\",\n \" 'type': col[2] or 'TEXT',\\n\",\n \" 'pk': col[5] == 1,\\n\",\n \" 'not_null': col[3] == 1,\\n\",\n \" 'fk': fks.get(col[1])\\n\",\n \" }\\n\",\n \" for col in columns\\n\",\n \" ]\\n\",\n \" }\\n\",\n \" \\n\",\n \" conn.close()\\n\",\n \" return schema\\n\",\n \"\\n\",\n \"def format_schema_for_prompt(schema: dict, db_name: str) -> str:\\n\",\n \" \\\"\\\"\\\"Format schema as text for LLM prompt.\\\"\\\"\\\"\\n\",\n \" lines = [f\\\"Database: {db_name}\\\\n\\\"]\\n\",\n \" \\n\",\n \" for table, info in schema.items():\\n\",\n \" lines.append(f\\\"Table: {table}\\\")\\n\",\n \" lines.append(\\\"Columns:\\\")\\n\",\n \" for col in info['columns']:\\n\",\n \" constraints = []\\n\",\n \" if col['pk']:\\n\",\n \" constraints.append(\\\"PRIMARY KEY\\\")\\n\",\n \" if col['not_null'] and not col['pk']:\\n\",\n \" constraints.append(\\\"NOT NULL\\\")\\n\",\n \" if col['fk']:\\n\",\n \" constraints.append(f\\\"FK -> {col['fk']}\\\")\\n\",\n \" \\n\",\n \" constraint_str = f\\\" [{', '.join(constraints)}]\\\" if constraints else \\\"\\\"\\n\",\n \" lines.append(f\\\" - {col['name']} ({col['type']}){constraint_str}\\\")\\n\",\n \" lines.append(\\\"\\\")\\n\",\n \" \\n\",\n \" return \\\"\\\\n\\\".join(lines)\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 5,\n \"id\": \"705d64b1\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Available BIRD databases (11):\\n\",\n \" - california_schools\\n\",\n \" - card_games\\n\",\n \" - codebase_community\\n\",\n \" - debit_card_specializing\\n\",\n \" - european_football_2\\n\",\n \" - financial\\n\",\n \" - formula_1\\n\",\n \" - student_club\\n\",\n \" - superhero\\n\",\n \" - thrombosis_prediction\\n\",\n \" ...\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Load a sample BIRD database - use the latest 2025 version\\n\",\n \"bird_db_dir = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases'\\n\",\n \"\\n\",\n \"# List available databases\\n\",\n \"available_dbs = [d.name for d in bird_db_dir.iterdir() if d.is_dir()]\\n\",\n \"print(f\\\"Available BIRD databases ({len(available_dbs)}):\\\")\\n\",\n \"for db in sorted(available_dbs)[:10]:\\n\",\n \" print(f\\\" - {db}\\\")\\n\",\n \"print(\\\" ...\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 6,\n \"id\": \"f9e1a86a\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Loading schema from: /home/feiy/Role-SQL-benchmark/data/Bird/dev_20251106/dev_databases/california_schools/california_schools.sqlite\\n\",\n \"\\n\",\n \"Tables: ['frpm', 'satscores', 'schools']\\n\",\n \"\\n\",\n \"frpm: 29 columns\\n\",\n \" - CDSCode (TEXT)\\n\",\n \" - Academic Year (TEXT)\\n\",\n \" - County Code (TEXT)\\n\",\n \" - District Code (INTEGER)\\n\",\n \" - School Code (TEXT)\\n\",\n \" ... and 24 more\\n\",\n \"\\n\",\n \"satscores: 11 columns\\n\",\n \" - cds (TEXT)\\n\",\n \" - rtype (TEXT)\\n\",\n \" - sname (TEXT)\\n\",\n \" - dname (TEXT)\\n\",\n \" - cname (TEXT)\\n\",\n \" ... and 6 more\\n\",\n \"\\n\",\n \"schools: 49 columns\\n\",\n \" - CDSCode (TEXT)\\n\",\n \" - NCESDist (TEXT)\\n\",\n \" - NCESSchool (TEXT)\\n\",\n \" - StatusType (TEXT)\\n\",\n \" - County (TEXT)\\n\",\n \" ... and 44 more\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Select a database with moderate complexity\\n\",\n \"# 'california_schools' has tables with sensitive-looking columns\\n\",\n \"TEST_DB = \\\"california_schools\\\"\\n\",\n \"db_path = bird_db_dir / TEST_DB / f\\\"{TEST_DB}.sqlite\\\"\\n\",\n \"\\n\",\n \"print(f\\\"Loading schema from: {db_path}\\\")\\n\",\n \"schema = get_schema_from_sqlite(str(db_path))\\n\",\n \"\\n\",\n \"print(f\\\"\\\\nTables: {list(schema.keys())}\\\")\\n\",\n \"for table, info in schema.items():\\n\",\n \" print(f\\\"\\\\n{table}: {len(info['columns'])} columns\\\")\\n\",\n \" for col in info['columns'][:5]:\\n\",\n \" print(f\\\" - {col['name']} ({col['type']})\\\")\\n\",\n \" if len(info['columns']) > 5:\\n\",\n \" print(f\\\" ... and {len(info['columns']) - 5} more\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 7,\n \"id\": \"e5fb0114\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Database: california_schools\\n\",\n \"\\n\",\n \"Table: frpm\\n\",\n \"Columns:\\n\",\n \" - CDSCode (TEXT) [PRIMARY KEY, FK -> schools.CDSCode]\\n\",\n \" - Academic Year (TEXT)\\n\",\n \" - County Code (TEXT)\\n\",\n \" - District Code (INTEGER)\\n\",\n \" - School Code (TEXT)\\n\",\n \" - County Name (TEXT)\\n\",\n \" - District Name (TEXT)\\n\",\n \" - School Name (TEXT)\\n\",\n \" - District Type (TEXT)\\n\",\n \" - School Type (TEXT)\\n\",\n \" - Educational Option Type (TEXT)\\n\",\n \" - NSLP Provision Status (TEXT)\\n\",\n \" - Charter School (Y/N) (INTEGER)\\n\",\n \" - Charter School Number (TEXT)\\n\",\n \" - Charter Funding Type (TEXT)\\n\",\n \" - IRC (INTEGER)\\n\",\n \" - Low Grade (TEXT)\\n\",\n \" - High Grade (TEXT)\\n\",\n \" - Enrollment (K-12) (REAL)\\n\",\n \" - Free Meal Count (K-12) (REAL)\\n\",\n \" - Percent (%) Eligible Free (K-12) (REAL)\\n\",\n \" - FRPM Count (K-12) (REAL)\\n\",\n \" - Percent (%) Eligible FRPM (K-12) (REAL)\\n\",\n \" - Enrollment (Ages 5-17) (REAL)\\n\",\n \" - Free Meal Count (Ages 5-17) (REAL)\\n\",\n \" - Percent (%) Eligible Free (Ages 5-17) (REAL)\\n\",\n \" - FRPM Count (Ages 5-17) (REAL)\\n\",\n \" - Percent (%) Eligible FRPM (Ages 5-17) (REAL)\\n\",\n \" - 2013-14 CALPADS Fall 1 Certification Status (INTEGER)\\n\",\n \"\\n\",\n \"Table: satscores\\n\",\n \"Columns:\\n\",\n \" - cds (TEXT) [PRIMARY KEY, FK -> schools.CDSCode]\\n\",\n \" - rtype (TEXT) [NOT NULL]\\n\",\n \" - sname (TEXT)\\n\",\n \" - dname (TEXT)\\n\",\n \" - cname (TEXT)\\n\",\n \" - enroll12 (INTEGER) [NOT NULL]\\n\",\n \" - NumTstTakr (INTEGER) [NOT NULL]\\n\",\n \" - AvgScrRead (INTEGER)\\n\",\n \" - AvgScrMath (INTEGER)\\n\",\n \" - AvgScrWrite (INTEGER)\\n\",\n \" - NumGE1500 (INTEGER)\\n\",\n \"\\n\",\n \"Table: schools\\n\",\n \"Columns:\\n\",\n \" - CDSCode (TEXT) [PRIMARY KEY]\\n\",\n \" - NCESDist (TEXT)\\n\",\n \" - NCESSchool (TEXT)\\n\",\n \" - StatusType (TEXT) [NOT NULL]\\n\",\n \" - County (TEXT) [NOT NULL]\\n\",\n \" - District (TEXT) [NOT NULL]\\n\",\n \" - School (TEXT)\\n\",\n \" - Street (TEXT)\\n\",\n \" - StreetAbr (TEXT)\\n\",\n \" - City (TEXT)\\n\",\n \" - Zip (TEXT)\\n\",\n \" - State (TEXT)\\n\",\n \" - MailStreet (TEXT)\\n\",\n \" - MailStrAbr (TEXT)\\n\",\n \" - MailCity (TEXT)\\n\",\n \" - MailZip (TEXT)\\n\",\n \" - MailState (TEXT)\\n\",\n \" - Phone (TEXT)\\n\",\n \" - Ext (TEXT)\\n\",\n \" - Website (TEXT)\\n\",\n \" - OpenDate (DATE)\\n\",\n \" - ClosedDate (DATE)\\n\",\n \" - Charter (INTEGER)\\n\",\n \" - CharterNum (TEXT)\\n\",\n \" - FundingType (TEXT)\\n\",\n \" - DOC (TEXT) [NOT NULL]\\n\",\n \" - DOCType (TEXT) [NOT NULL]\\n\",\n \" - SOC (TEXT)\\n\",\n \" - SOCType (TEXT)\\n\",\n \" - EdOpsCode (TEXT)\\n\",\n \" \\n\",\n \"\\n\",\n \"... (total 2401 characters)\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Format schema for prompt\\n\",\n \"schema_text = format_schema_for_prompt(schema, TEST_DB)\\n\",\n \"print(schema_text[:2000])\\n\",\n \"print(f\\\"\\\\n... (total {len(schema_text)} characters)\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8e904509\",\n \"metadata\": {},\n \"source\": [\n \"## 2. Load Prompts from External Modules\\n\",\n \"\\n\",\n \"Prompts are now defined in `configs/column_level_prompts.py`.\\n\",\n \"- **JSON format** is used (more structured, requires max_tokens=4000)\\n\",\n \"- Parser auto-detects format and handles both JSON and Plain Text\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 8,\n \"id\": \"1a425645\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"✓ Loaded from external modules:\\n\",\n \" - COLUMN_LEVEL_SYSTEM_PROMPT (2734 chars)\\n\",\n \" - COLUMN_LEVEL_USER_PROMPT_TEMPLATE\\n\",\n \" - DEFAULT_MAX_TOKENS = 4000\\n\",\n \" - parse_column_level_roles (auto-detects JSON/Text)\\n\",\n \" - validate_roles_against_schema\\n\",\n \"\\n\",\n \"--- System Prompt Preview ---\\n\",\n \"You are a database security expert specializing in fine-grained Role-Based Access Control (RBAC).\\n\",\n \"Your job is to read a relational database schema and propose a COHERENT set of roles with COLUMN-LEVEL permissions.\\n\",\n \"\\n\",\n \"Key goals:\\n\",\n \"- Minimize role count while covering real-world workflows (decide number of roles according to real world scenario).\\n\",\n \"- Each role must be clearly related to others (same scenario, complementary duties).\\n\",\n \"- Enforce least-privilege at the COLUMN level (specify which columns eac\\n\",\n \"...\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Reload modules to get latest changes\\n\",\n \"import importlib\\n\",\n \"import configs.column_level_prompts\\n\",\n \"import src.role_parser.column_level_parser\\n\",\n \"importlib.reload(configs.column_level_prompts)\\n\",\n \"importlib.reload(src.role_parser.column_level_parser)\\n\",\n \"\\n\",\n \"# Import prompts and parser from external modules\\n\",\n \"from configs.column_level_prompts import (\\n\",\n \" COLUMN_LEVEL_SYSTEM_PROMPT, \\n\",\n \" COLUMN_LEVEL_USER_PROMPT_TEMPLATE,\\n\",\n \" DEFAULT_MAX_TOKENS\\n\",\n \")\\n\",\n \"from src.role_parser import parse_column_level_roles, validate_roles_against_schema\\n\",\n \"\\n\",\n \"print(\\\"✓ Loaded from external modules:\\\")\\n\",\n \"print(f\\\" - COLUMN_LEVEL_SYSTEM_PROMPT ({len(COLUMN_LEVEL_SYSTEM_PROMPT)} chars)\\\")\\n\",\n \"print(f\\\" - COLUMN_LEVEL_USER_PROMPT_TEMPLATE\\\")\\n\",\n \"print(f\\\" - DEFAULT_MAX_TOKENS = {DEFAULT_MAX_TOKENS}\\\")\\n\",\n \"print(f\\\" - parse_column_level_roles (auto-detects JSON/Text)\\\")\\n\",\n \"print(f\\\" - validate_roles_against_schema\\\")\\n\",\n \"\\n\",\n \"print(\\\"\\\\n--- System Prompt Preview ---\\\")\\n\",\n \"print(COLUMN_LEVEL_SYSTEM_PROMPT[:500])\\n\",\n \"print(\\\"...\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"0acacb6e\",\n \"metadata\": {},\n \"source\": [\n \"## [Archived] Single-Sample Experiments\\n\",\n \"\\n\",\n \"以下代码块包含单样本测试代码,用于调试 prompt 和 parser。\\n\",\n \"**已注释,正式运行请跳过此节,直接执行第8节批量处理。**\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 9,\n \"id\": \"e9700ae1\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"⏭️ 此cell已注释,跳过单样本实验,请直接运行第8节批量处理\\n\"\n ]\n }\n ],\n \"source\": [\n \"# ============================================================\\n\",\n \"# [ARCHIVED] 单样本实验代码 - 已注释,跳过此cell\\n\",\n \"# ============================================================\\n\",\n \"# 以下代码用于单个数据库 (california_schools) 的测试和调试\\n\",\n \"# 正式运行请直接跳到第8节批量处理\\n\",\n \"\\n\",\n \"'''\\n\",\n \"# --- 3. Generate Column-Level Roles (Single DB) ---\\n\",\n \"print(\\\"Generating column-level roles...\\\")\\n\",\n \"user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=schema_text)\\n\",\n \"response = oracle.query(\\n\",\n \" prompt_sys=COLUMN_LEVEL_SYSTEM_PROMPT,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3, top_p=0.9,\\n\",\n \" max_completion_tokens=DEFAULT_MAX_TOKENS\\n\",\n \")\\n\",\n \"llm_response = response['answer']\\n\",\n \"print(f\\\"Response length: {len(llm_response)} chars\\\")\\n\",\n \"\\n\",\n \"# --- 4. Parse and Validate ---\\n\",\n \"from src.role_parser.column_level_parser import parse_column_level_roles\\n\",\n \"parsed_roles = parse_column_level_roles(llm_response)\\n\",\n \"print(f\\\"Parsed {len(parsed_roles)} roles\\\")\\n\",\n \"\\n\",\n \"validation = validate_roles_against_schema(parsed_roles, str(db_path))\\n\",\n \"print(f\\\"Valid: {validation['valid_roles']}/{validation['total_roles']}\\\")\\n\",\n \"\\n\",\n \"# --- 5. Show Results ---\\n\",\n \"for role in parsed_roles:\\n\",\n \" print(f\\\" - {role['role']}: {len(role.get('columns', {}))} tables\\\")\\n\",\n \"\\n\",\n \"# --- 6. Compare with Table-Level ---\\n\",\n \"from configs.prompts import SYSTEM_PROMPT as TABLE_LEVEL_PROMPT, USER_PROMPT_TEMPLATE\\n\",\n \"table_level_response = oracle.query(\\n\",\n \" prompt_sys=TABLE_LEVEL_PROMPT,\\n\",\n \" prompt_user=USER_PROMPT_TEMPLATE.format(schema_content=schema_text),\\n\",\n \" temp=0.3, top_p=0.9\\n\",\n \")\\n\",\n \"print(\\\"Table-Level Response:\\\", table_level_response['answer'][:500])\\n\",\n \"\\n\",\n \"# --- 7. Save Single DB Result ---\\n\",\n \"def convert_to_assignment_format(parsed_roles, db_name):\\n\",\n \" return {\\\"assignments\\\": {db_name: [\\n\",\n \" {\\\"role\\\": r[\\\"role\\\"], \\\"description\\\": r.get(\\\"description\\\", \\\"\\\"), \\\"policy\\\": r.get(\\\"columns\\\", {})}\\n\",\n \" for r in parsed_roles\\n\",\n \" ]}}\\n\",\n \"\\n\",\n \"column_level_assignments = convert_to_assignment_format(parsed_roles, TEST_DB)\\n\",\n \"output_file = project_root / 'outputs' / 'column_level_role_assignments.json'\\n\",\n \"with open(output_file, 'w') as f:\\n\",\n \" json.dump(column_level_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \"print(f\\\"Saved to: {output_file}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ 此cell已注释,跳过单样本实验,请直接运行第8节批量处理\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"8bc8cb95\",\n \"metadata\": {},\n \"source\": [\n \"## 3. Batch Process All BIRD Databases\\n\",\n \"\\n\",\n \"Generate column-level role assignments for all BIRD databases with parallel processing.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 10,\n \"id\": \"fe3f2e81\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Configuration:\\n\",\n \" Workers: 40\\n\",\n \" Databases: 11\\n\",\n \" Max tokens: 4000\\n\",\n \" Model: deepseek-chat\\n\",\n \"\\n\",\n \"Databases to process:\\n\",\n \" - superhero\\n\",\n \" - thrombosis_prediction\\n\",\n \" - student_club\\n\",\n \" - toxicology\\n\",\n \" - california_schools\\n\",\n \" - debit_card_specializing\\n\",\n \" - card_games\\n\",\n \" - codebase_community\\n\",\n \" - formula_1\\n\",\n \" - european_football_2\\n\",\n \" - financial\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Batch processing configuration\\n\",\n \"from concurrent.futures import ThreadPoolExecutor, as_completed\\n\",\n \"from tqdm.notebook import tqdm\\n\",\n \"\\n\",\n \"# Configuration\\n\",\n \"WORKERS = 40\\n\",\n \"SAMPLES = 100 # 0 for all databases\\n\",\n \"MAX_TOKENS = DEFAULT_MAX_TOKENS\\n\",\n \"\\n\",\n \"# Get all BIRD databases\\n\",\n \"all_dbs = [d.name for d in bird_db_dir.iterdir() if d.is_dir()]\\n\",\n \"if SAMPLES > 0:\\n\",\n \" all_dbs = all_dbs[:SAMPLES]\\n\",\n \"\\n\",\n \"print(f\\\"Configuration:\\\")\\n\",\n \"print(f\\\" Workers: {WORKERS}\\\")\\n\",\n \"print(f\\\" Databases: {len(all_dbs)}\\\")\\n\",\n \"print(f\\\" Max tokens: {MAX_TOKENS}\\\")\\n\",\n \"print(f\\\" Model: {MODEL}\\\")\\n\",\n \"print(f\\\"\\\\nDatabases to process:\\\")\\n\",\n \"for db in all_dbs:\\n\",\n \" print(f\\\" - {db}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 11,\n \"id\": \"7b5edd94\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"✓ Helper functions defined\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Helper functions for batch processing\\n\",\n \"\\n\",\n \"def process_single_database(db_name: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Process a single database and return role assignments.\\\"\\\"\\\"\\n\",\n \" db_path = bird_db_dir / db_name / f\\\"{db_name}.sqlite\\\"\\n\",\n \" \\n\",\n \" if not db_path.exists():\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": f\\\"Database not found\\\"}\\n\",\n \" \\n\",\n \" try:\\n\",\n \" # Extract schema\\n\",\n \" db_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" db_schema_text = format_schema_for_prompt(db_schema, db_name)\\n\",\n \" \\n\",\n \" # Generate roles via LLM\\n\",\n \" user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=db_schema_text)\\n\",\n \" \\n\",\n \" response = oracle.query(\\n\",\n \" prompt_sys=COLUMN_LEVEL_SYSTEM_PROMPT,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3,\\n\",\n \" top_p=0.9,\\n\",\n \" max_completion_tokens=MAX_TOKENS\\n\",\n \" )\\n\",\n \" \\n\",\n \" llm_response = response['answer']\\n\",\n \" usage = response.get('usage', {})\\n\",\n \" \\n\",\n \" # Parse response\\n\",\n \" roles = parse_column_level_roles(llm_response)\\n\",\n \" \\n\",\n \" if not roles:\\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"parse_error\\\",\\n\",\n \" \\\"error\\\": \\\"Failed to parse LLM response\\\",\\n\",\n \" \\\"raw_response\\\": llm_response[:300]\\n\",\n \" }\\n\",\n \" \\n\",\n \" # Validate against schema\\n\",\n \" validation = validate_roles_against_schema(roles, str(db_path))\\n\",\n \" \\n\",\n \" # Convert to target format\\n\",\n \" assignments = []\\n\",\n \" for role in roles:\\n\",\n \" assignments.append({\\n\",\n \" \\\"role\\\": role[\\\"role\\\"],\\n\",\n \" \\\"description\\\": role.get(\\\"description\\\", \\\"\\\"),\\n\",\n \" \\\"policy\\\": role.get(\\\"columns\\\", {})\\n\",\n \" })\\n\",\n \" \\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"success\\\",\\n\",\n \" \\\"roles_count\\\": len(assignments),\\n\",\n \" \\\"valid_roles\\\": validation['valid_roles'],\\n\",\n \" \\\"all_valid\\\": validation['all_valid'],\\n\",\n \" \\\"assignments\\\": assignments,\\n\",\n \" \\\"usage\\\": usage\\n\",\n \" }\\n\",\n \" \\n\",\n \" except Exception as e:\\n\",\n \" return {\\n\",\n \" \\\"database\\\": db_name,\\n\",\n \" \\\"status\\\": \\\"error\\\",\\n\",\n \" \\\"error\\\": str(e)\\n\",\n \" }\\n\",\n \"\\n\",\n \"print(\\\"✓ Helper functions defined\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 12,\n \"id\": \"61e61795\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Starting batch processing with 40 workers...\\n\",\n \"============================================================\\n\",\n \"[1/11] ✓ toxicology: 4 roles (4 valid)\\n\",\n \"[2/11] ✓ debit_card_specializing: 4 roles (4 valid)\\n\",\n \"[3/11] ✓ thrombosis_prediction: 4 roles (4 valid)\\n\",\n \"[4/11] ✓ student_club: 5 roles (5 valid)\\n\",\n \"[5/11] ✓ superhero: 5 roles (5 valid)\\n\",\n \"[6/11] ✓ financial: 5 roles (5 valid)\\n\",\n \"[7/11] ✓ card_games: 5 roles (5 valid)\\n\",\n \"[8/11] ✓ codebase_community: 4 roles (4 valid)\\n\",\n \"[9/11] ✓ california_schools: 5 roles (5 valid)\\n\",\n \"[10/11] ✓ formula_1: 5 roles (5 valid)\\n\",\n \"[11/11] ✓ european_football_2: 5 roles (5 valid)\\n\",\n \"============================================================\\n\",\n \"Completed: 11/11 success, 0 errors\\n\",\n \"Total tokens: 25,679\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Execute batch processing with parallel workers\\n\",\n \"print(f\\\"Starting batch processing with {WORKERS} workers...\\\")\\n\",\n \"print(\\\"=\\\" * 60)\\n\",\n \"\\n\",\n \"results = {\\n\",\n \" \\\"metadata\\\": {\\n\",\n \" \\\"model\\\": MODEL,\\n\",\n \" \\\"max_tokens\\\": MAX_TOKENS,\\n\",\n \" \\\"total_databases\\\": len(all_dbs)\\n\",\n \" },\\n\",\n \" \\\"assignments\\\": {},\\n\",\n \" \\\"errors\\\": []\\n\",\n \"}\\n\",\n \"\\n\",\n \"success_count = 0\\n\",\n \"error_count = 0\\n\",\n \"total_tokens = {\\\"prompt\\\": 0, \\\"completion\\\": 0}\\n\",\n \"\\n\",\n \"with ThreadPoolExecutor(max_workers=WORKERS) as executor:\\n\",\n \" futures = {executor.submit(process_single_database, db): db for db in all_dbs}\\n\",\n \" \\n\",\n \" for i, future in enumerate(as_completed(futures), 1):\\n\",\n \" db_name = futures[future]\\n\",\n \" try:\\n\",\n \" result = future.result()\\n\",\n \" \\n\",\n \" if result[\\\"status\\\"] == \\\"success\\\":\\n\",\n \" results[\\\"assignments\\\"][result[\\\"database\\\"]] = result[\\\"assignments\\\"]\\n\",\n \" success_count += 1\\n\",\n \" \\n\",\n \" usage = result.get(\\\"usage\\\", {})\\n\",\n \" total_tokens[\\\"prompt\\\"] += usage.get(\\\"prompt_tokens\\\", 0)\\n\",\n \" total_tokens[\\\"completion\\\"] += usage.get(\\\"completion_tokens\\\", 0)\\n\",\n \" \\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✓ {db_name}: {result['roles_count']} roles ({result['valid_roles']} valid)\\\")\\n\",\n \" else:\\n\",\n \" results[\\\"errors\\\"].append(result)\\n\",\n \" error_count += 1\\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✗ {db_name}: {result.get('error', result['status'])}\\\")\\n\",\n \" \\n\",\n \" except Exception as e:\\n\",\n \" error_count += 1\\n\",\n \" results[\\\"errors\\\"].append({\\\"database\\\": db_name, \\\"error\\\": str(e)})\\n\",\n \" print(f\\\"[{i}/{len(all_dbs)}] ✗ {db_name}: {e}\\\")\\n\",\n \"\\n\",\n \"results[\\\"metadata\\\"][\\\"success_count\\\"] = success_count\\n\",\n \"results[\\\"metadata\\\"][\\\"error_count\\\"] = error_count\\n\",\n \"results[\\\"metadata\\\"][\\\"total_tokens\\\"] = total_tokens\\n\",\n \"\\n\",\n \"print(\\\"=\\\" * 60)\\n\",\n \"print(f\\\"Completed: {success_count}/{len(all_dbs)} success, {error_count} errors\\\")\\n\",\n \"print(f\\\"Total tokens: {total_tokens['prompt'] + total_tokens['completion']:,}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 13,\n \"id\": \"39d38aa3\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"✓ Results saved to: /home/feiy/Role-SQL-benchmark/outputs/column_level_role_assignments_bird.json\\n\",\n \"\\n\",\n \"File structure:\\n\",\n \" - metadata: model, tokens, counts\\n\",\n \" - assignments: 11 databases\\n\",\n \" - errors: 0 entries\\n\",\n \"\\n\",\n \"Sample (toxicology):\\n\",\n \"[\\n\",\n \" {\\n\",\n \" \\\"role\\\": \\\"MolecularResearcher\\\",\\n\",\n \" \\\"description\\\": \\\"Analyzes molecular structures and their components for research purposes\\\",\\n\",\n \" \\\"policy\\\": {\\n\",\n \" \\\"molecule\\\": [\\n\",\n \" \\\"molecule_id\\\",\\n\",\n \" \\\"label\\\"\\n\",\n \" ],\\n\",\n \" \\\"atom\\\": [\\n\",\n \" \\\"atom_id\\\",\\n\",\n \" \\\"molecule_id\\\",\\n\",\n \" \\\"element\\\"\\n\",\n \" ],\\n\",\n \" \\\"bond\\\": [\\n\",\n \" \\\"bond_id\\\",\\n\",\n \" \\\"molecule_id\\\",\\n\",\n \" \\\"bond_type\\\"\\n\",\n \" ],\\n\",\n \" \\\"connected\\\": [\\n\",\n \" \\\"atom_id\\\",\\n\",\n \" \\\"atom_id2\\\",\\n\",\n \" \\\"bond_id\\\"\\n\",\n \" ]\\n\",\n \" }\\n\",\n \" }\\n\",\n \"]\\n\"\n ]\n }\n ],\n \"source\": [\n \"# Save batch results to file\\n\",\n \"output_file = project_root / 'outputs' / 'column_level_role_assignments_bird.json'\\n\",\n \"\\n\",\n \"with open(output_file, 'w', encoding='utf-8') as f:\\n\",\n \" json.dump(results, f, indent=2, ensure_ascii=False)\\n\",\n \"\\n\",\n \"print(f\\\"✓ Results saved to: {output_file}\\\")\\n\",\n \"print(f\\\"\\\\nFile structure:\\\")\\n\",\n \"print(f\\\" - metadata: model, tokens, counts\\\")\\n\",\n \"print(f\\\" - assignments: {len(results['assignments'])} databases\\\")\\n\",\n \"print(f\\\" - errors: {len(results['errors'])} entries\\\")\\n\",\n \"\\n\",\n \"# Show sample\\n\",\n \"if results['assignments']:\\n\",\n \" sample_db = list(results['assignments'].keys())[0]\\n\",\n \" print(f\\\"\\\\nSample ({sample_db}):\\\")\\n\",\n \" print(json.dumps(results['assignments'][sample_db][:1], indent=2, ensure_ascii=False)[:500])\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"9595c46d\",\n \"metadata\": {},\n \"source\": [\n \"## 4. Generate Column-Level RBAC Dataset\\n\",\n \"\\n\",\n \"Based on the generated role policies, combined with BIRD dataset queries, generate the final RBAC training/evaluation dataset.\\n\",\n \"\\n\",\n \"Dataset format (consistent with table-level):\\n\",\n \"- `db_id`: Database name\\n\",\n \"- `instruction`: Full instruction with schema + evidence + role policy\\n\",\n \"- `role`: Role name\\n\",\n \"- `policy`: Column-level permission policy {table: [columns]}\\n\",\n \"- `input`: User question\\n\",\n \"- `output`: SQL or denial message\\n\",\n \"- `difficulty`: Query difficulty level\\n\",\n \"- `metadata`: Contains gold_sql, permission, query_columns, missing_columns, etc.\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 45,\n \"id\": \"405783fa\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Dataset Generation Config ===\\n\",\n \"Role assignments: /home/feiy/Role-SQL-benchmark/outputs/column_level_role_assignments_bird.json\\n\",\n \"BIRD data file: /home/feiy/Role-SQL-benchmark/data/Bird/dev_20251106/dev.json\\n\",\n \"BIRD database dir: /home/feiy/Role-SQL-benchmark/data/Bird/dev_20251106/dev_databases\\n\",\n \"\\n\",\n \"✓ All file paths are valid\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 4.1 Import generator and test dependencies\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator, format_policy_text\\n\",\n \"from src.processors.column_extractor import extract_query_columns\\n\",\n \"from src.processors.column_permission_checker import check_column_permission\\n\",\n \"import glob\\n\",\n \"\\n\",\n \"# Configure paths - use 2025 version (dev_20251106) for latest BIRD data\\n\",\n \"ROLE_ASSIGNMENTS_PATH = project_root / 'outputs' / 'column_level_role_assignments_bird.json'\\n\",\n \"BIRD_DATA_PATH = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev.json'\\n\",\n \"BIRD_DB_ROOT = project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases'\\n\",\n \"\\n\",\n \"print(\\\"=== Dataset Generation Config ===\\\")\\n\",\n \"print(f\\\"Role assignments: {ROLE_ASSIGNMENTS_PATH}\\\")\\n\",\n \"print(f\\\"BIRD data file: {BIRD_DATA_PATH}\\\")\\n\",\n \"print(f\\\"BIRD database dir: {BIRD_DB_ROOT}\\\")\\n\",\n \"\\n\",\n \"# Verify file existence\\n\",\n \"# assert ROLE_ASSIGNMENTS_PATH.exists(), f\\\"Role assignments file not found: {ROLE_ASSIGNMENTS_PATH}\\\"\\n\",\n \"assert BIRD_DATA_PATH.exists(), f\\\"BIRD data file not found: {BIRD_DATA_PATH}\\\"\\n\",\n \"assert BIRD_DB_ROOT.exists(), f\\\"BIRD database dir not found: {BIRD_DB_ROOT}\\\"\\n\",\n \"print(\\\"\\\\n✓ All file paths are valid\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"4b80dc65\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== SQL Column Extraction Test ===\\n\",\n \"Tables: {'song', 'singer'}\\n\",\n \"Columns: {'song': {'singer_id', 'title'}, 'singer': {'id', 'name', 'country', 'age'}}\\n\",\n \"Has *: False\\n\",\n \"\\n\",\n \"=== Permission Check Test (Allow) ===\\n\",\n \"Allowed: True\\n\",\n \"Reason: All required columns are permitted\\n\",\n \"\\n\",\n \"=== Permission Check Test (Deny) ===\\n\",\n \"Allowed: False\\n\",\n \"Reason: Missing column permissions: song: singer_id; singer: age, country, id\\n\",\n \"Missing: {'song': {'singer_id'}, 'singer': {'id', 'country', 'age'}}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.2 Test SQL column extraction and permission checking\\n\",\n \"# 已验证工作正常,archive 以简化 notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"test_sql = \\\"\\\"\\\"\\n\",\n \"SELECT singer.name, singer.age, song.title\\n\",\n \"FROM singer\\n\",\n \"JOIN song ON singer.id = song.singer_id\\n\",\n \"WHERE singer.country = 'USA'\\n\",\n \"\\\"\\\"\\\"\\n\",\n \"\\n\",\n \"print(\\\"=== SQL Column Extraction Test ===\\\")\\n\",\n \"info = extract_query_columns(test_sql)\\n\",\n \"print(f\\\"Tables: {info.tables}\\\")\\n\",\n \"print(f\\\"Columns: {info.columns}\\\")\\n\",\n \"print(f\\\"Has *: {info.has_star}\\\")\\n\",\n \"\\n\",\n \"# Test permission checking\\n\",\n \"policy_allow = {\\n\",\n \" 'singer': ['name', 'age', 'country', 'id'],\\n\",\n \" 'song': ['title', 'singer_id']\\n\",\n \"}\\n\",\n \"policy_deny = {\\n\",\n \" 'singer': ['name'], # Missing 'age', 'country', 'id'\\n\",\n \" 'song': ['title'] # Missing 'singer_id'\\n\",\n \"}\\n\",\n \"\\n\",\n \"print(\\\"\\\\n=== Permission Check Test (Allow) ===\\\")\\n\",\n \"result1 = check_column_permission(test_sql, policy_allow)\\n\",\n \"print(f\\\"Allowed: {result1.allowed}\\\")\\n\",\n \"print(f\\\"Reason: {result1.reason}\\\")\\n\",\n \"\\n\",\n \"print(\\\"\\\\n=== Permission Check Test (Deny) ===\\\")\\n\",\n \"result2 = check_column_permission(test_sql, policy_deny)\\n\",\n \"print(f\\\"Allowed: {result2.allowed}\\\")\\n\",\n \"print(f\\\"Reason: {result2.reason}\\\")\\n\",\n \"print(f\\\"Missing: {result2.missing_columns}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Column extraction/permission tests - 已验证正常\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 16,\n \"id\": \"0a018208\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Generator Initialized ===\\n\",\n \"Total BIRD queries: 1534\\n\",\n \"Databases with role assignments: 11\\n\",\n \"Databases with queries: 11\\n\",\n \"\\n\",\n \"Queries per database:\\n\",\n \" california_schools: 89\\n\",\n \" card_games: 191\\n\",\n \" codebase_community: 186\\n\",\n \" debit_card_specializing: 64\\n\",\n \" european_football_2: 129\\n\",\n \" financial: 106\\n\",\n \" formula_1: 174\\n\",\n \" student_club: 158\\n\",\n \" superhero: 129\\n\",\n \" thrombosis_prediction: 163\\n\",\n \" toxicology: 145\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 4.3 Initialize generator\\n\",\n \"generator = ColumnLevelRBACGenerator(\\n\",\n \" role_assignments_path=str(ROLE_ASSIGNMENTS_PATH),\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH),\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" project_root=str(project_root),\\n\",\n \" seed=42\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"=== Generator Initialized ===\\\")\\n\",\n \"print(f\\\"Total BIRD queries: {len(generator.bird_data)}\\\")\\n\",\n \"print(f\\\"Databases with role assignments: {len(generator.role_assignments.get('assignments', {}))}\\\")\\n\",\n \"print(f\\\"Databases with queries: {len(generator.queries_by_db)}\\\")\\n\",\n \"\\n\",\n \"# Show query counts per database\\n\",\n \"print(\\\"\\\\nQueries per database:\\\")\\n\",\n \"for db_id, queries in sorted(generator.queries_by_db.items()):\\n\",\n \" print(f\\\" {db_id}: {len(queries)}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"2a267246\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Generating Sample Dataset ===\\n\",\n \"Generating column-level RBAC dataset...\\n\",\n \" Roles per DB: 1\\n\",\n \" Queries per role: 3\\n\",\n \"Current ratio (allowed): 60.61%\\n\",\n \" Allowed: 20, Denied: 13\\n\",\n \"\\n\",\n \"Dataset saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_sample.json\\n\",\n \"Metadata saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_sample_metadata.json\\n\",\n \" Total entries: 33\\n\",\n \" Allowed: 20 (60.6%)\\n\",\n \" Denied: 13 (39.4%)\\n\",\n \"\\n\",\n \"Statistics:\\n\",\n \" total_entries: 33\\n\",\n \" allowed: 20\\n\",\n \" denied: 13\\n\",\n \" allowed_ratio: 0.6060606060606061\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.4 Generate sample dataset (for format validation)\\n\",\n \"# Sample 数据集已用于调试,正式生成使用 4.5\\n\",\n \"\\n\",\n \"'''\\n\",\n \"print(\\\"=== Generating Sample Dataset ===\\\")\\n\",\n \"sample_stats = generator.generate_and_save(\\n\",\n \" output_path=str(project_root / 'outputs' / 'column_level_rbac_dataset_bird_sample.json'),\\n\",\n \" roles_per_db=1, # Use only 1 role per database\\n\",\n \" queries_per_role=3 # Use only 3 queries per role\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"\\\\\\\\nStatistics:\\\")\\n\",\n \"for k, v in sample_stats.items():\\n\",\n \" if k != 'by_database':\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Sample dataset generation - 正式生成使用 4.5\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"4851fde0\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Data Format Validation ===\\n\",\n \"Total entries: 33\\n\",\n \"\\n\",\n \"--- Allowed Entry Example ---\\n\",\n \"db_id: toxicology\\n\",\n \"role: MolecularResearcher\\n\",\n \"policy: {\\\"molecule\\\": [\\\"molecule_id\\\", \\\"label\\\"], \\\"atom\\\": [\\\"atom_id\\\", \\\"molecule_id\\\", \\\"element\\\"], \\\"bond\\\": [\\\"bond...\\n\",\n \"input: What are the atom IDs of the bond TR000_2_5?...\\n\",\n \"output: SELECT T.atom_id FROM connected AS T WHERE T.bond_id = 'TR000_2_5'...\\n\",\n \"difficulty: simple\\n\",\n \"instruction length: 1887 chars\\n\",\n \"\\n\",\n \"--- Denied Entry Example ---\\n\",\n \"db_id: thrombosis_prediction\\n\",\n \"role: ClinicalResearcher\\n\",\n \"input: How old was the patient who had the highest hemoglobin count on the date of labo...\\n\",\n \"output: Sorry, I cannot answer.\\n\",\n \"missing_columns: {'patient': ['Diagnosis']}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.5-4.6 Validate generated data format & Show instruction example\\n\",\n \"# 已验证数据格式正确,archive 以简化 notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"sample_file = project_root / 'outputs' / 'column_level_rbac_dataset_bird_sample.json'\\n\",\n \"with open(sample_file, 'r') as f:\\n\",\n \" sample_data = json.load(f)\\n\",\n \"\\n\",\n \"print(\\\"=== Data Format Validation ===\\\")\\n\",\n \"print(f\\\"Total entries: {len(sample_data)}\\\")\\n\",\n \"\\n\",\n \"allowed_entry = next((e for e in sample_data if e['metadata']['permission'] == 'allowed'), None)\\n\",\n \"denied_entry = next((e for e in sample_data if e['metadata']['permission'] == 'denied'), None)\\n\",\n \"\\n\",\n \"if allowed_entry:\\n\",\n \" print(\\\"\\\\\\\\n--- Allowed Entry Example ---\\\")\\n\",\n \" print(f\\\"db_id: {allowed_entry['db_id']}\\\")\\n\",\n \" print(f\\\"role: {allowed_entry['role']}\\\")\\n\",\n \" print(f\\\"policy: {json.dumps(allowed_entry['policy'])[:100]}...\\\")\\n\",\n \" print(f\\\"input: {allowed_entry['input'][:80]}...\\\")\\n\",\n \" print(f\\\"output: {allowed_entry['output'][:80]}...\\\")\\n\",\n \"\\n\",\n \"if denied_entry:\\n\",\n \" print(\\\"\\\\\\\\n--- Denied Entry Example ---\\\")\\n\",\n \" print(f\\\"db_id: {denied_entry['db_id']}\\\")\\n\",\n \" print(f\\\"role: {denied_entry['role']}\\\")\\n\",\n \" print(f\\\"output: {denied_entry['output']}\\\")\\n\",\n \" print(f\\\"missing_columns: {denied_entry['metadata']['missing_columns']}\\\")\\n\",\n \"\\n\",\n \"# Show full instruction\\n\",\n \"print(\\\"\\\\\\\\n=== Full Instruction Example ===\\\")\\n\",\n \"if allowed_entry:\\n\",\n \" print(allowed_entry['instruction'][:2500])\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Data format validation - 已验证正常\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"c05e8787\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Generating Full Column-Level RBAC Dataset ===\\n\",\n \"Output: /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_20251224.json\\n\",\n \"Note: Using updated column_extractor with CTE fix\\n\",\n \"Generating column-level RBAC dataset...\\n\",\n \" Roles per DB: None\\n\",\n \" Queries per role: all\\n\",\n \"Current ratio (allowed): 61.18%\\n\",\n \" Allowed: 4351, Denied: 2761\\n\",\n \"\\n\",\n \"Dataset saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_20251224.json\\n\",\n \"Metadata saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_20251224_metadata.json\\n\",\n \" Total entries: 7112\\n\",\n \" Allowed: 4351 (61.2%)\\n\",\n \" Denied: 2761 (38.8%)\\n\",\n \"\\n\",\n \"Full dataset statistics:\\n\",\n \" total_entries: 7112\\n\",\n \" allowed: 4351\\n\",\n \" denied: 2761\\n\",\n \" allowed_ratio: 0.6117829021372329\\n\",\n \"\\n\",\n \"Per-database distribution:\\n\",\n \" toxicology: allowed=580, denied=0\\n\",\n \" debit_card_specializing: allowed=223, denied=33\\n\",\n \" thrombosis_prediction: allowed=432, denied=220\\n\",\n \" student_club: allowed=396, denied=394\\n\",\n \" superhero: allowed=328, denied=317\\n\",\n \" financial: allowed=212, denied=318\\n\",\n \" card_games: allowed=507, denied=448\\n\",\n \" codebase_community: allowed=669, denied=75\\n\",\n \" california_schools: allowed=173, denied=272\\n\",\n \" formula_1: allowed=553, denied=317\\n\",\n \" european_football_2: allowed=278, denied=367\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 4.5 Generate Full Column-Level RBAC Dataset\\n\",\n \"from datetime import datetime\\n\",\n \"\\n\",\n \"# Reload the modules to ensure we use the latest CTE fix\\n\",\n \"import importlib\\n\",\n \"import src.processors.column_extractor\\n\",\n \"import src.processors.column_permission_checker\\n\",\n \"import src.processors.column_level_rbac_generator\\n\",\n \"\\n\",\n \"importlib.reload(src.processors.column_extractor)\\n\",\n \"importlib.reload(src.processors.column_permission_checker)\\n\",\n \"importlib.reload(src.processors.column_level_rbac_generator)\\n\",\n \"\\n\",\n \"# Re-import with fresh code\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator\\n\",\n \"\\n\",\n \"# Re-create generator with fresh modules\\n\",\n \"generator = ColumnLevelRBACGenerator(\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" role_assignments_path=str(ROLE_ASSIGNMENTS_PATH),\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH)\\n\",\n \")\\n\",\n \"\\n\",\n \"# Output with date suffix\\n\",\n \"today = datetime.now().strftime('%Y%m%d')\\n\",\n \"output_path = project_root / 'outputs' / f'column_level_rbac_dataset_bird_{today}.json'\\n\",\n \"\\n\",\n \"print(\\\"=== Generating Full Column-Level RBAC Dataset ===\\\")\\n\",\n \"print(f\\\"Output: {output_path}\\\")\\n\",\n \"\\n\",\n \"full_stats = generator.generate_and_save(\\n\",\n \" output_path=str(output_path),\\n\",\n \" roles_per_db=None, # Use all roles (number varies per db based on LLM generation)\\n\",\n \" queries_per_role=None # Use all queries\\n\",\n \")\\n\",\n \"\\n\",\n \"print(\\\"\\\\nFull dataset statistics:\\\")\\n\",\n \"for k, v in full_stats.items():\\n\",\n \" if k == 'by_database':\\n\",\n \" print(f\\\"\\\\nPer-database distribution:\\\")\\n\",\n \" for db_id, counts in v.items():\\n\",\n \" print(f\\\" {db_id}: allowed={counts['allowed']}, denied={counts['denied']}\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" {k}: {v}\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": null,\n \"id\": \"2142ac6e\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Comparison with Table-Level Data Format ===\\n\",\n \"\\n\",\n \"--- Table-Level Entry Fields ---\\n\",\n \"['db_id', 'difficulty', 'input', 'instruction', 'metadata', 'output', 'role', 'tables']\\n\",\n \"\\n\",\n \"--- Column-Level Entry Fields ---\\n\",\n \"['db_id', 'difficulty', 'input', 'instruction', 'metadata', 'output', 'policy', 'role']\\n\",\n \"\\n\",\n \"Common fields: {'output', 'role', 'metadata', 'difficulty', 'db_id', 'input', 'instruction'}\\n\",\n \"Table-Level only: {'tables'}\\n\",\n \"Column-Level only: {'policy'}\\n\"\n ]\n }\n ],\n \"source\": [\n \"# [ARCHIVED] 4.6 Compare with Table-Level format\\n\",\n \"# 格式对比已完成,archive 以简化 notebook\\n\",\n \"\\n\",\n \"'''\\n\",\n \"print(\\\"=== Comparison with Table-Level Data Format ===\\\")\\n\",\n \"\\n\",\n \"table_level_file = project_root / 'data' / 'selected' / 'bird' / 'bird_dev_with_role_rbac.json'\\n\",\n \"if table_level_file.exists():\\n\",\n \" with open(table_level_file, 'r') as f:\\n\",\n \" table_level_data = json.load(f)\\n\",\n \" \\n\",\n \" print(\\\"\\\\\\\\n--- Table-Level Entry Fields ---\\\")\\n\",\n \" print(sorted(table_level_data[0].keys()))\\n\",\n \" \\n\",\n \" print(\\\"\\\\\\\\n--- Column-Level Entry Fields ---\\\") \\n\",\n \" print(sorted(sample_data[0].keys()))\\n\",\n \" \\n\",\n \" tl_keys = set(table_level_data[0].keys())\\n\",\n \" cl_keys = set(sample_data[0].keys())\\n\",\n \" \\n\",\n \" print(f\\\"\\\\\\\\nCommon fields: {tl_keys & cl_keys}\\\")\\n\",\n \" print(f\\\"Table-Level only: {tl_keys - cl_keys}\\\")\\n\",\n \" print(f\\\"Column-Level only: {cl_keys - tl_keys}\\\")\\n\",\n \"'''\\n\",\n \"\\n\",\n \"print(\\\"⏭️ [ARCHIVED] Table-level comparison - 格式已验证兼容\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"7392ebc1\",\n \"metadata\": {},\n \"source\": [\n \"## 5. Role Generation Quality Assessment\\n\",\n \"\\n\",\n \"Automated quality checks for generated role files and RBAC datasets.\\n\",\n \"\\n\",\n \"### Quality Metrics\\n\",\n \"\\n\",\n \"| Metric | Healthy Range | Issue Description |\\n\",\n \"|--------|---------------|-------------------|\\n\",\n \"| **Deny Rate** | 5% - 90% | <5% too permissive, >90% too restrictive |\\n\",\n \"| **All-Allow Queries** | <50% | High value indicates poor role differentiation |\\n\",\n \"| **Role-Schema Similarity** | >0.6 | <0.6 indicates poor semantic alignment |\\n\",\n \"| **Policy Overlap** | <70% | >70% indicates redundant roles |\\n\",\n \"| **Coverage Diversity** | std >0.1 | Low std indicates roles have similar permissions |\\n\",\n \"\\n\",\n \"### Evaluation Pipeline\\n\",\n \"1. **Deny Rate Analysis**: Check permission distribution balance\\n\",\n \"2. **Semantic Quality**: Embedding-based role-schema alignment\\n\",\n \"3. **Policy Overlap**: Jaccard similarity to detect redundant roles\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 73,\n \"id\": \"71f8f60f\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Quality Thresholds (Configurable for BIRD) ===\\n\",\n \" deny_rate_min: 5.0\\n\",\n \" coverage_max: 0.85\\n\",\n \" coverage_std_min: 0.065\\n\",\n \" max_overlap: 0.8\\n\",\n \" embed_model: text-embedding-3-small\\n\",\n \" embed_backend: auto\\n\",\n \"\\n\",\n \"Role Assignments: bird_column_level_rbac_assignments_v2_20251230.json\\n\",\n \"Dataset: column_level_rbac_dataset_bird_20251230.json\\n\",\n \"Schema Dir: /home/feiy/Role-SQL-benchmark/data/Bird/dev_20251106/dev_databases\\n\",\n \"\\n\",\n \"✓ Quality evaluator initialized with custom thresholds\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 5.1 Setup Quality Evaluator (force reload all modules)\\n\",\n \"import importlib\\n\",\n \"import sys\\n\",\n \"from pathlib import Path\\n\",\n \"import glob\\n\",\n \"\\n\",\n \"# Force reload the entire module tree\\n\",\n \"if 'src.role_evaluator.quality_metrics' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator.quality_metrics']\\n\",\n \"if 'src.role_evaluator.quality_visualizer' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator.quality_visualizer']\\n\",\n \"if 'src.role_evaluator' in sys.modules:\\n\",\n \" del sys.modules['src.role_evaluator']\\n\",\n \"\\n\",\n \"from src.role_evaluator import RoleQualityEvaluator, RoleQualityVisualizer, print_quality_report\\n\",\n \"\\n\",\n \"# Configure paths\\n\",\n \"project_root = Path.cwd().parent if Path.cwd().name == 'notebooks' else Path.cwd()\\n\",\n \"\\n\",\n \"# Find most recent dataset and role assignments files\\n\",\n \"role_files = sorted(glob.glob(str(project_root / 'outputs' / 'column_level_role_assignments_bird_*.json')))\\n\",\n \"ROLE_ASSIGNMENTS_PATH = role_files[-1] if role_files else str(project_root / 'outputs' / 'column_level_role_assignments_bird.json')\\n\",\n \"\\n\",\n \"# Check for v2 role assignments (after regeneration)\\n\",\n \"role_files_v2 = sorted([f for f in glob.glob(str(project_root / 'outputs' / 'bird_column_level_rbac_assignments_*.json'))\\n\",\n \" if 'v2' in f])\\n\",\n \"if role_files_v2:\\n\",\n \" ROLE_ASSIGNMENTS_PATH = role_files_v2[-1]\\n\",\n \"\\n\",\n \"dataset_files = sorted([f for f in glob.glob(str(project_root / 'outputs' / 'column_level_rbac_dataset_bird_*.json')) \\n\",\n \" if 'metadata' not in f])\\n\",\n \"if not dataset_files:\\n\",\n \" raise FileNotFoundError(\\\"No RBAC dataset found in outputs/\\\")\\n\",\n \"DATASET_PATH = dataset_files[-1]\\n\",\n \"\\n\",\n \"SCHEMA_DIR = str(project_root / 'data' / 'Bird' / 'dev_20251106' / 'dev_databases')\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# CONFIGURABLE QUALITY THRESHOLDS\\n\",\n \"# Adjust based on dataset characteristics\\n\",\n \"# BIRD has larger DBs with more columns, so thresholds differ from Spider\\n\",\n \"# ============================================================\\n\",\n \"QUALITY_THRESHOLDS = {\\n\",\n \" \\\"deny_rate_min\\\": 5.0, # Warn if deny rate < this (roles too permissive)\\n\",\n \" \\\"coverage_max\\\": 0.85, # Warn if avg coverage > this (roles too broad)\\n\",\n \" \\\"coverage_std_min\\\": 0.065, # Warn if coverage std < this (low diversity)\\n\",\n \" \\\"max_overlap\\\": 0.80, # Warn if max overlap > this (redundant roles)\\n\",\n \"}\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EMBEDDING MODEL CONFIGURATION\\n\",\n \"# Backend auto-detected based on model name, or set explicitly\\n\",\n \"# ============================================================\\n\",\n \"# OpenAI models (requires OPENAI_API_KEY):\\n\",\n \"# - \\\"text-embedding-3-small\\\" (default, cost-effective)\\n\",\n \"# - \\\"text-embedding-3-large\\\" (higher accuracy)\\n\",\n \"# - \\\"text-embedding-ada-002\\\" (legacy)\\n\",\n \"# \\n\",\n \"# BAAI/fastembed models (local, no API key needed):\\n\",\n \"# - \\\"BAAI/bge-small-en-v1.5\\\" (fast, lightweight)\\n\",\n \"# - \\\"BAAI/bge-base-en-v1.5\\\" (balanced)\\n\",\n \"# - \\\"BAAI/bge-large-en-v1.5\\\" (best accuracy)\\n\",\n \"# ============================================================\\n\",\n \"# EMBED_MODEL = \\\"BAAI/bge-small-en-v1.5\\\"\\n\",\n \"EMBED_MODEL = \\\"text-embedding-3-small\\\"\\n\",\n \"EMBED_BACKEND = None # None = auto-detect, or \\\"openai\\\" / \\\"fastembed\\\"\\n\",\n \"\\n\",\n \"print(\\\"=== Quality Thresholds (Configurable for BIRD) ===\\\")\\n\",\n \"for k, v in QUALITY_THRESHOLDS.items():\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"print(f\\\" embed_model: {EMBED_MODEL}\\\")\\n\",\n \"print(f\\\" embed_backend: {EMBED_BACKEND or 'auto'}\\\")\\n\",\n \"\\n\",\n \"print(f\\\"\\\\nRole Assignments: {Path(ROLE_ASSIGNMENTS_PATH).name if ROLE_ASSIGNMENTS_PATH else 'Not found'}\\\")\\n\",\n \"print(f\\\"Dataset: {Path(DATASET_PATH).name if DATASET_PATH else 'Not found'}\\\")\\n\",\n \"print(f\\\"Schema Dir: {SCHEMA_DIR}\\\")\\n\",\n \"\\n\",\n \"if ROLE_ASSIGNMENTS_PATH and DATASET_PATH:\\n\",\n \" evaluator = RoleQualityEvaluator(\\n\",\n \" role_assignments_path=ROLE_ASSIGNMENTS_PATH,\\n\",\n \" dataset_path=DATASET_PATH,\\n\",\n \" schema_dir=SCHEMA_DIR,\\n\",\n \" embed_model=EMBED_MODEL,\\n\",\n \" embed_backend=EMBED_BACKEND,\\n\",\n \" thresholds=QUALITY_THRESHOLDS\\n\",\n \" )\\n\",\n \" print(\\\"\\\\n✓ Quality evaluator initialized with custom thresholds\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"\\\\n Missing files - generate roles and dataset first\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 74,\n \"id\": \"8135e2e3\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[embedding_utils] Using OpenAI embeddings (text-embedding-3-small) for 11 texts\\n\",\n \"[embedding_utils] Using OpenAI embeddings (text-embedding-3-small) for 47 texts\\n\",\n \"Overall: 11 databases, 53 roles, 7252 entries\\n\",\n \"Pass Rate: 100.0% (11/11 pass)\\n\",\n \"\\n\",\n \"Using Thresholds: deny_rate_min=5.0%, max_overlap=80%, coverage_std_min=0.065\\n\",\n \"\\n\",\n \"=== Deny Rate Analysis ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Deny%</th>\\n\",\n \" <th>AllAllow%</th>\\n\",\n \" <th>AllDeny</th>\\n\",\n \" <th>Roles</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>73.5%</td>\\n\",\n \" <td>1.1%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>46.9%</td>\\n\",\n \" <td>2.1%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>37.8%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>54.7%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>57.1%</td>\\n\",\n \" <td>1.6%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>70.6%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>44.7%</td>\\n\",\n \" <td>8.7%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>49.9%</td>\\n\",\n \" <td>1.3%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>49.1%</td>\\n\",\n \" <td>0.0%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>33.7%</td>\\n\",\n \" <td>2.5%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>47.4%</td>\\n\",\n \" <td>11.7%</td>\\n\",\n \" <td>0</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Deny% AllAllow% AllDeny Roles Status\\n\",\n \"0 california_schools 73.5% 1.1% 0 5 OK\\n\",\n \"1 card_games 46.9% 2.1% 0 5 OK\\n\",\n \"2 codebase_community 37.8% 0.0% 0 5 OK\\n\",\n \"3 debit_card_specializing 54.7% 0.0% 0 6 OK\\n\",\n \"4 european_football_2 57.1% 1.6% 0 5 OK\\n\",\n \"5 financial 70.6% 0.0% 0 5 OK\\n\",\n \"6 formula_1 44.7% 8.7% 0 4 OK\\n\",\n \"7 student_club 49.9% 1.3% 0 5 OK\\n\",\n \"8 superhero 49.1% 0.0% 0 5 OK\\n\",\n \"9 thrombosis_prediction 33.7% 2.5% 0 4 OK\\n\",\n \"10 toxicology 47.4% 11.7% 0 4 OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Semantic Quality (Similarity: role-schema alignment, Coverage: column access, Diversity: coverage std) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Similarity</th>\\n\",\n \" <th>Coverage</th>\\n\",\n \" <th>Diversity</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>0.6902</td>\\n\",\n \" <td>34.8%</td>\\n\",\n \" <td>0.0996</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>0.6843</td>\\n\",\n \" <td>50.0%</td>\\n\",\n \" <td>0.2965</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>0.6332</td>\\n\",\n \" <td>60.6%</td>\\n\",\n \" <td>0.2888</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>0.6264</td>\\n\",\n \" <td>63.8%</td>\\n\",\n \" <td>0.1299</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>0.6799</td>\\n\",\n \" <td>31.3%</td>\\n\",\n \" <td>0.0984</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>0.6410</td>\\n\",\n \" <td>46.8%</td>\\n\",\n \" <td>0.1288</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>0.6599</td>\\n\",\n \" <td>44.0%</td>\\n\",\n \" <td>0.1480</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>0.6780</td>\\n\",\n \" <td>50.5%</td>\\n\",\n \" <td>0.1701</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>0.6835</td>\\n\",\n \" <td>61.3%</td>\\n\",\n \" <td>0.1895</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>0.6607</td>\\n\",\n \" <td>69.3%</td>\\n\",\n \" <td>0.3798</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>0.6698</td>\\n\",\n \" <td>69.7%</td>\\n\",\n \" <td>0.1134</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Similarity Coverage Diversity Status\\n\",\n \"0 california_schools 0.6902 34.8% 0.0996 OK\\n\",\n \"1 card_games 0.6843 50.0% 0.2965 OK\\n\",\n \"2 codebase_community 0.6332 60.6% 0.2888 OK\\n\",\n \"3 debit_card_specializing 0.6264 63.8% 0.1299 OK\\n\",\n \"4 european_football_2 0.6799 31.3% 0.0984 OK\\n\",\n \"5 financial 0.6410 46.8% 0.1288 OK\\n\",\n \"6 formula_1 0.6599 44.0% 0.1480 OK\\n\",\n \"7 student_club 0.6780 50.5% 0.1701 OK\\n\",\n \"8 superhero 0.6835 61.3% 0.1895 OK\\n\",\n \"9 thrombosis_prediction 0.6607 69.3% 0.3798 OK\\n\",\n \"10 toxicology 0.6698 69.7% 0.1134 OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Policy Overlap Analysis ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>AvgOverlap</th>\\n\",\n \" <th>MaxOverlap</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>30.7%</td>\\n\",\n \" <td>57.1%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>card_games</td>\\n\",\n \" <td>11.9%</td>\\n\",\n \" <td>23.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>codebase_community</td>\\n\",\n \" <td>28.0%</td>\\n\",\n \" <td>57.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>debit_card_specializing</td>\\n\",\n \" <td>47.0%</td>\\n\",\n \" <td>65.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>european_football_2</td>\\n\",\n \" <td>14.4%</td>\\n\",\n \" <td>23.8%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>5</th>\\n\",\n \" <td>financial</td>\\n\",\n \" <td>21.5%</td>\\n\",\n \" <td>37.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>6</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>29.3%</td>\\n\",\n \" <td>32.9%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>7</th>\\n\",\n \" <td>student_club</td>\\n\",\n \" <td>30.2%</td>\\n\",\n \" <td>45.0%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>8</th>\\n\",\n \" <td>superhero</td>\\n\",\n \" <td>33.0%</td>\\n\",\n \" <td>52.6%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>9</th>\\n\",\n \" <td>thrombosis_prediction</td>\\n\",\n \" <td>18.1%</td>\\n\",\n \" <td>38.5%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>10</th>\\n\",\n \" <td>toxicology</td>\\n\",\n \" <td>53.7%</td>\\n\",\n \" <td>66.7%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database AvgOverlap MaxOverlap Status\\n\",\n \"0 california_schools 30.7% 57.1% OK\\n\",\n \"1 card_games 11.9% 23.8% OK\\n\",\n \"2 codebase_community 28.0% 57.8% OK\\n\",\n \"3 debit_card_specializing 47.0% 65.0% OK\\n\",\n \"4 european_football_2 14.4% 23.8% OK\\n\",\n \"5 financial 21.5% 37.0% OK\\n\",\n \"6 formula_1 29.3% 32.9% OK\\n\",\n \"7 student_club 30.2% 45.0% OK\\n\",\n \"8 superhero 33.0% 52.6% OK\\n\",\n \"9 thrombosis_prediction 18.1% 38.5% OK\\n\",\n \"10 toxicology 53.7% 66.7% OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n }\n ],\n \"source\": [\n \"# 5.2 Run Quality Evaluations and Display as DataFrame\\n\",\n \"import pandas as pd\\n\",\n \"import importlib\\n\",\n \"\\n\",\n \"# Force reload modules to use latest code\\n\",\n \"import src.role_evaluator.quality_metrics\\n\",\n \"import src.role_evaluator.quality_visualizer\\n\",\n \"importlib.reload(src.role_evaluator.quality_metrics)\\n\",\n \"importlib.reload(src.role_evaluator.quality_visualizer)\\n\",\n \"\\n\",\n \"from src.role_evaluator.quality_metrics import print_quality_report\\n\",\n \"\\n\",\n \"# Set display options for full content (no truncation)\\n\",\n \"pd.set_option('display.max_colwidth', None)\\n\",\n \"pd.set_option('display.max_rows', None)\\n\",\n \"pd.set_option('display.width', None)\\n\",\n \"\\n\",\n \"if 'evaluator' in dir():\\n\",\n \" report = evaluator.evaluate_all()\\n\",\n \" \\n\",\n \" # Summary\\n\",\n \" print(f\\\"Overall: {report.total_databases} databases, {report.total_roles} roles, {report.total_entries} entries\\\")\\n\",\n \" print(f\\\"Pass Rate: {report.pass_rate:.1%} ({report.total_databases - len(report.databases_with_issues)}/{report.total_databases} pass)\\\")\\n\",\n \" print(f\\\"\\\\nUsing Thresholds: deny_rate_min={QUALITY_THRESHOLDS['deny_rate_min']}%, \\\"\\n\",\n \" f\\\"max_overlap={QUALITY_THRESHOLDS['max_overlap']:.0%}, \\\"\\n\",\n \" f\\\"coverage_std_min={QUALITY_THRESHOLDS['coverage_std_min']}\\\\n\\\")\\n\",\n \" \\n\",\n \" # Deny Rate DataFrame - Full display\\n\",\n \" if report.deny_rate_metrics:\\n\",\n \" deny_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"Deny%\\\": f\\\"{m.deny_rate:.1f}%\\\",\\n\",\n \" \\\"AllAllow%\\\": f\\\"{m.all_allow_pct:.1f}%\\\",\\n\",\n \" \\\"AllDeny\\\": m.all_deny_queries,\\n\",\n \" \\\"Roles\\\": len(m.roles),\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.deny_rate_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"=== Deny Rate Analysis ===\\\")\\n\",\n \" display(deny_df)\\n\",\n \"\\n\",\n \" # Semantic Quality DataFrame \\n\",\n \" if report.semantic_metrics:\\n\",\n \" sem_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"Similarity\\\": f\\\"{m.avg_similarity:.4f}\\\",\\n\",\n \" \\\"Coverage\\\": f\\\"{m.avg_coverage:.1%}\\\",\\n\",\n \" \\\"Diversity\\\": f\\\"{m.coverage_std:.4f}\\\",\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.semantic_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Semantic Quality (Similarity: role-schema alignment, Coverage: column access, Diversity: coverage std) ===\\\")\\n\",\n \" display(sem_df)\\n\",\n \"\\n\",\n \" # Policy Overlap DataFrame\\n\",\n \" if report.overlap_metrics:\\n\",\n \" ovl_df = pd.DataFrame([\\n\",\n \" {\\n\",\n \" \\\"Database\\\": db_id,\\n\",\n \" \\\"AvgOverlap\\\": f\\\"{m.avg_overlap:.1%}\\\",\\n\",\n \" \\\"MaxOverlap\\\": f\\\"{m.max_overlap:.1%}\\\",\\n\",\n \" \\\"Status\\\": \\\"WARN\\\" if m.issues else \\\"OK\\\"\\n\",\n \" }\\n\",\n \" for db_id, m in sorted(report.overlap_metrics.items())\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Policy Overlap Analysis ===\\\")\\n\",\n \" display(ovl_df)\\n\",\n \"else:\\n\",\n \" print(\\\" Evaluator not initialized\\\")\\n\",\n \"\\n\",\n \"pd.reset_option('display.max_colwidth')\\n\",\n \"pd.reset_option('display.max_rows')\\n\",\n \"pd.reset_option('display.width')\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 75,\n \"id\": \"89f843e7\",\n \"metadata\": {},\n \"outputs\": [],\n \"source\": [\n \"# # 5.3 Quality Dashboard Visualization\\n\",\n \"# import matplotlib.pyplot as plt\\n\",\n \"# from src.role_evaluator.quality_visualizer import RoleQualityVisualizer\\n\",\n \"\\n\",\n \"# visualizer = RoleQualityVisualizer(evaluator=evaluator, report=report)\\n\",\n \"# fig = visualizer.plot_quality_dashboard()\\n\",\n \"# plt.show()\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 76,\n \"id\": \"86eb26de\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Total roles: 53, SystemManager: 11\\n\"\n ]\n },\n {\n \"name\": \"stderr\",\n \"output_type\": \"stream\",\n \"text\": [\n \"/tmp/ipykernel_1782890/140258372.py:110: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.\\n\",\n \" plt.tight_layout(rect=[0,0,0.95,0.95])\\n\"\n ]\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Saved: /home/feiy/Role-SQL-benchmark/plot/column_level_rbac_semantic.png\\n\"\n ]\n },\n {\n \"data\": {\n \"image/png\": \"iVBORw0KGgoAAAANSUhEUgAABtcAAAOzCAYAAADDRVknAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAXEgAAFxIBZ5/SUgABAABJREFUeJzs3XdYU9f/B/B3QtgblK1MweLAWdyg4t6tde9arbXVWq211jrraB1122pb96rWva3iFve2OBG3IsqQTXJ/f/AjX+O9gQCBgL5fz5NHc3LuuSfr5HI/93yOTBAEAURERERERERERERERESUK7mhO0BERERERERERERERERUUjC4RkRERERERERERERERKQjBteIiIiIiIiIiIiIiIiIdMTgGhEREREREREREREREZGOGFwjIiIiIiIiIiIiIiIi0hGDa0REREREREREREREREQ6Uhi6A0RERERERERERERUMgmCAEEQDN0NIqJ8k8lkkMlkedqGwTUiIiIiIiIiIiIi0plSqURsbCwSExORnp5u6O4QERWYiYkJrK2t4ejoCCMjo1zrywReVkBEREREREREREREOlAqlbh//z5SU1MN3RUiIr0zMzND2bJlcw2wceYaEREREREREREREekkNjYWqampMDIygrOzMywtLSGXyw3dLSKifFOpVEhKSsKzZ8+QmpqK2NhYODk55bgNg2tEREREREREREREpJPExEQAgLOzM2xtbQ3cGyKigpPL5erx7PHjx0hMTGRwjYiIiIiIiIiIiIgKThAE9RprlpaWhbafzLQ0XFi6BKpMJar3HwiFmVmh7YuIKFv2uJaeng5BECCTybTWZXCNiIiIiIiIiIiIiHIlCIL6/4WZCvL6xvU4MnkiAMDY3BzVPh1QaPsiIsr25riWW3CNyXCJiIiIiIiIiIiIqFhQZWbi9MJ56vsR82YjPem1AXtERCTG4BoRERERERERERERFQs3tm9B/P1omEAGC5kcKbEvcOGvPwzdLSIiDQyuEREREREREREREZHBCSoVTs2fAwDwMjZDORMLAMCZ3xYgNT7ekF0jItLA4BoRERERERERERERGdztvbsRe/MGFJDB09gMbkYmsJIZIS0hHmd/X2jo7hERqTG4RkREREREREREREQGJQgCTs2bDQDwNDaDsUwOmUwGfxNzAMD5P39H8osYA/YQ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3LkTW7Zswb59+9RrrQH5+w6sW7cOH374ISpWrKjTc9CFTBAEQW+tEREREREREREREdE7SaVS4caNGwCAgIAAUWq5/EhNTcWPP/6onh2kMjFBmp83Ml1dIBgbQ5aRAePHT2FyJysVJAA0atQIEydOLLQ1xyjL9OnTMXr0aNy9e1ed6o+oKIWEhOD69euIiYnJdxunT59GcHAwVq5ciR49euRYNy9jHINrRERERERERERERJSrwgiuAYBSqcT69euxbt06jTWu3ubu7o7OnTujS5cuets3aZeSkoJy5cqhffv2mD9/vqG7Q++wp0+fwsXFRaNs48aN+OSTT9C3b1/89ddf+W67Xbt2iIqKwsWLF3MdN/IyxjEtJBEREREREREREREZjJGREbp164bOnTvj5MmT2Lp1Kx49eoTk5GRYWFjA3d0d7dq1Q+3atYtsjTXKWgts+fLlOHv2LARBgEwmM3SX6B3Vs2dPJCQkoHr16rCwsMCVK1ewb98+ODo6Yty4cfluNy0tDdWrV8fo0aP1HpDnzDUiIiIiIiIiIiIiylVhzVwjovfbH3/8gSVLluDWrVtITExEqVKl0KRJE4wfPx4+Pj5F1g+mhSQiIiIiIiIiIiIivWJwjYjeZXkZ4zj6EREREREREREREREREemIwTUiIiIiIiIiIiIiIiIiHTG4RkRERERERERERES5kslk6v+rVCoD9oSISP/eHNfeHO+kMLhGRERERERERERERLmSyWQwMTEBACQlJRm4N0RE+pU9rpmYmOQaXFMURYeIiIiIiIiIiIiIqOSztrZGbGwsnj17BgCwtLSEXM45HERUcqlUKiQlJanHNWtr61y3kQmCIBR2x4iIiIiIiIiIiIio5FMqlbh//z5SU1MN3RUiIr0zMzND2bJlYWRklGM9BteIiIiIiIiIiIiISGdKpRKxsbFITExEenq6obtDRFRgJiYmsLa2hqOjY66BNYDBNSIiIiIiIiIiIiLKJ0EQwFPMRFSSyWSyXNdYE23D4BoRERERERERERERERGRbrjSJBEREREREREREREREZGOGFwjIiIiIiIiIiIiIiIi0hGDa0REREREREREREREREQ6YnCNiIiIiIiIiIiIiIiISEcMrhERERERERERERERERHpiME1IiIiIiIiIiIiIiIiIh0xuEZERERERERERERERESkIwbXiIiIiIiIiIiIiIiIiHTE4BoRERERERERERERERGRjhhcIyIiIiIiIiIiIiIiItIRg2tEREREREREREREREREOmJwjYiIiIiIiIiIiIiIiEhHDK4RERERERERERERERER6YjBNSIiIiIiIiIiIiIiIiIdMbhGREREREREREREREREpCMG14iIiIiIiIiIiIiIiIh0xOAaERERERERERERERERkY4YXCMiIiIiIiIiIiIiIiLSEYNrRERERERERERERERERDpicI2IiIiIiIiIiIiIiIhIRwyuEREREREREREREREREemIwTUiIiIiIiIiIiIiIiIiHTG4RkRERERERERERERERKQjBteIiIiIiIiIiIiIiIiIdMTgGhEREREREREREREREZGOGFwjIiIiIiIiIiIiIiIi0hGDa0REREREREREREREREQ6YnCNiIiIiIiIiIiIiIiISEcMrhERERERERERERERERHpiME1IiIiIiIiIiIiIiIiIh0xuEZERERERERERERERESkIwbXiIiIiIiIiIiIiIiIiHTE4BoRERERERERERERERGRjhhcIyIiIiIiIiIiIiIiItIRg2tEREREREREREREREREOmJwjYiIiIiIiIiIiIiIiEhHDK4RERERERERERERERER6YjBNSIiIiIiIiIiIiIiIiIdMbhGREREREREREREREREpCMG14iIiIiIiIiIiIiIiIh0xOAaERERERERERERERERkY4YXCMiIiIiIiIiIiIiIiLSEYNrRERERERERERERERERDpicI2IiIiIiIiIiIiIiIhIRwyuEREREREREREREREREemIwTUiIiIiIiIiIiIiIiIiHSkM3QEiorxwcXFBUlISypYta+iuEBERERERERERFYn79+/D0tIST58+LXBbbdu2xZ07d/TQK/3z9fXFtm3bDN0NolwxuEZEJUpSUhIyMjIM3Q0iIiIiIiIiIqIik5GRgaSkJL20defOHdy+/R/8vIz10p6+3L7Hc35UcjC4RkQlSvaMtWvXrhm4J4UjMzMTCgWHZiLSH44rRKRvHFeISJ84phCRvr2r40qFChX02p6flzGuHPbUa5sFVSkk2tBdINIZ11wjIiomBEFAdHQ0BEEwdFeI6B3BcYWI9I3jChHpE8cUItI3jitEVFTevRA+EVEJJZPJ4Ovra+huENE7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qv92yAKAVRRteRWu80JaSREKzp3Dho8+RK+H/sb33g343mPuAXzvVacx771dB/bCOqgFyv77ne96Go0GWk3Fn05mi6XKCUvVrWcym2u8NplWq4Xmv7+L+q5nNJlsvzMx0h+bN2xH7249APC9Vx1Xeu8x9/5H6vfe8dPH4Ne+YnvlBgPEGtqj9Pr/nThpMFS/TwECPPT/nUZRFGEor5oVQMVsKR4e+v+uZkV5eXm166kEFXT/PfFStFpRbrRfTx8iYPf+nejcqYvk771Lly7Bq5kVFosFJmP1eabWaKDV/jf3zBaYa8k9TeV6pprzTKPVQqP5b+6ZzLDYrWcA/vtyanVaW16YTCZYzPZ5ofEtw759+9CqVStFv/eYe1Ux9/jeq89779iRveh/7/+y2mq11vhcVSo1dDrtf+uzwmT633oawYDK+FWr1dBqK9er+XucWn1D7plryb3K74W1fI/TaOxzr0VzC37bkoxjxwbYrdeQ954jWEVln1hGpGScFpIU7fovW2+++SZ27tyJlStX4sCBA8jNzUVycjL+9re/OWTfb775Jo4cOYItW7Zg9+7ddh1nixYtsg22rV69Grt27bIt+7//+z+kpKRg1apVtqkYK3322We1zq8cFxeHY8eOYfv27di8eXONF6vX6XTYtm0btm/fjj///BMjR460LcvIyLCb5vHVV1+1HaiOjo5GWloaNm7ciPXr1+P48ePw9fUFUPFl+Z133qn1NXnjjTeQlpaG33//Hampqbjnnntw9OhR/PTTT7Z1EhIScPLkSaxfvx6pqal2029evHix1sHFm50oiigoKOA0S+Q2LCaTrWstxtMX6gZOeq8WBMR4+gBmEzJ+/jcsJl57raGYK0T1U15ejrz8a1D5e8ldSr2o/L2QX1hQ48EhR2KukCu5dPkivJu51vXWKnkFeeDqteoHAZrKaDRC0FY94UkWIqC71rpe00ICgEor1nggm4hcm9VqRW7uZUSEN+1wtSgC5y53VNy0kBHNVci9cqXay8YQkWsSRP5VRAo2fvx4W8dUYmIinnjiCSQmJqJt27a2M5muFx0dbTtLatCgQdi6davd8usvvP7AAw/YXTPs+sf6+/vj0qVLdgNq9957r901zXbt2oW+ffvisccewyeffGJXs+66qcsuXrxoN/j2r3/9C1OnTgVQcc21r776yrbshx9+wF/+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K6+EKIrQ6/W45JJL0LdvX0RHR6OmpgZpaWm2ebWaK05YrVZceumlGD58OPR6PXbv3t0gvtaqP77xhRdeiAEDBkAul+OOO+7A5MmTW70uuVyOefPm4Z577rE99sgjj+Ddd99F7969UVlZid27d2PChAm4+uqrAQBDhw7Fjh07AAApKSno1asX4uLisG/fPuTn57d620258MILMXToUNtndPjwYSQkJGDQoEFITU21K0p2xKJFi/DII48gODgYvXv3hr+/P8rKyuzmXgOaH0969uzZWLZsGQIDA5GSkmI3t999991nK4xdeeWVuOCCC/DHH38AAFauXIlu3bph4MCB8Pb2xpkzZ3Do0CG71wNAYGAgnnjiCbzyyisAaq9qmTp1KhITExEfH4/CwkLs3bsXDzzwACZMmNDie+7s766+++67z26ewrCwMLz//vt4//337ZabNm0apk2bJvn261xwwQUYOXKkrXPv0KFD6NmzJwYOHIgjR440mJPtfNdc1xqAdp2cjdR4IbO6wta91qcVc68R0flBEIRWzwXhbOFdu0L9R+cPE6iOiYHqrwPQV9dA1LV/btJzCVax4VxYErNWVsOnsAQXdXCOWFcRHByMfvEJ2JN5Gur4CGeHYyNXKBDo74+CshIIfroWlxdyCqEJ9YOiFctKRbSKMG9KxaO33mU3jLdMJkNkRCTOFlRA3dWvmTV0kABkaytbXq6VjAUV6BbZrU1DYxOR53CnYxUicg/MK0TkKJL8gpk0aRIefPBBu8d2796N3377DRqNBldeeWWjr5s8eTI+/PBDu5MRhw4dwurVq7Fp0yZbYQ1ofr6nq666ClqtFjt27GhQWJs5c2aDzqbmTJ061a57LTs7Gz/99BOWL1+O48ePt3o9de6++27MmTPH7sdi3XCBv/32W4N55F5++WW7Lru0tDSsXbsW+fn5eOmll9q8/cZ89dVXdpNyFhYWYuPGjThx4gRuueUWSbZRp6CgANu3b8dPP/2ErVu32hW4pk+fjsGDBzf6uqFDh2L48OE4ePAgtm7davc5DRs2zK7YKggCVq5cabeu0tJSbN26FatXr8bu3btt263/o33u3Ll2xU+gdljDX375BSkpKXbdaC1xxHd3rrqOwzrZ2dlYvnx5g1v9oT87w5dffmk3gWtBQQE2btyI3Nxc3HfffXbLqlSqTo/HVbXUtQa0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ykrmzZvH+PHj693u2Vx++eWO/7fb7dx8883k5eU5nlNVle+//97xtzwzptdee82xvN1u5/HHH3d6/fR9qLWJiYlx/P/q1av5/fffAdi1axcvvviiR2Ly8/Nzenzs2LF6lx0xYgS9evVyPN64caPj/2+55RbXB+djFEWhY8eOLikiuLJrrYZ0rwlPyMvLw2b0O/eCXsxm9HP6nnMnV+YVIcQfdDodit27TvwVmx2druVH/feVvLLnwF5Msa45RqqPoii069+R4i2FLbqdWtvVa6h0YXGtYEseV4yd1Or/5sI7+UpOEUJ4D8krQgh38ao51wCefPJJ5s+fT2Vl9d19X375JYsWLaJPnz7odDq2bNmCxWLhhhtuYMKECfTr148tW7YAsGrVKlJSUujVqxd79+51mrOsW7duXHPNNUD18HS33347r7zyClBdLBo8eDCDBg2ioKCA7du31xtfVFQU9957r+O96enppKSkMGDAAKKioigoKGD37t2OrqPrrruu3nUdPXqUHj16MGDAAA4ePEh6errjteDgYG699Vageo4xPz8/KioqAHj55ZdZvXo1AQEBrF271vF8XWbNmsW9995LVFQU3bp1IywsjKKiolpdUDVzM0yYMIGBAwc6ioI//PADHTt2pG/fvgQGBpKTk8OuXbscf5+meOCBB/jkk08c844tW7aM5ORk+vTpQ2BgIDt27CA7O5uCggL8/f3p3bs3kydP5ocffgBg//79pKam0r9/fw4ePMiBAwcc627Xrh133HFHk2PztJEjR/L5558D1XPnde/endjYWLKysjx2x82Z83YMHTqUPn36oNVqufnmm52KpQB33303//d//+f0XHx8PKNGjWrxWH2BN3at1ZC514QnmM1mVBftw56iajQeGxYSXJNXhBDOAgICUCot517QjZQqq9vmF/GFvHIg8wDBAzu2+HYi+7bn+L8OYr8oCo2h5YdqB9DoNFSVuqa4ZquyUrm/nIvuvMgl6xOiLr6QU4QQ3kXyihDCHbzualXPnj357rvvnOapOnXqFL/88guLFy92uvNbo9Ewd+5cevbs6Xju+PHjLF682Kmw1rlzZ3766SenIfeefvppLrjgAsfjiooKVqxYwfbt2+nZsye9e/euN8aZM2fy5z//2fHYarWybt06fvrpJ1avXu00nN/Z5rq68cYbKSoqYunSpU6FNY1Gw4cffkh0dDRQPQnnE0884fTedevWsWzZMnQ6HQ8++GC926hx8uRJfv31V+bOncuKFSucimPXXXcd/fr1A6rv7vjhhx8cjwEKCwtZsWIF8+bNY9OmTY73njm3WEPFxMSwYMEC2rdv73iuuLiYVatW8fPPPzu6+U43e/ZsRowY4Xicn5/PkiVLnApr0dHRzJs3r1UPV/LII484dYpZrVaOHj2Kqqr85S9/8UhMV111lVNMhw4dYu7cuXz//fdOv/8aU6dOrfU3mDZtWpP3l7ZEVVWOHTvW7IPAluhaqyHda8LdrFYrdlr3HYd2lFpd6O7iqrwihHDWsWNHrHnFXvNvS1VVbHkljmHlW3pbrT2vlJaWUlZVjr4F51urofPTE9o5ipLddY+M0hIUrYJdtWO325u9rrydJxnaf4jbCrei7fGFnCKE8C6SVxpJ9bIfIVoRr7zaPX78ePbt28fjjz9O//79CQkJQafTER0dzfDhw5k+fbpj2bi4ODZu3Mg777zDyJEjCQ8PR6fTERYWxtChQ3nttdfYunUrycnJTtvw9/dn+fLlPPLIIyQkJGAwGEhISOCBBx5gzZo1Z50bSqvV8umnn/LLL79www030KlTJ/z8/NDr9cTExDBixAgef/xxtm3bxvDhw+tdz6233sqSJUu46KKLCAoKIiAggIsuuojly5c7uuxqPProo/z73/+mR48e6PV6IiIimDJlCps2baJbt271buPNN9/kH//4B0OHDqVjx45OcY4ePZpPPvmEL7/80uk9sbGxrFu3jtmzZ3PZZZcRExODXq/HZDKRmJjI+PHjef311zly5Ei92z2XgQMHsmfPHl566SWGDRvm+LtFRkZywQUX8NhjjzkNfxkcHMwvv/zC559/ztixY2nXrh06nY6goCD69+/P008/zZ49e5wKpq1Rt27dWLlyJZdccgkBAQEEBAQwfPhwFixYwKOPPuqRmOLj41m8eDGjRo0iODj4nMv7+fk5ui6humB78803t2SIPkNRFBITE5s1dEFLda3VkLnXhLvpdDo0rfwIW4PqdIOPO7kirwghajOZTESEhGErLvd0KADYisoJDwnDaGz5YpEv5JUjR45gjPZ32/baXdCRks2FqG4cSlSja37XtGpXKdtSxISxTZsOQIiG8IWcIoTwLpJXhBDuoqhSxhdCuNitt97Kxx9/DMDF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9OhRjh49yty5c7n33nt544033BG6wz/+8Q+nImCN/fv3s3//fv7973/zwQcf1CrInW7Lli289tpr5zzRq6ysZPTo0axcudLxXFVVFZ999hmbN29my5YtmEymBsX96aef8uWXX9Z6vuZ3v3jxYubOnct3332HRtP4WvKyZcsYN24cFovF6fns7Gyys7NZv349e/bsYfTo0Y7XKioquOGGG5g7d67TeyorKykpKeHAgQP06tWL3r1717nNr776io8//hibzeZ4bt26dYwbN46lS5dy0UUXOS3f1L/dqlWruPLKKzl16pTT88ePH2f+/PnMnz+fCRMm8N1332E0Nuzg/9Zbb3UUVGsUFxdTXFxMRkYGCxYsIDExkRtvvLFB62trVFUlMzOTpKQkOQgUQriE5JXWrbFdazWke020JF/NKwcPHsQU4+/pMBrEFOvPwYMH6dmzp6dD8YiKigpWb1jJoPujPR2Kk7h+Iax+YwU33Xgzfn5+ng6n1WhOTjkvpTsrdi1t1vYNBgOVpe677/xUNiQkNOw7Of+YkWEjk1o4IiF8j68eqwghvI8U15pp9+7dTJkyhYqKCsdzERER9O7dG61Wy+bNm52GZQTw9/enS5cuhIeHExAQQH5+Plu3bqWsrAyoLsBdd911DBo0qFmxTZkyhfLychYuXOh4rmvXrnTr1g2AAQMGAPDOO+84FWcMBgMDBw6ksrKSzZs3o6oqZrOZ2267jc6dOzN06NA6tzd/fvVdeklJSaSmppKRkVHncrm5ueTm5hIfH0/nzp1Zv36947Pv3buXr7/++qxFvDMZDAa6du1KREQEQUFBFBcXs23bNgoLCwH473//y7fffst1113X4HXWeOmllxyFNa1Wy6BBgwgPD+fYsWMcPHiwzkmr//rXvzoV1hRFoWfPnsTHx3P06FF27tx51m1++OGHBAQEcMEFF3Dw4EEOHToEgM1m45lnnnEqrjX1b5ednc2kSZMcvyOA1NRUOnbsyMaNGykuLgZg3rx53Hfffbz33nvn/F1lZWU5FdYiIyMZMGAANpuNI0eOkJmZidnc8LsK65tfLyMjg7i4OHbt2lXrNa1W69i/bTYbe/furXMdBoOB1NRUAMxmc73dfX5+fqSkpADVJ/Hp6el1LhcQEEBSUvVJT2lpKQcPHqxzueDgYBISEoDqouPhw4drLWO328nOziYu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CxYs0L59++zL8fHx9rhd7eKhNi6c0+udd95xiPtqvffee/r+++9VWvrbF21/f3/dcMMNGjZsmEO7smET+/bt67B++vTpWr9+vcO6gwcPmlJ0q8rfrk+fPvb1BQUFDsXC48eP680337Qve3t7q3v37leMJzc3V9OnT1dKSop9XVhYmOLj43XbbbfZ1+Xn5yszM9PZ06xTLBaLGjZsyNAFAExDXql9zOy1VobeazCTp+aVa5tfq6LM2lEQKczMV2zza10dhkuUlJTIy+re157FamEo/EowI6f4+vpq7KNPa+NSQ0WFV/dDzuCgYJUWWVVUZO4PQYsLDW1aalHvP4bK6nPlczyWUqistDD16N7L1DiAusRT71WqhSHJZrjXiw6HqEUorlXRpEmTHIpNCxcuVIsWLdS9e3f17t1bjRs31qOPPipJ6ty5s71dfn6+rr/+evXv319t27bVlClTXJL0rVarnn/+eftyYWGhunbtqvj4eN16660aOXKkfZuXl5cmTZpU4zFeyoWfpyR16dJF/fr1080336y//OUvpnyey5Yt05133qnw8HDddtttGjhwoHr16qUOHTo4tGvZsqUklSu8nT17VgkJCWrfvr3uvvtudezYUa1bt9bWrVurHFtV/nZ//etfFRISYl9+5ZVXdP3116tnz56Ki4vTsWPH7NseeughtWjR4orxFBQUaMKECWrZsqViY2PVs2dP3XPPPbr11lv1j3/8w96uQYMGpvUq9DSGYSgjI4OhCwCYhrxSu5jda60MvddgJk/NK7ExsbL9WjsKIsbJEsVeG3vlhh7IarXKVo1D95nBKDFktVpdHUatYVZOad26tW6/9S79+OXVFcktXhaFhUXofI6503T89KWhVjcGqum1V54nsLjIprVLLBo7ZoLb/KgZqI089V4FgPuhuFZF7dq105IlS1S/fn37uuzsbH333Xf65ptvlJWVZV8/duxYNW3a1L588uRJffnll9q3b5/+/Oc/Oz30ntnGjRunxx9/3L5cWFio9evX66effrL/j8jHx0dvv/22unXr5pIYKzJs2DDdeOON9uWzZ8/qq6++0q5du9SrVy917drVtGPl5ORo06ZN+uyzz/Ttt986zLHWrVs3DRgwwL48b9489e/f375sGIZ2796tL774Qjt27DB1SMSr/dtFRUVpxYoVDvOi7dmzR6tXr9aZ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y7WZO3euQ5sZM2aUa5Obm+vQ5r333ivX5nLi4+Md2j744IMO23v06OGw3cfHx9i6dWuF+zp9+rRzH8j/Wrx4scO+R48efcm2eXl5RnFxcaViT01NLfdZ3HzzzYbNZrO3ef3118u1GThwoMN+xo8fX+66vdjF19rkyZMdtq9du7bccVJTU537oP7X1V5HTz755CX36UxclY194sSJDm379u3rsL1Dhw4O2z/88MNKfQ4AAABAbUbPNQAAAADldOnSRT/++KN69eoli8Vy2bZZWVl64IEHtGzZMof169atc1j++eef5e3tLYvFYn9ZrVYdPnzYod369esve7xevXqpYcOG9uWYmJhybYYMGSI/Pz/7cqtWrcq1qaiHXEpKiiZOnKhu3bqpcePGCggIsMfaqVOncu2PHTt22ViDgoI0YsQIh3Vt2rRxKhaz2Gy2cp/poEGDKjwfSZXuudahQweHa2TevHnq1auXJkyYoHfffVebN2+2DwsaEBAgq7XqU3/ff//9Dses6BoYPny4w/LF10B1fuZmX0c+Pj5VGsr0aowZM8bhb7Vq1SodPXpUknTw4EHt2LHDvq1+/foaNGhQjcYHAAAAuBLFNQAAAAAViouL06pVq3To0CG99dZbGjZsmFq2bHnJ9pMnT3ZYPn78+FUdNz09/bLbLx56rl69euXaXFxsCQgIKNembKi9MvPmzVObNm00bdo0bdy4URkZGSooKLhsLLm5uZfdHh0d7VDkczYWM2VnZ6uwsNBhXceOHU3bf8uWLR3m6LLZbPr22281ffp0jRo1Sl27dlWDBg3Uo0cPrVmzxpRjmnENXDyHmFmq4zqKiopSWFiYmWFeUdOmTXXvvffal202m31+uIuHhBw6dGiF1zUAAADgqSiuAQAAALis6OhojR49Wh9++KEOHDig9PR0vfbaa+UepicnJ+vs2bNVPl5+fv5lt1/cs8rLq/zXmsr2vvrll1/06KOPVrrIZRjGZbdXVBDx9vau1DFqg9mzZ2v58uXq27dvhfOqFRcXa82aNerZs2e5Ho5XozquATNU13XUpEmTqoR11caNG+ew/O6778pms2nx4sUO60eNGlWTYQEAAAAuV/XxOAAAAADUKZGRkRo/frwyMjI0ffp0h23nz59XSEiIpN8KAnv27LFv69Gjh+bNm3fF/V/c06smLFmyxKEnk5eXl55+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\",\n \"text/plain\": [\n \"<IPython.core.display.Image object>\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n }\n ],\n \"source\": [\n \"# 5.4 Semantic Alignment Visualization (Column-Level RBAC)\\n\",\n \"# Circle size = column coverage, Red triangles = SystemManager\\n\",\n \"import random\\n\",\n \"import math\\n\",\n \"import matplotlib.pyplot as plt\\n\",\n \"from matplotlib.colors import PowerNorm\\n\",\n \"from matplotlib import colormaps as cm\\n\",\n \"from collections import defaultdict\\n\",\n \"from matplotlib.lines import Line2D\\n\",\n \"import numpy as np\\n\",\n \"\\n\",\n \"sys.path.insert(0, str(project_root / \\\"plot\\\"))\\n\",\n \"from embedding_utils import cosine_similarity\\n\",\n \"\\n\",\n \"# Prepare records (recompute)\\n\",\n \"role_records = evaluator._prepare_role_records()\\n\",\n \"evaluator._compute_embeddings()\\n\",\n \"\\n\",\n \"# Build vis_records with is_system_manager\\n\",\n \"vis_records = []\\n\",\n \"for spec in role_records:\\n\",\n \" schema_emb = evaluator._schema_emb_dict.get(spec[\\\"schema_text\\\"])\\n\",\n \" role_emb = evaluator._role_emb_dict.get(spec[\\\"role_text\\\"])\\n\",\n \" \\n\",\n \" if schema_emb is not None and role_emb is not None:\\n\",\n \" raw_sim = cosine_similarity(role_emb, schema_emb)\\n\",\n \" similarity = max(0.0, min(1.0, (raw_sim + 1.0) / 2.0))\\n\",\n \" else:\\n\",\n \" similarity = 0.5\\n\",\n \" \\n\",\n \" total_cols = spec.get(\\\"total_columns\\\", 1)\\n\",\n \" accessible_cols = spec.get(\\\"accessible_columns\\\", 0)\\n\",\n \" col_coverage = accessible_cols / total_cols if total_cols > 0 else 0\\n\",\n \" \\n\",\n \" vis_records.append({\\n\",\n \" \\\"db_id\\\": spec[\\\"db_id\\\"],\\n\",\n \" \\\"role\\\": spec[\\\"role\\\"],\\n\",\n \" \\\"similarity\\\": similarity,\\n\",\n \" \\\"is_system_manager\\\": spec.get(\\\"is_system_manager\\\", False),\\n\",\n \" \\\"column_coverage\\\": col_coverage,\\n\",\n \" })\\n\",\n \"\\n\",\n \"sm_count = sum(1 for r in vis_records if r[\\\"is_system_manager\\\"])\\n\",\n \"print(f\\\"Total roles: {len(vis_records)}, SystemManager: {sm_count}\\\")\\n\",\n \"\\n\",\n \"# Plot function\\n\",\n \"def plot_semantic(records, output_path, title, dbs_per_panel=12):\\n\",\n \" db_to_entries = defaultdict(list)\\n\",\n \" sims = []\\n\",\n \" for rec in records:\\n\",\n \" sims.append(rec[\\\"similarity\\\"])\\n\",\n \" size = 90.0 + rec[\\\"column_coverage\\\"] * 220.0\\n\",\n \" db_to_entries[rec[\\\"db_id\\\"]].append({\\n\",\n \" \\\"sim\\\": rec[\\\"similarity\\\"], \\\"size\\\": size,\\n\",\n \" \\\"is_sm\\\": rec[\\\"is_system_manager\\\"],\\n\",\n \" })\\n\",\n \" \\n\",\n \" db_ids = sorted(db_to_entries.keys())\\n\",\n \" panels = [db_ids[i:i+dbs_per_panel] for i in range(0, len(db_ids), dbs_per_panel)]\\n\",\n \" \\n\",\n \" min_s, max_s = min(sims), max(sims)\\n\",\n \" if math.isclose(min_s, max_s): max_s = min_s + 1e-6\\n\",\n \" span = max_s - min_s\\n\",\n \" norm = PowerNorm(gamma=0.55, vmin=min_s, vmax=max_s)\\n\",\n \" cmap = cm[\\\"viridis\\\"]\\n\",\n \" \\n\",\n \" fig = plt.figure(figsize=(11, sum(0.35*len(p)+0.6 for p in panels)+2))\\n\",\n \" gs = fig.add_gridspec(len(panels), 1, height_ratios=[0.35*len(p)+0.6 for p in panels], hspace=0.18)\\n\",\n \" axes = []\\n\",\n \" \\n\",\n \" random.seed(42)\\n\",\n \" sm = plt.cm.ScalarMappable(norm=norm, cmap=cmap)\\n\",\n \" sm.set_array([])\\n\",\n \" \\n\",\n \" for pi, chunk in enumerate(panels):\\n\",\n \" ax = fig.add_subplot(gs[pi, 0])\\n\",\n \" axes.append(ax)\\n\",\n \" for ri, db in enumerate(chunk):\\n\",\n \" y = float(ri)\\n\",\n \" ax.axhline(y, color=\\\"#d0d0d0\\\", linestyle=\\\"--\\\", lw=0.9, zorder=0)\\n\",\n \" for e in db_to_entries[db]:\\n\",\n \" x = (e[\\\"sim\\\"] - min_s) / span + random.uniform(-0.012, 0.012)\\n\",\n \" if e[\\\"is_sm\\\"]:\\n\",\n \" ax.scatter(x, y, s=250, c=\\\"#d62728\\\", alpha=0.95,\\n\",\n \" edgecolors=\\\"#8c1c15\\\", lw=1.0, marker=\\\"^\\\", zorder=4)\\n\",\n \" else:\\n\",\n \" ax.scatter(x, y + random.uniform(-0.16, 0.16), s=e[\\\"size\\\"]*2.5,\\n\",\n \" c=[e[\\\"sim\\\"]], cmap=cmap, norm=norm, alpha=0.82,\\n\",\n \" edgecolors=\\\"#333\\\", lw=0.45, marker=\\\"o\\\")\\n\",\n \" ax.set_yticks(range(len(chunk)))\\n\",\n \" ax.set_yticklabels(chunk, fontsize=10, weight='bold')\\n\",\n \" ax.set_xlim(-0.05, 1.05)\\n\",\n \" ax.set_ylim(-0.6, len(chunk)-0.4)\\n\",\n \" ax.set_xticks(np.linspace(0, 1, 5))\\n\",\n \" ax.set_xticklabels([f\\\"{min_s + p*span:.2f}\\\" for p in np.linspace(0,1,5)], fontsize=9)\\n\",\n \" ax.grid(axis=\\\"x\\\", ls=\\\":\\\", lw=0.5, alpha=0.45)\\n\",\n \" \\n\",\n \" axes[-1].set_xlabel(\\\"Semantic similarity\\\", fontsize=12, weight='bold')\\n\",\n \" fig.suptitle(f\\\"{title}: Role-Schema Alignment\\\", fontsize=14, y=0.98, weight='bold')\\n\",\n \" cbar = fig.colorbar(sm, ax=axes, fraction=0.03, pad=0.015)\\n\",\n \" cbar.set_label(\\\"Similarity\\\", fontsize=10)\\n\",\n \" \\n\",\n \" legend = [\\n\",\n \" Line2D([0],[0], marker='^', color='w', markerfacecolor='#d62728',\\n\",\n \" markeredgecolor='#8c1c15', markersize=12, label='SystemManager'),\\n\",\n \" Line2D([0],[0], marker='o', color='w', markerfacecolor='#21918c',\\n\",\n \" markeredgecolor='#333', markersize=8, label='Other (size=col coverage)'),\\n\",\n \" ]\\n\",\n \" fig.legend(handles=legend, loc='upper right', fontsize=9)\\n\",\n \" plt.tight_layout(rect=[0,0,0.95,0.95])\\n\",\n \" fig.savefig(output_path, dpi=150, bbox_inches='tight')\\n\",\n \" plt.close(fig)\\n\",\n \" print(f\\\"Saved: {output_path}\\\")\\n\",\n \"\\n\",\n \"output_path = project_root / \\\"plot\\\" / \\\"column_level_rbac_semantic.png\\\"\\n\",\n \"plot_semantic(vis_records, output_path, \\\"Column-Level RBAC (BIRD)\\\")\\n\",\n \"from IPython.display import Image, display\\n\",\n \"display(Image(filename=str(output_path)))\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 13,\n \"id\": \"2dd807b4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Databases Requiring Review (1/11) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Deny%</th>\\n\",\n \" <th>AllAllow%</th>\\n\",\n \" <th>Coverage</th>\\n\",\n \" <th>CovStd</th>\\n\",\n \" <th>MaxOverlap</th>\\n\",\n \" <th>Issues</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>california_schools</td>\\n\",\n \" <td>63.1%</td>\\n\",\n \" <td>5.6%</td>\\n\",\n \" <td>50.8%</td>\\n\",\n \" <td>0.059</td>\\n\",\n \" <td>72.3%</td>\\n\",\n \" <td>all-deny queries found (1) - SystemManager coverage broken; Low role diversity</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Deny% AllAllow% Coverage CovStd MaxOverlap \\\\\\n\",\n \"0 california_schools 63.1% 5.6% 50.8% 0.059 72.3% \\n\",\n \"\\n\",\n \" Issues \\n\",\n \"0 all-deny queries found (1) - SystemManager coverage broken; Low role diversity \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"Issue Thresholds:\\n\",\n \" - Deny% < 5%: roles too permissive\\n\",\n \" - Coverage > 85%: most roles have near-full access\\n\",\n \" - CovStd < 0.065: roles have similar coverage (low diversity)\\n\",\n \" - MaxOverlap >80%: redundant roles with similar policies\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 5.5 Export Problematic Databases as DataFrame\\n\",\n \"if report.databases_with_issues:\\n\",\n \" issues_data = []\\n\",\n \" for db_id in report.databases_with_issues:\\n\",\n \" row = {\\\"Database\\\": db_id}\\n\",\n \" \\n\",\n \" if db_id in report.deny_rate_metrics:\\n\",\n \" m = report.deny_rate_metrics[db_id]\\n\",\n \" row[\\\"Deny%\\\"] = f\\\"{m.deny_rate:.1f}%\\\"\\n\",\n \" row[\\\"AllAllow%\\\"] = f\\\"{m.all_allow_pct:.1f}%\\\"\\n\",\n \" \\n\",\n \" if db_id in report.semantic_metrics:\\n\",\n \" m = report.semantic_metrics[db_id]\\n\",\n \" row[\\\"Coverage\\\"] = f\\\"{m.avg_coverage:.1%}\\\" # Source of \\\"Most roles too permissive\\\" (>90%)\\n\",\n \" row[\\\"CovStd\\\"] = f\\\"{m.coverage_std:.3f}\\\" # Source of \\\"Low role diversity\\\" (<0.1)\\n\",\n \" \\n\",\n \" if db_id in report.overlap_metrics:\\n\",\n \" m = report.overlap_metrics[db_id]\\n\",\n \" row[\\\"MaxOverlap\\\"] = f\\\"{m.max_overlap:.1%}\\\" # Source of \\\"High max overlap\\\" (>70%)\\n\",\n \" \\n\",\n \" row[\\\"Issues\\\"] = \\\"; \\\".join(report.all_issues.get(db_id, []))\\n\",\n \" issues_data.append(row)\\n\",\n \" \\n\",\n \" issues_df = pd.DataFrame(issues_data)\\n\",\n \" print(f\\\"=== Databases Requiring Review ({len(issues_df)}/{report.total_databases}) ===\\\")\\n\",\n \" \\n\",\n \" # Display with full column width for Issues column\\n\",\n \" pd.set_option('display.max_colwidth', None)\\n\",\n \" pd.set_option('display.max_rows', None)\\n\",\n \" display(issues_df)\\n\",\n \" pd.reset_option('display.max_colwidth')\\n\",\n \" pd.reset_option('display.max_rows')\\n\",\n \" \\n\",\n \" print(\\\"\\\\nIssue Thresholds:\\\")\\n\",\n \" print(\\\" - Deny% < 5%: roles too permissive\\\")\\n\",\n \" print(\\\" - Coverage > 85%: most roles have near-full access\\\")\\n\",\n \" print(\\\" - CovStd < 0.065: roles have similar coverage (low diversity)\\\")\\n\",\n \" print(\\\" - MaxOverlap >80%: redundant roles with similar policies\\\")\\n\",\n \"\\n\",\n \"else:\\n\",\n \" print(\\\"[OK] All databases pass quality checks!\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"7586c455\",\n \"metadata\": {},\n \"source\": [\n \"## 6. Regenerate Roles for Problematic Databases\\n\",\n \"\\n\",\n \"Human-in-the-loop workflow for fixing quality issues:\\n\",\n \"\\n\",\n \"| Step | Cell | Description |\\n\",\n \"|------|------|-------------|\\n\",\n \"| 1 | 6.1 | Select database, view schema and current roles |\\n\",\n \"| 2 | 6.2 | Write regeneration hints (edit manually) |\\n\",\n \"| 3 | 6.3 | Execute regeneration with LLM |\\n\",\n \"| 4 | 6.4 | Preview and compare OLD vs NEW |\\n\",\n \"| 5 | 6.4.1 | Select & combine roles from OLD and NEW |\\n\",\n \"| 6 | 6.5 | Apply changes if satisfied |\\n\",\n \"| 7 | 6.6 | Regenerate RBAC Dataset with updated roles |\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 32,\n \"id\": \"70f25858\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"Database: formula_1\\n\",\n \"======================================================================\\n\",\n \"\\n\",\n \"=== Schema (13 tables, 94 columns) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Table</th>\\n\",\n \" <th>Column</th>\\n\",\n \" <th>Type</th>\\n\",\n \" <th>PK</th>\\n\",\n \" <th>FK</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>circuitId</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td>Yes</td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>circuitRef</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>name</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>location</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>circuits</td>\\n\",\n \" <td>country</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>...</th>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" <td>...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>89</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLap</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>90</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>rank</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>91</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLapTime</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>92</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>fastestLapSpeed</td>\\n\",\n \" <td>TEXT</td>\\n\",\n \" <td></td>\\n\",\n \" <td></td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>93</th>\\n\",\n \" <td>results</td>\\n\",\n \" <td>statusId</td>\\n\",\n \" <td>INTEGER</td>\\n\",\n \" <td></td>\\n\",\n \" <td>status.statusId</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"<p>94 rows × 5 columns</p>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Table Column Type PK FK\\n\",\n \"0 circuits circuitId INTEGER Yes \\n\",\n \"1 circuits circuitRef TEXT \\n\",\n \"2 circuits name TEXT \\n\",\n \"3 circuits location TEXT \\n\",\n \"4 circuits country TEXT \\n\",\n \".. ... ... ... ... ...\\n\",\n \"89 results fastestLap INTEGER \\n\",\n \"90 results rank INTEGER \\n\",\n \"91 results fastestLapTime TEXT \\n\",\n \"92 results fastestLapSpeed TEXT \\n\",\n \"93 results statusId INTEGER status.statusId\\n\",\n \"\\n\",\n \"[94 rows x 5 columns]\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Current Roles (5) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Columns</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap times, pit stops, and qualifying results</td>\\n\",\n \" <td>9</td>\\n\",\n \" <td>65 (*)</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date, time]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>StandingsManager</td>\\n\",\n \" <td>Manages championship standings and constructor/driver points calculations</td>\\n\",\n \" <td>7</td>\\n\",\n \" <td>43 (*)</td>\\n\",\n \" <td>constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; results: [resultId, raceId, driverId, constructorId, position, positionOrder, points]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>MediaCoordinator</td>\\n\",\n \" <td>Provides public information for media and fan engagement</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>33 (*)</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, constructorRef, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [*]; races: [raceId, year, round, circuitId, name, date, time, url]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal details including date of birth</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>32 (*)</td>\\n\",\n \" <td>drivers: [*]; races: [raceId, year, round, name, date]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, statusId]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintenance and data integrity</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>94 (*)</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*]</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Role \\\\\\n\",\n \"0 RaceAnalyst \\n\",\n \"1 StandingsManager \\n\",\n \"2 MediaCoordinator \\n\",\n \"3 DriverManager \\n\",\n \"4 SystemManager \\n\",\n \"\\n\",\n \" Description \\\\\\n\",\n \"0 Analyzes race performance data including lap times, pit stops, and qualifying results \\n\",\n \"1 Manages championship standings and constructor/driver points calculations \\n\",\n \"2 Provides public information for media and fan engagement \\n\",\n \"3 Manages driver information and personal details including date of birth \\n\",\n \"4 Full administrative access for system maintenance and data integrity \\n\",\n \"\\n\",\n \" Tables Columns \\\\\\n\",\n \"0 9 65 (*) \\n\",\n \"1 7 43 (*) \\n\",\n \"2 6 33 (*) \\n\",\n \"3 4 32 (*) \\n\",\n \"4 13 94 (*) \\n\",\n \"\\n\",\n \" Policy \\n\",\n \"0 circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date, time]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]; status: [statusId, status] \\n\",\n \"1 constructors: [constructorId, constructorRef, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; results: [resultId, raceId, driverId, constructorId, position, positionOrder, points] \\n\",\n \"2 circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, constructorRef, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [*]; races: [raceId, year, round, circuitId, name, date, time, url]; status: [statusId, status] \\n\",\n \"3 drivers: [*]; races: [raceId, year, round, name, date]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, statusId]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins] \\n\",\n \"4 circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*] \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Role x Table Matrix (column counts, * expanded) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<style type=\\\"text/css\\\">\\n\",\n \"#T_3c946_row0_col0, #T_3c946_row0_col1, #T_3c946_row0_col2, #T_3c946_row0_col3, #T_3c946_row0_col6, #T_3c946_row0_col7, #T_3c946_row0_col8, #T_3c946_row0_col11, #T_3c946_row0_col12, #T_3c946_row1_col1, #T_3c946_row1_col2, #T_3c946_row1_col4, #T_3c946_row1_col6, #T_3c946_row1_col7, #T_3c946_row1_col8, #T_3c946_row1_col10, #T_3c946_row2_col1, #T_3c946_row2_col2, #T_3c946_row2_col4, #T_3c946_row2_col11, #T_3c946_row3_col0, #T_3c946_row3_col6, #T_3c946_row3_col7, #T_3c946_row3_col8, #T_3c946_row3_col11, #T_3c946_row3_col12 {\\n\",\n \" background-color: #f7fbff;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col4, #T_3c946_row2_col5, #T_3c946_row2_col7, #T_3c946_row2_col9, #T_3c946_row3_col2, #T_3c946_row3_col4, #T_3c946_row3_col5, #T_3c946_row3_col10, #T_3c946_row4_col2, #T_3c946_row4_col4, #T_3c946_row4_col7 {\\n\",\n \" background-color: #99c7e0;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col5, #T_3c946_row2_col8, #T_3c946_row4_col0, #T_3c946_row4_col5, #T_3c946_row4_col8 {\\n\",\n \" background-color: #6aaed6;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col9, #T_3c946_row1_col3, #T_3c946_row3_col1, #T_3c946_row4_col1, #T_3c946_row4_col3 {\\n\",\n \" background-color: #bdd7ec;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row0_col10 {\\n\",\n \" background-color: #4695c8;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row1_col0, #T_3c946_row1_col5, #T_3c946_row1_col9, #T_3c946_row4_col9 {\\n\",\n \" background-color: #82bbdb;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row1_col11, #T_3c946_row1_col12, #T_3c946_row2_col12, #T_3c946_row4_col11, #T_3c946_row4_col12 {\\n\",\n \" background-color: #e1edf8;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col0, #T_3c946_row2_col6, #T_3c946_row3_col9, #T_3c946_row4_col6 {\\n\",\n \" background-color: #abd0e6;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col3, #T_3c946_row3_col3 {\\n\",\n \" background-color: #ccdff1;\\n\",\n \" color: #000000;\\n\",\n \"}\\n\",\n \"#T_3c946_row2_col10 {\\n\",\n \" background-color: #083e81;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"#T_3c946_row4_col10 {\\n\",\n \" background-color: #08306b;\\n\",\n \" color: #f1f1f1;\\n\",\n \"}\\n\",\n \"</style>\\n\",\n \"<table id=\\\"T_3c946\\\">\\n\",\n \" <thead>\\n\",\n \" <tr>\\n\",\n \" <th class=\\\"index_name level0\\\" >Table</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col0\\\" class=\\\"col_heading level0 col0\\\" >circuits</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col1\\\" class=\\\"col_heading level0 col1\\\" >constructorResults</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col2\\\" class=\\\"col_heading level0 col2\\\" >constructorStandings</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col3\\\" class=\\\"col_heading level0 col3\\\" >constructors</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col4\\\" class=\\\"col_heading level0 col4\\\" >driverStandings</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col5\\\" class=\\\"col_heading level0 col5\\\" >drivers</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col6\\\" class=\\\"col_heading level0 col6\\\" >lapTimes</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col7\\\" class=\\\"col_heading level0 col7\\\" >pitStops</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col8\\\" class=\\\"col_heading level0 col8\\\" >qualifying</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col9\\\" class=\\\"col_heading level0 col9\\\" >races</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col10\\\" class=\\\"col_heading level0 col10\\\" >results</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col11\\\" class=\\\"col_heading level0 col11\\\" >seasons</th>\\n\",\n \" <th id=\\\"T_3c946_level0_col12\\\" class=\\\"col_heading level0 col12\\\" >status</th>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th class=\\\"index_name level0\\\" >Role</th>\\n\",\n \" <th class=\\\"blank col0\\\" > </th>\\n\",\n \" <th class=\\\"blank col1\\\" > </th>\\n\",\n \" <th class=\\\"blank col2\\\" > </th>\\n\",\n \" <th class=\\\"blank col3\\\" > </th>\\n\",\n \" <th class=\\\"blank col4\\\" > </th>\\n\",\n \" <th class=\\\"blank col5\\\" > </th>\\n\",\n \" <th class=\\\"blank col6\\\" > </th>\\n\",\n \" <th class=\\\"blank col7\\\" > </th>\\n\",\n \" <th class=\\\"blank col8\\\" > </th>\\n\",\n \" <th class=\\\"blank col9\\\" > </th>\\n\",\n \" <th class=\\\"blank col10\\\" > </th>\\n\",\n \" <th class=\\\"blank col11\\\" > </th>\\n\",\n \" <th class=\\\"blank col12\\\" > </th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row0\\\" class=\\\"row_heading level0 row0\\\" >DriverManager</th>\\n\",\n \" <td id=\\\"T_3c946_row0_col0\\\" class=\\\"data row0 col0\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col1\\\" class=\\\"data row0 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col2\\\" class=\\\"data row0 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col3\\\" class=\\\"data row0 col3\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col4\\\" class=\\\"data row0 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col5\\\" class=\\\"data row0 col5\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col6\\\" class=\\\"data row0 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col7\\\" class=\\\"data row0 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col8\\\" class=\\\"data row0 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col9\\\" class=\\\"data row0 col9\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col10\\\" class=\\\"data row0 col10\\\" >11.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col11\\\" class=\\\"data row0 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row0_col12\\\" class=\\\"data row0 col12\\\" >0.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row1\\\" class=\\\"row_heading level0 row1\\\" >MediaCoordinator</th>\\n\",\n \" <td id=\\\"T_3c946_row1_col0\\\" class=\\\"data row1 col0\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col1\\\" class=\\\"data row1 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col2\\\" class=\\\"data row1 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col3\\\" class=\\\"data row1 col3\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col4\\\" class=\\\"data row1 col4\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col5\\\" class=\\\"data row1 col5\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col6\\\" class=\\\"data row1 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col7\\\" class=\\\"data row1 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col8\\\" class=\\\"data row1 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col9\\\" class=\\\"data row1 col9\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col10\\\" class=\\\"data row1 col10\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col11\\\" class=\\\"data row1 col11\\\" >2.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row1_col12\\\" class=\\\"data row1 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row2\\\" class=\\\"row_heading level0 row2\\\" >RaceAnalyst</th>\\n\",\n \" <td id=\\\"T_3c946_row2_col0\\\" class=\\\"data row2 col0\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col1\\\" class=\\\"data row2 col1\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col2\\\" class=\\\"data row2 col2\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col3\\\" class=\\\"data row2 col3\\\" >4.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col4\\\" class=\\\"data row2 col4\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col5\\\" class=\\\"data row2 col5\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col6\\\" class=\\\"data row2 col6\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col7\\\" class=\\\"data row2 col7\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col8\\\" class=\\\"data row2 col8\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col9\\\" class=\\\"data row2 col9\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col10\\\" class=\\\"data row2 col10\\\" >17.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col11\\\" class=\\\"data row2 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row2_col12\\\" class=\\\"data row2 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row3\\\" class=\\\"row_heading level0 row3\\\" >StandingsManager</th>\\n\",\n \" <td id=\\\"T_3c946_row3_col0\\\" class=\\\"data row3 col0\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col1\\\" class=\\\"data row3 col1\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col2\\\" class=\\\"data row3 col2\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col3\\\" class=\\\"data row3 col3\\\" >4.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col4\\\" class=\\\"data row3 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col5\\\" class=\\\"data row3 col5\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col6\\\" class=\\\"data row3 col6\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col7\\\" class=\\\"data row3 col7\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col8\\\" class=\\\"data row3 col8\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col9\\\" class=\\\"data row3 col9\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col10\\\" class=\\\"data row3 col10\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col11\\\" class=\\\"data row3 col11\\\" >0.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row3_col12\\\" class=\\\"data row3 col12\\\" >0.000000</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th id=\\\"T_3c946_level0_row4\\\" class=\\\"row_heading level0 row4\\\" >SystemManager</th>\\n\",\n \" <td id=\\\"T_3c946_row4_col0\\\" class=\\\"data row4 col0\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col1\\\" class=\\\"data row4 col1\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col2\\\" class=\\\"data row4 col2\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col3\\\" class=\\\"data row4 col3\\\" >5.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col4\\\" class=\\\"data row4 col4\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col5\\\" class=\\\"data row4 col5\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col6\\\" class=\\\"data row4 col6\\\" >6.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col7\\\" class=\\\"data row4 col7\\\" >7.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col8\\\" class=\\\"data row4 col8\\\" >9.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col9\\\" class=\\\"data row4 col9\\\" >8.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col10\\\" class=\\\"data row4 col10\\\" >18.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col11\\\" class=\\\"data row4 col11\\\" >2.000000</td>\\n\",\n \" <td id=\\\"T_3c946_row4_col12\\\" class=\\\"data row4 col12\\\" >2.000000</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\"\n ],\n \"text/plain\": [\n \"<pandas.io.formats.style.Styler at 0x7f5045bdae00>\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"[SUMMARY] 1 database(s) selected: ['formula_1']\\n\",\n \"[NEXT] Edit hints in Cell 6.2, then run Cell 6.3\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.1 Select Database(s) & View Current State\\n\",\n \"# Supports single db (str) or multiple dbs (list)\\n\",\n \"\\n\",\n \"TARGET_DBS = [\\\"formula_1\\\"] # Single: \\\"toxicology\\\" or Multiple: [\\\"toxicology\\\", \\\"california_schools\\\"]\\n\",\n \"\\n\",\n \"# Normalize to list\\n\",\n \"if isinstance(TARGET_DBS, str):\\n\",\n \" TARGET_DBS = [TARGET_DBS]\\n\",\n \"\\n\",\n \"# Load current role assignments\\n\",\n \"with open(ROLE_ASSIGNMENTS_PATH, 'r') as f:\\n\",\n \" all_assignments = json.load(f)\\n\",\n \"\\n\",\n \"available_dbs = set(all_assignments.get('assignments', {}).keys())\\n\",\n \"valid_target_dbs = [db for db in TARGET_DBS if db in available_dbs]\\n\",\n \"invalid_dbs = [db for db in TARGET_DBS if db not in available_dbs]\\n\",\n \"\\n\",\n \"if invalid_dbs:\\n\",\n \" print(f\\\"[WARN] Databases not found: {invalid_dbs}\\\")\\n\",\n \" print(f\\\"Available: {sorted(available_dbs)}\\\\n\\\")\\n\",\n \"\\n\",\n \"# Store schemas for later use\\n\",\n \"target_schemas = {}\\n\",\n \"\\n\",\n \"for TARGET_DB in valid_target_dbs:\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"Database: {TARGET_DB}\\\")\\n\",\n \" print(f\\\"{'='*70}\\\")\\n\",\n \" \\n\",\n \" # --- Schema Summary ---\\n\",\n \" db_path = bird_db_dir / TARGET_DB / f\\\"{TARGET_DB}.sqlite\\\"\\n\",\n \" if db_path.exists():\\n\",\n \" target_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" target_schemas[TARGET_DB] = target_schema\\n\",\n \" \\n\",\n \" schema_rows = []\\n\",\n \" for table, info in target_schema.items():\\n\",\n \" for col in info['columns']:\\n\",\n \" schema_rows.append({\\n\",\n \" \\\"Table\\\": table,\\n\",\n \" \\\"Column\\\": col['name'],\\n\",\n \" \\\"Type\\\": col['type'],\\n\",\n \" \\\"PK\\\": \\\"Yes\\\" if col['pk'] else \\\"\\\",\\n\",\n \" \\\"FK\\\": col.get('fk', '') or \\\"\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" schema_df = pd.DataFrame(schema_rows)\\n\",\n \" print(f\\\"\\\\n=== Schema ({len(target_schema)} tables, {len(schema_df)} columns) ===\\\")\\n\",\n \" display(schema_df)\\n\",\n \" else:\\n\",\n \" print(f\\\"[ERROR] Database not found: {db_path}\\\")\\n\",\n \" target_schemas[TARGET_DB] = None\\n\",\n \" continue\\n\",\n \"\\n\",\n \" # --- Current Roles ---\\n\",\n \" if TARGET_DB in all_assignments.get('assignments', {}):\\n\",\n \" current_roles = all_assignments['assignments'][TARGET_DB]\\n\",\n \" \\n\",\n \" # Set display options for full content\\n\",\n \" pd.set_option('display.max_colwidth', None)\\n\",\n \" \\n\",\n \" roles_rows = []\\n\",\n \" for role_info in current_roles:\\n\",\n \" policy = role_info.get('policy', {})\\n\",\n \" total_access = count_policy_columns(policy, target_schema)\\n\",\n \" has_wildcard = has_wildcard_policy(policy)\\n\",\n \" cols_display = f\\\"{total_access}\\\" + (\\\" (*)\\\" if has_wildcard else \\\"\\\")\\n\",\n \" # Full policy string\\n\",\n \" policy_str = \\\"; \\\".join([\\n\",\n \" f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" \\n\",\n \" for t, c in policy.items()\\n\",\n \" ])\\n\",\n \" roles_rows.append({\\n\",\n \" \\\"Role\\\": role_info['role'],\\n\",\n \" \\\"Description\\\": role_info.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Columns\\\": cols_display,\\n\",\n \" \\\"Policy\\\": policy_str\\n\",\n \" })\\n\",\n \" \\n\",\n \" roles_df = pd.DataFrame(roles_rows)\\n\",\n \" print(f\\\"\\\\n=== Current Roles ({len(current_roles)}) ===\\\")\\n\",\n \" display(roles_df)\\n\",\n \" \\n\",\n \" pd.reset_option('display.max_colwidth')\\n\",\n \" \\n\",\n \" # Policy detail matrix\\n\",\n \" policy_rows = []\\n\",\n \" for role_info in current_roles:\\n\",\n \" policy = role_info.get('policy', {})\\n\",\n \" for table, cols in policy.items():\\n\",\n \" num_cols = count_policy_columns({table: cols}, target_schema)\\n\",\n \" policy_rows.append({\\n\",\n \" \\\"Role\\\": role_info['role'],\\n\",\n \" \\\"Table\\\": table,\\n\",\n \" \\\"NumCols\\\": num_cols\\n\",\n \" })\\n\",\n \" \\n\",\n \" if policy_rows:\\n\",\n \" policy_df = pd.DataFrame(policy_rows)\\n\",\n \" pivot = policy_df.pivot_table(index='Role', columns='Table', values='NumCols', fill_value=0)\\n\",\n \" print(\\\"\\\\n=== Role x Table Matrix (column counts, * expanded) ===\\\")\\n\",\n \" display(pivot.style.background_gradient(cmap='Blues', axis=None))\\n\",\n \" \\n\",\n \" # Quality issues\\n\",\n \" if 'report' in dir() and TARGET_DB in report.all_issues:\\n\",\n \" print(f\\\"\\\\n=== Quality Issues ===\\\")\\n\",\n \" issues = report.all_issues[TARGET_DB]\\n\",\n \" if TARGET_DB in report.deny_rate_metrics:\\n\",\n \" m = report.deny_rate_metrics[TARGET_DB]\\n\",\n \" issues_df = pd.DataFrame([{\\n\",\n \" \\\"Deny Rate\\\": f\\\"{m.deny_rate:.1f}%\\\",\\n\",\n \" \\\"All-Allow %\\\": f\\\"{m.all_allow_pct:.1f}%\\\",\\n\",\n \" \\\"Issues\\\": \\\"; \\\".join(issues)\\n\",\n \" }])\\n\",\n \" display(issues_df)\\n\",\n \" else:\\n\",\n \" print(f\\\"[ERROR] No roles found for {TARGET_DB}\\\")\\n\",\n \"\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"[SUMMARY] {len(valid_target_dbs)} database(s) selected: {valid_target_dbs}\\\")\\n\",\n \" print(\\\"[NEXT] Edit hints in Cell 6.2, then run Cell 6.3\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[INFO] No databases selected. Edit TARGET_DBS above and re-run.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 34,\n \"id\": \"f8416bc4\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Applying shared hint to 1 database(s) ===\\n\",\n \"\\n\",\n \" ✓ formula_1: 13 tables, 94 columns\\n\",\n \"\\n\",\n \"[NEXT] Run Cell 6.3 to regenerate all 1 database(s)\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.2 Write Regeneration Hints (Human-in-the-Loop)\\n\",\n \"# Write ONE shared hint for all selected databases (they have similar issues)\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EDIT HERE: Shared problem description for all selected DBs\\n\",\n \"# Supports {db}, {num_tables}, {num_cols} placeholders\\n\",\n \"# ============================================================\\n\",\n \"# PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"# [PROBLEM]\\n\",\n \"# Low deny rate - almost all queries are allowed by all roles (roles too permissive)\\n\",\n \"# Low diversity - all roles have similar access patterns (coverage_std too low)\\n\",\n \"\\n\",\n \"\\n\",\n \"# [ROOT CAUSE]\\n\",\n \"# - Most roles can access most/all columns, so very few queries get denied\\n\",\n \"# - This defeats the purpose of RBAC - no meaningful access control\\n\",\n \"\\n\",\n \"# [DIRECTION]\\n\",\n \"# - Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"# - Create restrictive roles that only access specific subsets of data\\n\",\n \"# - Each role should have a clear, limited scope\\n\",\n \"# - Some roles should be restricted to fewer tables\\n\",\n \"# - Do not frequently giving non-admin roles \\\"*\\\" (all columns) access\\n\",\n \"# - Create clear functional boundaries between roles\\n\",\n \"# \\\"\\\"\\\"\\n\",\n \"\\n\",\n \"# All-denied cases - even SystemManager role cannot access some columns\\n\",\n \"# - Ensure SystemManager role has full access to all tables and columns\\n\",\n \"\\n\",\n \"\\n\",\n \"PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"[PROBLEM]\\n\",\n \"Low semantic alignment - roles do not align well with schema\\n\",\n \"All-denied cases - even SystemManager role cannot access some columns\\n\",\n \"\\n\",\n \"[ROOT CAUSE]\\n\",\n \"- This defeats the purpose of RBAC - no meaningful access control\\n\",\n \"\\n\",\n \"[DIRECTION]\\n\",\n \"- Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"- Ensure SystemManager role has full access to all tables and columns\\n\",\n \"- Review other roles to ensure they align with specific functional areas\\n\",\n \"- Each role should have a clear, limited scope\\n\",\n \"- Create meaningful semantic role names and descriptions that reflect their access patterns\\n\",\n \"\\\"\\\"\\\"\\n\",\n \"\\n\",\n \"\\n\",\n \"# PROBLEM_TO_FIX = \\\"\\\"\\\"\\n\",\n \"# [PROBLEM]\\n\",\n \"# Low diversity - all roles have similar access patterns (coverage_std too low)\\n\",\n \"\\n\",\n \"# [ROOT CAUSE]\\n\",\n \"# - This defeats the purpose of RBAC - no meaningful access control differentiation\\n\",\n \"\\n\",\n \"# [DIRECTION]\\n\",\n \"# - Database: {db} ({num_tables} tables, {num_cols} columns)\\n\",\n \"# - Create restrictive roles that only access specific subsets of data\\n\",\n \"# - Each role should have a clear, limited scope\\n\",\n \"# - Some roles should be restricted to fewer tables\\n\",\n \"# - Do not frequently giving non-admin roles \\\"*\\\" (all columns) access\\n\",\n \"# - Create clear functional boundaries between roles\\n\",\n \"# \\\"\\\"\\\"\\n\",\n \"\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# Auto-apply to all selected databases (no need to edit below)\\n\",\n \"# ============================================================\\n\",\n \"REGENERATION_HINTS = {}\\n\",\n \"\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"=== Applying shared hint to {len(valid_target_dbs)} database(s) ===\\\\n\\\")\\n\",\n \" for db in valid_target_dbs:\\n\",\n \" schema = target_schemas.get(db, {})\\n\",\n \" num_tables = len(schema)\\n\",\n \" num_cols = sum(len(t['columns']) for t in schema.values())\\n\",\n \" \\n\",\n \" # Fill placeholders\\n\",\n \" REGENERATION_HINTS[db] = PROBLEM_TO_FIX.format(\\n\",\n \" db=db, num_tables=num_tables, num_cols=num_cols\\n\",\n \" )\\n\",\n \" print(f\\\" ✓ {db}: {num_tables} tables, {num_cols} columns\\\")\\n\",\n \"\\n\",\n \" print(f\\\"\\\\n[NEXT] Run Cell 6.3 to regenerate all {len(valid_target_dbs)} database(s)\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[INFO] No databases selected. Run Cell 6.1 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 68,\n \"id\": \"91109a7c\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"Regenerating roles for 1 database(s)...\\n\",\n \"============================================================\\n\",\n \"\\n\",\n \"[formula_1] Generating...\\n\",\n \" ✓ 5 roles (5 valid)\\n\",\n \"\\n\",\n \"============================================================\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Status</th>\\n\",\n \" <th>Roles</th>\\n\",\n \" <th>Valid</th>\\n\",\n \" <th>Tokens</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>success</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>3058</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Status Roles Valid Tokens\\n\",\n \"0 formula_1 success 5 5 3058\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"[OK] All databases regenerated. Run Cell 6.4 to preview.\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.3 Execute Regeneration with Hints (supports multiple databases)\\n\",\n \"\\n\",\n \"def regenerate_single_database(db_name: str, hint: str) -> dict:\\n\",\n \" \\\"\\\"\\\"Regenerate roles for a single database with guidance hints.\\\"\\\"\\\"\\n\",\n \" db_path = bird_db_dir / db_name / f\\\"{db_name}.sqlite\\\"\\n\",\n \" \\n\",\n \" if not db_path.exists():\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": \\\"Database not found\\\"}\\n\",\n \" \\n\",\n \" try:\\n\",\n \" db_schema = get_schema_from_sqlite(str(db_path))\\n\",\n \" db_schema_text = format_schema_for_prompt(db_schema, db_name)\\n\",\n \" \\n\",\n \" enhanced_system_prompt = f\\\"{COLUMN_LEVEL_SYSTEM_PROMPT}\\\\n\\\\n[OPTIMIZATION GUIDANCE]\\\\n{hint}\\\"\\n\",\n \" user_prompt = COLUMN_LEVEL_USER_PROMPT_TEMPLATE.format(schema_content=db_schema_text)\\n\",\n \" \\n\",\n \" response = oracle.query(\\n\",\n \" prompt_sys=enhanced_system_prompt,\\n\",\n \" prompt_user=user_prompt,\\n\",\n \" temp=0.3,\\n\",\n \" top_p=0.9,\\n\",\n \" max_completion_tokens=DEFAULT_MAX_TOKENS\\n\",\n \" )\\n\",\n \" \\n\",\n \" llm_response = response['answer']\\n\",\n \" usage = response.get('usage', {})\\n\",\n \" roles = parse_column_level_roles(llm_response)\\n\",\n \" \\n\",\n \" if not roles:\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"parse_error\\\", \\n\",\n \" \\\"error\\\": \\\"Failed to parse LLM response\\\", \\\"raw_response\\\": llm_response[:500]}\\n\",\n \" \\n\",\n \" validation = validate_roles_against_schema(roles, str(db_path))\\n\",\n \" assignments = [{\\\"role\\\": r[\\\"role\\\"], \\\"description\\\": r.get(\\\"description\\\", \\\"\\\"), \\n\",\n \" \\\"policy\\\": r.get(\\\"columns\\\", {})} for r in roles]\\n\",\n \" \\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"success\\\", \\\"roles_count\\\": len(assignments),\\n\",\n \" \\\"valid_roles\\\": validation['valid_roles'], \\\"all_valid\\\": validation['all_valid'],\\n\",\n \" \\\"assignments\\\": assignments, \\\"usage\\\": usage, \\\"raw_response\\\": llm_response}\\n\",\n \" except Exception as e:\\n\",\n \" return {\\\"database\\\": db_name, \\\"status\\\": \\\"error\\\", \\\"error\\\": str(e)}\\n\",\n \"\\n\",\n \"# Execute for all target databases\\n\",\n \"if valid_target_dbs:\\n\",\n \" print(f\\\"Regenerating roles for {len(valid_target_dbs)} database(s)...\\\")\\n\",\n \" print(\\\"=\\\"*60)\\n\",\n \"\\n\",\n \" regenerated_results = {} # Store all results\\n\",\n \" all_success = True\\n\",\n \"\\n\",\n \" for db_name in valid_target_dbs:\\n\",\n \" hint = REGENERATION_HINTS.get(db_name, PROBLEM_TO_FIX)\\n\",\n \" print(f\\\"\\\\n[{db_name}] Generating...\\\")\\n\",\n \" \\n\",\n \" result = regenerate_single_database(db_name, hint)\\n\",\n \" regenerated_results[db_name] = result\\n\",\n \" \\n\",\n \" if result[\\\"status\\\"] == \\\"success\\\":\\n\",\n \" print(f\\\" ✓ {result['roles_count']} roles ({result['valid_roles']} valid)\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" ✗ {result.get('error', result['status'])}\\\")\\n\",\n \" all_success = False\\n\",\n \"\\n\",\n \" # Summary table\\n\",\n \" print(\\\"\\\\n\\\" + \\\"=\\\"*60)\\n\",\n \" result_df = pd.DataFrame([{\\n\",\n \" \\\"Database\\\": r[\\\"database\\\"],\\n\",\n \" \\\"Status\\\": r[\\\"status\\\"],\\n\",\n \" \\\"Roles\\\": r.get(\\\"roles_count\\\", \\\"N/A\\\"),\\n\",\n \" \\\"Valid\\\": r.get(\\\"valid_roles\\\", \\\"N/A\\\"),\\n\",\n \" \\\"Tokens\\\": r.get(\\\"usage\\\", {}).get(\\\"total_tokens\\\", \\\"N/A\\\")\\n\",\n \" } for r in regenerated_results.values()])\\n\",\n \" display(result_df)\\n\",\n \"\\n\",\n \" if all_success:\\n\",\n \" print(\\\"\\\\n[OK] All databases regenerated. Run Cell 6.4 to preview.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"\\\\n[WARN] Some databases failed. Check errors above.\\\")\\n\",\n \"else:\\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"[INFO] No databases selected. Run Cell 6.1 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 69,\n \"id\": \"a2b0ba98\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"======================================================================\\n\",\n \"Database: formula_1\\n\",\n \"======================================================================\\n\",\n \"\\n\",\n \"=== NEW Roles (5) ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Cols</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintenance and data management</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>94 (*)</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap times, pit stops, and qualifying results</td>\\n\",\n \" <td>8</td>\\n\",\n \" <td>61</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; lapTimes: [raceId, driverId, lap, position, time, milliseconds]; pitStops: [raceId, driverId, stop, lap, time, duration, milliseconds]; qualifying: [qualifyId, raceId, driverId, constructorId, number, position, q1, q2, q3]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>StandingsManager</td>\\n\",\n \" <td>Manages and monitors championship standings for drivers and constructors</td>\\n\",\n \" <td>7</td>\\n\",\n \" <td>42</td>\\n\",\n \" <td>constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, name, date]; constructorResults: [constructorResultsId, raceId, constructorId, points, status]; constructorStandings: [constructorStandingsId, raceId, constructorId, points, position, positionText, wins]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, points, position, positionText, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>PublicRelations</td>\\n\",\n \" <td>Accesses public information about circuits, drivers, constructors, and race schedules</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>31</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [year, url]; races: [raceId, year, round, circuitId, name, date, url]; status: [statusId, status]</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>4</th>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal details including date of birth</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>32</td>\\n\",\n \" <td>drivers: [driverId, driverRef, number, code, forename, surname, dob, nationality]; races: [raceId, year, round, name, date]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionText, positionOrder, points, laps, statusId]</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Role \\\\\\n\",\n \"0 SystemManager \\n\",\n \"1 RaceAnalyst \\n\",\n \"2 StandingsManager \\n\",\n \"3 PublicRelations \\n\",\n \"4 DriverManager \\n\",\n \"\\n\",\n \" Description \\\\\\n\",\n \"0 Full administrative access for system maintenance and data management \\n\",\n \"1 Analyzes race performance data including lap times, pit stops, and qualifying results \\n\",\n \"2 Manages and monitors championship standings for drivers and constructors \\n\",\n \"3 Accesses public information about circuits, drivers, constructors, and race schedules \\n\",\n \"4 Manages driver information and personal details including date of birth \\n\",\n \"\\n\",\n \" Tables Cols \\\\\\n\",\n \"0 13 94 (*) \\n\",\n \"1 8 61 \\n\",\n \"2 7 42 \\n\",\n \"3 6 31 \\n\",\n \"4 4 32 \\n\",\n \"\\n\",\n \" Policy \\n\",\n \"0 circuits: [*]; constructors: [*]; drivers: [*]; seasons: [*]; races: [*]; constructorResults: [*]; constructorStandings: [*]; driverStandings: [*]; lapTimes: [*]; pitStops: [*]; qualifying: [*]; status: [*]; results: [*] \\n\",\n \"1 circuits: [circuitId, name, location, country, lat, lng]; constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, circuitId, name, date]; lapTimes: [raceId, driverId, lap, position, time, milliseconds]; pitStops: [raceId, driverId, stop, lap, time, duration, milliseconds]; qualifying: [qualifyId, raceId, driverId, constructorId, number, position, q1, q2, q3]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionOrder, points, laps, time, milliseconds, fastestLap, rank, fastestLapTime, fastestLapSpeed, statusId] \\n\",\n \"2 constructors: [constructorId, name, nationality]; drivers: [driverId, driverRef, number, code, forename, surname, nationality]; races: [raceId, year, round, name, date]; constructorResults: [constructorResultsId, raceId, constructorId, points, status]; constructorStandings: [constructorStandingsId, raceId, constructorId, points, position, positionText, wins]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, points, position, positionText, statusId] \\n\",\n \"3 circuits: [circuitId, name, location, country, lat, lng, alt, url]; constructors: [constructorId, name, nationality, url]; drivers: [driverId, driverRef, number, code, forename, surname, nationality, url]; seasons: [year, url]; races: [raceId, year, round, circuitId, name, date, url]; status: [statusId, status] \\n\",\n \"4 drivers: [driverId, driverRef, number, code, forename, surname, dob, nationality]; races: [raceId, year, round, name, date]; driverStandings: [driverStandingsId, raceId, driverId, points, position, positionText, wins]; results: [resultId, raceId, driverId, constructorId, number, grid, position, positionText, positionOrder, points, laps, statusId] \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"=== Quality Comparison ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Metric</th>\\n\",\n \" <th>OLD</th>\\n\",\n \" <th>NEW</th>\\n\",\n \" <th>Threshold</th>\\n\",\n \" <th>Status</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>Roles</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>NaN</td>\\n\",\n \" <td>NaN</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>Avg Coverage</td>\\n\",\n \" <td>65.5%</td>\\n\",\n \" <td>55.3%</td>\\n\",\n \" <td><85%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>Coverage Std</td>\\n\",\n \" <td>0.183</td>\\n\",\n \" <td>0.251</td>\\n\",\n \" <td>>0.065</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>Max Overlap</td>\\n\",\n \" <td>87.3%</td>\\n\",\n \" <td>57.4%</td>\\n\",\n \" <td><80%</td>\\n\",\n \" <td>OK</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Metric OLD NEW Threshold Status\\n\",\n \"0 Roles 5 5 NaN NaN\\n\",\n \"1 Avg Coverage 65.5% 55.3% <85% OK\\n\",\n \"2 Coverage Std 0.183 0.251 >0.065 OK\\n\",\n \"3 Max Overlap 87.3% 57.4% <80% OK\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[OK] formula_1: All quality checks passed\\n\",\n \"\\n\",\n \"======================================================================\\n\",\n \"[DECISION] Satisfied? Run Cell 6.5 to apply. Otherwise edit Cell 6.2 and re-run 6.3.\\n\",\n \"[TIP] Use Cell 6.4.1 to select specific roles from OLD/NEW\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.4 Preview Regenerated Roles (Compare OLD vs NEW + Quality Check)\\n\",\n \"# Supports multiple databases\\n\",\n \"import numpy as np\\n\",\n \"from src.role_evaluator.quality_metrics import RoleQualityEvaluator\\n\",\n \"\\n\",\n \"pd.set_option('display.max_colwidth', None)\\n\",\n \"pd.set_option('display.max_rows', None)\\n\",\n \"pd.set_option('display.width', None)\\n\",\n \"\\n\",\n \"if 'regenerated_results' in dir() and regenerated_results:\\n\",\n \" for db_name, regen_result in regenerated_results.items():\\n\",\n \" if regen_result[\\\"status\\\"] != \\\"success\\\":\\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"[SKIP] {db_name}: {regen_result.get('error', 'failed')}\\\")\\n\",\n \" continue\\n\",\n \" \\n\",\n \" regenerated_assignments = regen_result[\\\"assignments\\\"]\\n\",\n \" target_schema = target_schemas.get(db_name)\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(f\\\"Database: {db_name}\\\")\\n\",\n \" print(f\\\"{'='*70}\\\")\\n\",\n \" \\n\",\n \" # --- NEW Role Summary ---\\n\",\n \" new_summary = []\\n\",\n \" for r in regenerated_assignments:\\n\",\n \" policy = r.get('policy', {})\\n\",\n \" total_cols = count_policy_columns(policy, target_schema)\\n\",\n \" has_wc = has_wildcard_policy(policy)\\n\",\n \" cols_display = f\\\"{total_cols}\\\" + (\\\" (*)\\\" if has_wc else \\\"\\\")\\n\",\n \" policy_str = \\\"; \\\".join([\\n\",\n \" f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" \\n\",\n \" for t, c in policy.items()\\n\",\n \" ])\\n\",\n \" new_summary.append({\\n\",\n \" \\\"Role\\\": r['role'],\\n\",\n \" \\\"Description\\\": r.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Cols\\\": cols_display,\\n\",\n \" \\\"Policy\\\": policy_str\\n\",\n \" })\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n=== NEW Roles ({len(regenerated_assignments)}) ===\\\")\\n\",\n \" display(pd.DataFrame(new_summary))\\n\",\n \" \\n\",\n \" # --- Quality Metrics ---\\n\",\n \" if target_schema:\\n\",\n \" total_schema_cols = sum(len(t['columns']) for t in target_schema.values())\\n\",\n \" else:\\n\",\n \" total_schema_cols = 10\\n\",\n \" \\n\",\n \" # NEW metrics\\n\",\n \" new_coverages = [count_policy_columns(r.get('policy', {}), target_schema) / total_schema_cols \\n\",\n \" for r in regenerated_assignments]\\n\",\n \" new_avg_cov = np.mean(new_coverages)\\n\",\n \" new_cov_std = np.std(new_coverages)\\n\",\n \" \\n\",\n \" new_max_overlap = 0.0\\n\",\n \" non_admin_new = [r for r in regenerated_assignments if 'admin' not in r['role'].lower()]\\n\",\n \" for i, r1 in enumerate(non_admin_new):\\n\",\n \" set1 = RoleQualityEvaluator.policy_to_column_set(r1.get('policy', {}))\\n\",\n \" for r2 in non_admin_new[i+1:]:\\n\",\n \" set2 = RoleQualityEvaluator.policy_to_column_set(r2.get('policy', {}))\\n\",\n \" new_max_overlap = max(new_max_overlap, RoleQualityEvaluator.jaccard_similarity(set1, set2))\\n\",\n \" \\n\",\n \" # OLD metrics\\n\",\n \" old_roles = all_assignments.get('assignments', {}).get(db_name, [])\\n\",\n \" if old_roles:\\n\",\n \" old_coverages = [count_policy_columns(r.get('policy', {}), target_schema) / total_schema_cols \\n\",\n \" for r in old_roles]\\n\",\n \" old_avg_cov = np.mean(old_coverages)\\n\",\n \" old_cov_std = np.std(old_coverages)\\n\",\n \" \\n\",\n \" old_max_overlap = 0.0\\n\",\n \" non_admin_old = [r for r in old_roles if 'admin' not in r['role'].lower()]\\n\",\n \" for i, r1 in enumerate(non_admin_old):\\n\",\n \" set1 = RoleQualityEvaluator.policy_to_column_set(r1.get('policy', {}))\\n\",\n \" for r2 in non_admin_old[i+1:]:\\n\",\n \" set2 = RoleQualityEvaluator.policy_to_column_set(r2.get('policy', {}))\\n\",\n \" old_max_overlap = max(old_max_overlap, RoleQualityEvaluator.jaccard_similarity(set1, set2))\\n\",\n \" else:\\n\",\n \" old_avg_cov, old_cov_std, old_max_overlap = 0, 0, 0\\n\",\n \" \\n\",\n \" # Comparison table (using QUALITY_THRESHOLDS from Cell 5.1)\\n\",\n \" def status(metric, val):\\n\",\n \" if metric == \\\"Coverage\\\": \\n\",\n \" return \\\"WARN\\\" if val > QUALITY_THRESHOLDS.get(\\\"coverage_max\\\", 0.90) else \\\"OK\\\"\\n\",\n \" if metric == \\\"CovStd\\\": \\n\",\n \" return \\\"WARN\\\" if val < QUALITY_THRESHOLDS.get(\\\"coverage_std_min\\\", 0.08) else \\\"OK\\\"\\n\",\n \" if metric == \\\"MaxOverlap\\\": \\n\",\n \" return \\\"WARN\\\" if val > QUALITY_THRESHOLDS.get(\\\"max_overlap\\\", 0.80) else \\\"OK\\\"\\n\",\n \" return \\\"OK\\\"\\n\",\n \" \\n\",\n \" quality_df = pd.DataFrame([\\n\",\n \" {\\\"Metric\\\": \\\"Roles\\\", \\\"OLD\\\": len(old_roles), \\\"NEW\\\": len(regenerated_assignments)},\\n\",\n \" {\\\"Metric\\\": \\\"Avg Coverage\\\", \\\"OLD\\\": f\\\"{old_avg_cov:.1%}\\\", \\\"NEW\\\": f\\\"{new_avg_cov:.1%}\\\", \\n\",\n \" \\\"Threshold\\\": f\\\"<{QUALITY_THRESHOLDS.get('coverage_max', 0.90):.0%}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"Coverage\\\", new_avg_cov)},\\n\",\n \" {\\\"Metric\\\": \\\"Coverage Std\\\", \\\"OLD\\\": f\\\"{old_cov_std:.3f}\\\", \\\"NEW\\\": f\\\"{new_cov_std:.3f}\\\",\\n\",\n \" \\\"Threshold\\\": f\\\">{QUALITY_THRESHOLDS.get('coverage_std_min', 0.08)}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"CovStd\\\", new_cov_std)},\\n\",\n \" {\\\"Metric\\\": \\\"Max Overlap\\\", \\\"OLD\\\": f\\\"{old_max_overlap:.1%}\\\", \\\"NEW\\\": f\\\"{new_max_overlap:.1%}\\\",\\n\",\n \" \\\"Threshold\\\": f\\\"<{QUALITY_THRESHOLDS.get('max_overlap', 0.80):.0%}\\\",\\n\",\n \" \\\"Status\\\": status(\\\"MaxOverlap\\\", new_max_overlap)},\\n\",\n \" ])\\n\",\n \" print(\\\"\\\\n=== Quality Comparison ===\\\")\\n\",\n \" display(quality_df)\\n\",\n \" \\n\",\n \" warn_count = quality_df['Status'].eq('WARN').sum() if 'Status' in quality_df.columns else 0\\n\",\n \" if warn_count == 0:\\n\",\n \" print(f\\\"[OK] {db_name}: All quality checks passed\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\"[WARN] {db_name}: {warn_count} issue(s)\\\")\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n{'='*70}\\\")\\n\",\n \" print(\\\"[DECISION] Satisfied? Run Cell 6.5 to apply. Otherwise edit Cell 6.2 and re-run 6.3.\\\")\\n\",\n \" print(\\\"[TIP] Use Cell 6.4.1 to select specific roles from OLD/NEW\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[WARN] No regenerated results. Run Cell 6.3 first.\\\")\\n\",\n \"\\n\",\n \"pd.reset_option('display.max_colwidth')\\n\",\n \"pd.reset_option('display.max_rows')\\n\",\n \"pd.reset_option('display.width')\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 70,\n \"id\": \"f003a014\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Role Selection Summary ===\\n\",\n \"\\n\",\n \"============================================================\\n\",\n \"Database: formula_1\\n\",\n \" From OLD (0): (none)\\n\",\n \" From NEW (4): ['SystemManager', 'RaceAnalyst', 'PublicRelations', 'DriverManager']\\n\",\n \" Combined: 4 roles\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Source</th>\\n\",\n \" <th>Role</th>\\n\",\n \" <th>Description</th>\\n\",\n \" <th>Tables</th>\\n\",\n \" <th>Policy</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>SystemManager</td>\\n\",\n \" <td>Full administrative access for system maintena...</td>\\n\",\n \" <td>13</td>\\n\",\n \" <td>circuits: [*]; constructors: [*]; drivers: [*]...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>1</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>RaceAnalyst</td>\\n\",\n \" <td>Analyzes race performance data including lap t...</td>\\n\",\n \" <td>8</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country,...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>2</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>PublicRelations</td>\\n\",\n \" <td>Accesses public information about circuits, dr...</td>\\n\",\n \" <td>6</td>\\n\",\n \" <td>circuits: [circuitId, name, location, country,...</td>\\n\",\n \" </tr>\\n\",\n \" <tr>\\n\",\n \" <th>3</th>\\n\",\n \" <td>NEW</td>\\n\",\n \" <td>DriverManager</td>\\n\",\n \" <td>Manages driver information and personal detail...</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>drivers: [driverId, driverRef, number, code, f...</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Source Role Description \\\\\\n\",\n \"0 NEW SystemManager Full administrative access for system maintena... \\n\",\n \"1 NEW RaceAnalyst Analyzes race performance data including lap t... \\n\",\n \"2 NEW PublicRelations Accesses public information about circuits, dr... \\n\",\n \"3 NEW DriverManager Manages driver information and personal detail... \\n\",\n \"\\n\",\n \" Tables Policy \\n\",\n \"0 13 circuits: [*]; constructors: [*]; drivers: [*]... \\n\",\n \"1 8 circuits: [circuitId, name, location, country,... \\n\",\n \"2 6 circuits: [circuitId, name, location, country,... \\n\",\n \"3 4 drivers: [driverId, driverRef, number, code, f... \"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"============================================================\\n\",\n \"[NEXT] Run Cell 6.5 to apply combined role sets\\n\",\n \"[TIP] Edit ROLE_SELECTION above to customize, then re-run this cell\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.4.1 Select & Combine Roles from OLD and NEW\\n\",\n \"# Human expert picks best roles from both versions to create final role set\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# EDIT HERE: Specify which roles to keep from each version\\n\",\n \"# Format: {\\\"db_name\\\": {\\\"old\\\": [role_names], \\\"new\\\": [role_names]}}\\n\",\n \"# Leave empty [] to skip a version, or use [\\\"*\\\"] to keep all from that version\\n\",\n \"# ============================================================\\n\",\n \"ROLE_SELECTION = {\\n\",\n \" \\\"formula_1\\\": {\\n\",\n \" \\\"old\\\": [], # e.g., [\\\"DataAnalyst\\\", \\\"Researcher\\\"]\\n\",\n \" \\\"new\\\": [\\\"SystemManager\\\", \\\"RaceAnalyst\\\", \\\"PublicRelations\\\", \\\"DriverManager\\\"], # keep all new roles\\n\",\n \" },\\n\",\n \" # \\\"financial\\\": {\\n\",\n \" # \\\"old\\\": [],\\n\",\n \" # \\\"new\\\": [\\\"*\\\"],\\n\",\n \" # },\\n\",\n \"}\\n\",\n \"\\n\",\n \"# ============================================================\\n\",\n \"# Process selections and create combined role sets\\n\",\n \"# ============================================================\\n\",\n \"combined_results = {}\\n\",\n \"\\n\",\n \"if 'regenerated_results' in dir() and regenerated_results:\\n\",\n \" print(\\\"=== Role Selection Summary ===\\\\n\\\")\\n\",\n \" \\n\",\n \" for db_name in valid_target_dbs:\\n\",\n \" selection = ROLE_SELECTION.get(db_name, {\\\"old\\\": [], \\\"new\\\": [\\\"*\\\"]})\\n\",\n \" old_keep = selection.get(\\\"old\\\", [])\\n\",\n \" new_keep = selection.get(\\\"new\\\", [\\\"*\\\"])\\n\",\n \" \\n\",\n \" # Get OLD roles\\n\",\n \" old_roles = all_assignments.get('assignments', {}).get(db_name, [])\\n\",\n \" old_role_dict = {r['role']: r for r in old_roles}\\n\",\n \" \\n\",\n \" # Get NEW roles\\n\",\n \" regen_result = regenerated_results.get(db_name, {})\\n\",\n \" if regen_result.get(\\\"status\\\") != \\\"success\\\":\\n\",\n \" print(f\\\"[SKIP] {db_name}: regeneration failed\\\")\\n\",\n \" continue\\n\",\n \" new_roles = regen_result[\\\"assignments\\\"]\\n\",\n \" new_role_dict = {r['role']: r for r in new_roles}\\n\",\n \" \\n\",\n \" # Build combined role set\\n\",\n \" combined_roles = []\\n\",\n \" selected_from_old = []\\n\",\n \" selected_from_new = []\\n\",\n \" \\n\",\n \" # Select from OLD\\n\",\n \" if old_keep == [\\\"*\\\"]:\\n\",\n \" combined_roles.extend(old_roles)\\n\",\n \" selected_from_old = [r['role'] for r in old_roles]\\n\",\n \" else:\\n\",\n \" for role_name in old_keep:\\n\",\n \" if role_name in old_role_dict:\\n\",\n \" combined_roles.append(old_role_dict[role_name])\\n\",\n \" selected_from_old.append(role_name)\\n\",\n \" else:\\n\",\n \" print(f\\\" [WARN] {db_name}: OLD role '{role_name}' not found\\\")\\n\",\n \" \\n\",\n \" # Select from NEW\\n\",\n \" if new_keep == [\\\"*\\\"]:\\n\",\n \" combined_roles.extend(new_roles)\\n\",\n \" selected_from_new = [r['role'] for r in new_roles]\\n\",\n \" else:\\n\",\n \" for role_name in new_keep:\\n\",\n \" if role_name in new_role_dict:\\n\",\n \" combined_roles.append(new_role_dict[role_name])\\n\",\n \" selected_from_new.append(role_name)\\n\",\n \" else:\\n\",\n \" print(f\\\" [WARN] {db_name}: NEW role '{role_name}' not found\\\")\\n\",\n \" \\n\",\n \" combined_results[db_name] = combined_roles\\n\",\n \" \\n\",\n \" # Display summary\\n\",\n \" print(f\\\"{'='*60}\\\")\\n\",\n \" print(f\\\"Database: {db_name}\\\")\\n\",\n \" print(f\\\" From OLD ({len(selected_from_old)}): {selected_from_old if selected_from_old else '(none)'}\\\")\\n\",\n \" print(f\\\" From NEW ({len(selected_from_new)}): {selected_from_new if selected_from_new else '(none)'}\\\")\\n\",\n \" print(f\\\" Combined: {len(combined_roles)} roles\\\")\\n\",\n \" \\n\",\n \" # Show combined roles table\\n\",\n \" target_schema = target_schemas.get(db_name)\\n\",\n \" combined_summary = []\\n\",\n \" for r in combined_roles:\\n\",\n \" policy = r.get('policy', {})\\n\",\n \" source = \\\"OLD\\\" if r['role'] in old_role_dict and r['role'] not in [nr['role'] for nr in new_roles] else \\\"NEW\\\"\\n\",\n \" if r['role'] in old_role_dict and r['role'] in [nr['role'] for nr in new_roles]:\\n\",\n \" source = \\\"NEW\\\" # Prefer NEW if same name exists in both\\n\",\n \" combined_summary.append({\\n\",\n \" \\\"Source\\\": source,\\n\",\n \" \\\"Role\\\": r['role'],\\n\",\n \" \\\"Description\\\": r.get('description', ''),\\n\",\n \" \\\"Tables\\\": len(policy),\\n\",\n \" \\\"Policy\\\": \\\"; \\\".join([f\\\"{t}: [{', '.join(c) if isinstance(c, list) else c}]\\\" for t, c in policy.items()])\\n\",\n \" })\\n\",\n \" \\n\",\n \" display(pd.DataFrame(combined_summary))\\n\",\n \" print()\\n\",\n \" \\n\",\n \" print(\\\"=\\\"*60)\\n\",\n \" print(f\\\"[NEXT] Run Cell 6.5 to apply combined role sets\\\")\\n\",\n \" print(\\\"[TIP] Edit ROLE_SELECTION above to customize, then re-run this cell\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[WARN] No regenerated results. Run Cell 6.3 first.\\\")\\n\",\n \" print(\\\"\\\\n[HINT] Available roles to select from:\\\")\\n\",\n \" print(\\\" OLD: roles from current assignments\\\")\\n\",\n \" print(\\\" NEW: roles from regeneration\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 71,\n \"id\": \"6cb24c7d\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"[MODE] Applying COMBINED role sets (from Cell 6.4.1 selection)\\n\",\n \"\\n\",\n \"=== Changes Applied ===\\n\"\n ]\n },\n {\n \"data\": {\n \"text/html\": [\n \"<div>\\n\",\n \"<style scoped>\\n\",\n \" .dataframe tbody tr th:only-of-type {\\n\",\n \" vertical-align: middle;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe tbody tr th {\\n\",\n \" vertical-align: top;\\n\",\n \" }\\n\",\n \"\\n\",\n \" .dataframe thead th {\\n\",\n \" text-align: right;\\n\",\n \" }\\n\",\n \"</style>\\n\",\n \"<table border=\\\"1\\\" class=\\\"dataframe\\\">\\n\",\n \" <thead>\\n\",\n \" <tr style=\\\"text-align: right;\\\">\\n\",\n \" <th></th>\\n\",\n \" <th>Database</th>\\n\",\n \" <th>Old Roles</th>\\n\",\n \" <th>New Roles</th>\\n\",\n \" <th>Source</th>\\n\",\n \" </tr>\\n\",\n \" </thead>\\n\",\n \" <tbody>\\n\",\n \" <tr>\\n\",\n \" <th>0</th>\\n\",\n \" <td>formula_1</td>\\n\",\n \" <td>5</td>\\n\",\n \" <td>4</td>\\n\",\n \" <td>Combined (OLD+NEW)</td>\\n\",\n \" </tr>\\n\",\n \" </tbody>\\n\",\n \"</table>\\n\",\n \"</div>\"\n ],\n \"text/plain\": [\n \" Database Old Roles New Roles Source\\n\",\n \"0 formula_1 5 4 Combined (OLD+NEW)\"\n ]\n },\n \"metadata\": {},\n \"output_type\": \"display_data\"\n },\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"\\n\",\n \"Saved to: bird_column_level_rbac_assignments_v2_20251230.json\\n\",\n \"\\n\",\n \"[OK] State cleared. Select new database(s) to continue.\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.5 Apply Changes (Save Combined Role Sets)\\n\",\n \"# Uses combined_results from Cell 6.4.1 (selected from OLD + NEW)\\n\",\n \"from datetime import datetime\\n\",\n \"\\n\",\n \"APPLY_CHANGES = True # Set to True to confirm\\n\",\n \"\\n\",\n \"if APPLY_CHANGES:\\n\",\n \" # Prefer combined_results (from selection), fallback to regenerated_results\\n\",\n \" use_combined = 'combined_results' in dir() and combined_results\\n\",\n \" use_regenerated = 'regenerated_results' in dir() and regenerated_results\\n\",\n \" \\n\",\n \" if use_combined:\\n\",\n \" print(\\\"[MODE] Applying COMBINED role sets (from Cell 6.4.1 selection)\\\\n\\\")\\n\",\n \" applied = []\\n\",\n \" \\n\",\n \" for db_name, combined_roles in combined_results.items():\\n\",\n \" if not combined_roles:\\n\",\n \" continue\\n\",\n \" \\n\",\n \" old_count = len(all_assignments.get('assignments', {}).get(db_name, []))\\n\",\n \" all_assignments['assignments'][db_name] = combined_roles\\n\",\n \" applied.append({\\n\",\n \" \\\"Database\\\": db_name,\\n\",\n \" \\\"Old Roles\\\": old_count,\\n\",\n \" \\\"New Roles\\\": len(combined_roles),\\n\",\n \" \\\"Source\\\": \\\"Combined (OLD+NEW)\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" if applied:\\n\",\n \" OUTPUT_DIR = project_root / 'outputs'\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = OUTPUT_DIR / f'bird_column_level_rbac_assignments_v2_{today}.json'\\n\",\n \" with open(output_path, 'w') as f:\\n\",\n \" json.dump(all_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \" \\n\",\n \" print(\\\"=== Changes Applied ===\\\")\\n\",\n \" display(pd.DataFrame(applied))\\n\",\n \" print(f\\\"\\\\nSaved to: {output_path.name}\\\")\\n\",\n \" \\n\",\n \" # Clear state\\n\",\n \" combined_results = {}\\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"\\\\n[OK] State cleared. Select new database(s) to continue.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No combined role sets to apply. Run Cell 6.4.1 first.\\\")\\n\",\n \" \\n\",\n \" elif use_regenerated:\\n\",\n \" print(\\\"[MODE] Applying ALL NEW roles (no selection made)\\\\n\\\")\\n\",\n \" applied = []\\n\",\n \" \\n\",\n \" for db_name, regen_result in regenerated_results.items():\\n\",\n \" if regen_result[\\\"status\\\"] != \\\"success\\\":\\n\",\n \" continue\\n\",\n \" \\n\",\n \" old_count = len(all_assignments.get('assignments', {}).get(db_name, []))\\n\",\n \" all_assignments['assignments'][db_name] = regen_result[\\\"assignments\\\"]\\n\",\n \" applied.append({\\n\",\n \" \\\"Database\\\": db_name,\\n\",\n \" \\\"Old Roles\\\": old_count,\\n\",\n \" \\\"New Roles\\\": len(regen_result[\\\"assignments\\\"]),\\n\",\n \" \\\"Source\\\": \\\"All NEW\\\"\\n\",\n \" })\\n\",\n \" \\n\",\n \" if applied:\\n\",\n \" OUTPUT_DIR = project_root / 'outputs'\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = OUTPUT_DIR / f'bird_column_level_rbac_assignments_v2_{today}.json'\\n\",\n \" with open(output_path, 'w') as f:\\n\",\n \" json.dump(all_assignments, f, indent=2, ensure_ascii=False)\\n\",\n \" \\n\",\n \" print(\\\"=== Changes Applied ===\\\")\\n\",\n \" display(pd.DataFrame(applied))\\n\",\n \" print(f\\\"\\\\nSaved to: {output_path.name}\\\")\\n\",\n \" \\n\",\n \" regenerated_results = {}\\n\",\n \" print(\\\"\\\\n[OK] State cleared. Select new database(s) to continue.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No successful regenerations to apply.\\\")\\n\",\n \" else:\\n\",\n \" print(\\\"[WARN] No results to apply. Run Cell 6.3 and optionally Cell 6.4.1 first.\\\")\\n\",\n \"else:\\n\",\n \" has_combined = 'combined_results' in dir() and combined_results\\n\",\n \" has_regenerated = 'regenerated_results' in dir() and regenerated_results\\n\",\n \" \\n\",\n \" if has_combined:\\n\",\n \" print(f\\\"[INFO] {len(combined_results)} database(s) with COMBINED role sets ready\\\")\\n\",\n \" print(\\\"[TIP] These are your custom selections from OLD + NEW\\\")\\n\",\n \" elif has_regenerated:\\n\",\n \" success_count = sum(1 for r in regenerated_results.values() if r[\\\"status\\\"] == \\\"success\\\")\\n\",\n \" print(f\\\"[INFO] {success_count} database(s) with ALL NEW roles ready\\\")\\n\",\n \" print(\\\"[TIP] Run Cell 6.4.1 to select specific roles from OLD/NEW\\\")\\n\",\n \" \\n\",\n \" print(\\\"\\\\n[INFO] APPLY_CHANGES is False. Set to True and re-run to apply.\\\")\"\n ]\n },\n {\n \"cell_type\": \"code\",\n \"execution_count\": 72,\n \"id\": \"f1bd0bc2\",\n \"metadata\": {},\n \"outputs\": [\n {\n \"name\": \"stdout\",\n \"output_type\": \"stream\",\n \"text\": [\n \"=== Regenerating RBAC Dataset ===\\n\",\n \"Role assignments: bird_column_level_rbac_assignments_v2_20251230.json\\n\",\n \"BIRD data: /home/feiy/Role-SQL-benchmark/data/Bird/dev_20251106/dev.json\\n\",\n \"Generating column-level RBAC dataset...\\n\",\n \" Roles per DB: None\\n\",\n \" Queries per role: all\\n\",\n \"Current ratio (allowed): 50.41%\\n\",\n \" Allowed: 3656, Denied: 3596\\n\",\n \"\\n\",\n \"Dataset saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_20251230.json\\n\",\n \"Metadata saved to /home/feiy/Role-SQL-benchmark/outputs/column_level_rbac_dataset_bird_20251230_metadata.json\\n\",\n \" Total entries: 7252\\n\",\n \" Allowed: 3656 (50.4%)\\n\",\n \" Denied: 3596 (49.6%)\\n\",\n \"\\n\",\n \"✓ Dataset saved to: column_level_rbac_dataset_bird_20251230.json\\n\",\n \"\\n\",\n \"=== Dataset Statistics ===\\n\",\n \" total_entries: 7252\\n\",\n \" allowed: 3656\\n\",\n \" denied: 3596\\n\",\n \" allowed_ratio: 0.5041367898510756\\n\",\n \"\\n\",\n \"Per-database distribution:\\n\",\n \" california_schools: allowed=118, denied=327\\n\",\n \" card_games: allowed=507, denied=448\\n\",\n \" codebase_community: allowed=578, denied=352\\n\",\n \" debit_card_specializing: allowed=174, denied=210\\n\",\n \" european_football_2: allowed=277, denied=368\\n\",\n \" financial: allowed=156, denied=374\\n\",\n \" formula_1: allowed=385, denied=311\\n\",\n \" student_club: allowed=396, denied=394\\n\",\n \" superhero: allowed=328, denied=317\\n\",\n \" thrombosis_prediction: allowed=432, denied=220\\n\",\n \" toxicology: allowed=305, denied=275\\n\"\n ]\n }\n ],\n \"source\": [\n \"# 6.6 Regenerate RBAC Dataset with Updated Role Assignments\\n\",\n \"# Run this after Cell 6.5 to create the final dataset\\n\",\n \"\\n\",\n \"from datetime import datetime\\n\",\n \"import importlib\\n\",\n \"\\n\",\n \"# IMPORTANT: Reload ALL dependent modules to pick up CTE parsing fix\\n\",\n \"import src.processors.column_extractor\\n\",\n \"import src.processors.column_permission_checker\\n\",\n \"import src.processors.column_level_rbac_generator\\n\",\n \"\\n\",\n \"importlib.reload(src.processors.column_extractor)\\n\",\n \"importlib.reload(src.processors.column_permission_checker)\\n\",\n \"importlib.reload(src.processors.column_level_rbac_generator)\\n\",\n \"\\n\",\n \"from src.processors.column_level_rbac_generator import ColumnLevelRBACGenerator\\n\",\n \"\\n\",\n \"# Find the updated role assignments file (v2 if exists, otherwise latest)\\n\",\n \"v2_files = sorted(glob.glob(str(project_root / 'outputs' / 'bird_column_level_rbac_assignments_v2_*.json')))\\n\",\n \"if v2_files:\\n\",\n \" UPDATED_ROLE_PATH = v2_files[-1]\\n\",\n \"else:\\n\",\n \" role_files = sorted(glob.glob(str(project_root / 'outputs' / 'column_level_role_assignments_bird_*.json')))\\n\",\n \" UPDATED_ROLE_PATH = role_files[-1] if role_files else None\\n\",\n \"\\n\",\n \"if UPDATED_ROLE_PATH:\\n\",\n \" print(f\\\"=== Regenerating RBAC Dataset ===\\\")\\n\",\n \" print(f\\\"Role assignments: {Path(UPDATED_ROLE_PATH).name}\\\")\\n\",\n \" print(f\\\"BIRD data: {BIRD_DATA_PATH}\\\")\\n\",\n \" \\n\",\n \" # Initialize generator with updated roles\\n\",\n \" generator = ColumnLevelRBACGenerator(\\n\",\n \" role_assignments_path=UPDATED_ROLE_PATH,\\n\",\n \" bird_data_path=str(BIRD_DATA_PATH),\\n\",\n \" bird_db_root=str(BIRD_DB_ROOT),\\n\",\n \" project_root=str(project_root),\\n\",\n \" seed=42\\n\",\n \" )\\n\",\n \" \\n\",\n \" # Generate dataset\\n\",\n \" today = datetime.now().strftime('%Y%m%d')\\n\",\n \" output_path = project_root / 'outputs' / f'column_level_rbac_dataset_bird_{today}.json'\\n\",\n \" \\n\",\n \" full_stats = generator.generate_and_save(\\n\",\n \" output_path=str(output_path),\\n\",\n \" roles_per_db=None,\\n\",\n \" queries_per_role=None\\n\",\n \" )\\n\",\n \" \\n\",\n \" print(f\\\"\\\\n✓ Dataset saved to: {output_path.name}\\\")\\n\",\n \" print(f\\\"\\\\n=== Dataset Statistics ===\\\")\\n\",\n \" for k, v in full_stats.items():\\n\",\n \" if k == 'by_database':\\n\",\n \" print(f\\\"\\\\nPer-database distribution:\\\")\\n\",\n \" for db_id, counts in sorted(v.items()):\\n\",\n \" print(f\\\" {db_id}: allowed={counts['allowed']}, denied={counts['denied']}\\\")\\n\",\n \" else:\\n\",\n \" print(f\\\" {k}: {v}\\\")\\n\",\n \"else:\\n\",\n \" print(\\\"[ERROR] No role assignments file found. Run Cell 6.5 first.\\\")\"\n ]\n },\n {\n \"cell_type\": \"markdown\",\n \"id\": \"a9345c97\",\n \"metadata\": {},\n \"source\": [\n \"## 7. Summary\\n\",\n \"\\n\",\n \"### Generated Files\\n\",\n \"- `outputs/column_level_role_assignments_bird_YYYYMMDD.json` - Role assignments with column-level policies\\n\",\n \"- `outputs/bird_column_level_rbac_assignments_v2_YYYYMMDD.json` - Updated role assignments after regeneration\\n\",\n \"- `outputs/column_level_rbac_dataset_bird_YYYYMMDD.json` - Column-Level RBAC dataset\\n\",\n \"\\n\",\n \"### Workflow\\n\",\n \"\\n\",\n \"| Section | Description |\\n\",\n \"|---------|-------------|\\n\",\n \"| **3** | Batch process all BIRD databases to generate initial roles |\\n\",\n \"| **4** | Generate RBAC dataset from role assignments |\\n\",\n \"| **5** | Quality assessment with configurable thresholds |\\n\",\n \"| **6.1** | Select problematic database(s) and view current state |\\n\",\n \"| **6.2** | Write regeneration hints with problem description |\\n\",\n \"| **6.3** | Execute LLM regeneration with hints |\\n\",\n \"| **6.4** | Preview OLD vs NEW roles with quality comparison |\\n\",\n \"| **6.4.1** | Select & combine best roles from OLD and NEW |\\n\",\n \"| **6.5** | Apply changes (save updated role assignments) |\\n\",\n \"| **6.6** | Regenerate RBAC dataset with updated roles |\\n\",\n \"\\n\",\n \"### Quality Assessment Module (Section 5)\\n\",\n \"\\n\",\n \"```python\\n\",\n \"from src.role_evaluator import RoleQualityEvaluator, RoleQualityVisualizer\\n\",\n \"\\n\",\n \"evaluator = RoleQualityEvaluator(\\n\",\n \" role_assignments_path=\\\"outputs/column_level_role_assignments_bird.json\\\",\\n\",\n \" dataset_path=\\\"outputs/column_level_rbac_dataset_bird.json\\\",\\n\",\n \" schema_dir=\\\"data/Bird/dev_20251106/dev_databases\\\",\\n\",\n \" embed_model=\\\"BAAI/bge-small-en-v1.5\\\" # or \\\"text-embedding-3-small\\\"\\n\",\n \")\\n\",\n \"\\n\",\n \"report = evaluator.evaluate_all()\\n\",\n \"visualizer = RoleQualityVisualizer(evaluator=evaluator, report=report)\\n\",\n \"visualizer.plot_quality_dashboard()\\n\",\n \"```\\n\",\n \"\\n\",\n \"### Quality Thresholds (Configurable)\\n\",\n \"\\n\",\n \"| Metric | Default | Description |\\n\",\n \"|--------|---------|-------------|\\n\",\n \"| `deny_rate_min` | 5% | Warn if deny rate below (roles too permissive) |\\n\",\n \"| `coverage_max` | 90% | Warn if avg coverage above (roles too broad) |\\n\",\n \"| `coverage_std_min` | 0.08 | Warn if coverage std below (low diversity) |\\n\",\n \"| `max_overlap` | 80% | Warn if max overlap above (redundant roles) |\\n\",\n \"\\n\",\n \"### External Modules\\n\",\n \"\\n\",\n \"1. **`src/role_evaluator/quality_metrics.py`** - Quality metrics computation\\n\",\n \" - `RoleQualityEvaluator`: Comprehensive evaluator\\n\",\n \" - `DenyRateMetrics`, `SemanticMetrics`, `OverlapMetrics`: Data classes\\n\",\n \" - `print_quality_report()`: Formatted report output\\n\",\n \"\\n\",\n \"2. **`src/role_evaluator/quality_visualizer.py`** - Visualization suite\\n\",\n \" - `plot_deny_rate_distribution()`: Permission distribution chart\\n\",\n \" - `plot_semantic_alignment()`: Semantic alignment scatter plot\\n\",\n \" - `plot_policy_overlap_bars()`: Policy overlap bar chart\\n\",\n \" - `plot_quality_dashboard()`: Comprehensive dashboard\\n\",\n \"\\n\",\n \"3. **`src/processors/column_level_rbac_generator.py`** - Dataset generation\\n\",\n \" - `ColumnLevelRBACGenerator`: Generate RBAC dataset from role assignments\"\n ]\n }\n ],\n \"metadata\": {\n \"kernelspec\": {\n \"display_name\": \"llm4db\",\n \"language\": \"python\",\n \"name\": \"python3\"\n },\n \"language_info\": {\n \"codemirror_mode\": {\n \"name\": \"ipython\",\n \"version\": 3\n },\n \"file_extension\": \".py\",\n \"mimetype\": \"text/x-python\",\n \"name\": \"python\",\n \"nbconvert_exporter\": \"python\",\n \"pygments_lexer\": \"ipython3\",\n \"version\": \"3.10.18\"\n }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 5\n}\n"}}}} |