""" Smart Chat Interface for Data Adjustment Uses liveboard context to intelligently understand requests and bundle confirmations into smart prompts. """ from dotenv import load_dotenv import os from smart_data_adjuster import SmartDataAdjuster load_dotenv() def chat_loop(): """Main smart chat loop""" print(""" ╔════════════════════════════════════════════════════════════╗ ║ ║ ║ Smart Data Adjustment Chat ║ ║ ║ ╚════════════════════════════════════════════════════════════╝ This chat understands your liveboard and visualizations! Commands: - Type your adjustment request naturally - Reference visualizations by number (e.g., "viz 2") - "done" or "exit" to quit - "help" for examples Examples: - "make 1080p Webcam 40 billion" - "increase tablet revenue to 100B" - "in viz 2, set laptops to 50B" - "set profit margin to 30% for electronics" """) # Setup - can be overridden by environment or passed as arguments database = os.getenv('SNOWFLAKE_DATABASE') schema = os.getenv('DEMO_SCHEMA', "20251116_140933_AMAZO_SAL") liveboard_guid = os.getenv('DEMO_LIVEBOARD_GUID', "9a30c9e4-efba-424a-8359-b16eb3a43ec3") print(f"📊 Initializing...") adjuster = SmartDataAdjuster(database, schema, liveboard_guid) adjuster.connect() # Load liveboard context if not adjuster.load_liveboard_context(): print("❌ Failed to load liveboard context") return print("\n" + "="*80) print("Ready! I understand your liveboard context.") print("="*80 + "\n") # Show numbered visualizations print("📊 Available Visualizations:") print("-" * 80) for idx, viz in enumerate(adjuster.visualizations, start=1): print(f" [{idx}] {viz['name']}") cols = ', '.join(viz['columns'][:5]) # Show first 5 columns if len(viz['columns']) > 5: cols += f"... (+{len(viz['columns'])-5} more)" print(f" Columns: {cols}") print("-" * 80) print("💡 TIP: You can reference visualizations by number (e.g., 'viz 2') or naturally!") print("="*80 + "\n") while True: # Get user input user_input = input("\n💬 You: ").strip() if not user_input: continue # Check for exit if user_input.lower() in ['done', 'exit', 'quit', 'bye']: print("\n👋 Goodbye!") break # Check for help if user_input.lower() == 'help': print(""" 📚 Help - Smart Data Adjustment I understand your liveboard context, so you can be natural: Examples: ✅ "make 1080p webcam 40 billion" → I'll find the viz with products and TOTAL_REVENUE ✅ "increase tablet to 100B" → I'll match to the right product visualization ✅ "in viz 2, set laptops to 50B" → Use viz numbers to be specific! ✅ "set profit margin to 30% for electronics" → I'll find the viz with profit margin and categories I'll show you what I understood and ask for one yes/no confirmation! """) continue try: # Check if user specified a viz number viz_number = None import re viz_match = re.search(r'\bviz\s+(\d+)\b', user_input, re.IGNORECASE) if viz_match: viz_number = int(viz_match.group(1)) if viz_number < 1 or viz_number > len(adjuster.visualizations): print(f"❌ Invalid viz number. Please use 1-{len(adjuster.visualizations)}") continue # Remove viz reference from request user_input = re.sub(r'\bviz\s+\d+\b', '', user_input, flags=re.IGNORECASE).strip() user_input = re.sub(r'^,?\s*', '', user_input) # Clean up leading comma/space # Match request to visualization print(f"\n🤔 Analyzing request...") match = adjuster.match_request_to_viz(user_input) if not match: print("❌ I couldn't understand that request.") print("💡 Try being more specific or type 'help' for examples") continue # If user specified viz number, override the match if viz_number: match['viz'] = adjuster.visualizations[viz_number - 1] print(f" → Using specified viz: [{match['viz']['name']}]") else: print(f" → Matched to: [{match['viz']['name']}]") print(f" → Entity: {match['entity_value']}") print(f" → Confidence: {match['confidence'].upper()}") # If low confidence, ask for confirmation if match['confidence'] == 'low': print(f"\n⚠️ I'm not very confident about this match.") confirm_match = input(" Is this correct? [yes/no]: ").strip().lower() if confirm_match not in ['yes', 'y']: print("💡 Try rephrasing your request") continue # Get current value print(f"\n📊 Querying current data...") current_value = adjuster.get_current_value( match['entity_value'], match['metric_column'] ) if current_value == 0: print(f"⚠️ No data found for '{match['entity_value']}'") print("💡 Check the spelling or try a different entity") continue # Calculate target value if percentage target_value = match.get('target_value') if match.get('is_percentage'): percentage = match.get('percentage', 0) target_value = current_value * (1 + percentage / 100) print(f" 💡 {percentage:+.1f}% change = ${current_value:,.0f} → ${target_value:,.0f}") # Generate strategy strategy = adjuster.generate_strategy( match['entity_value'], match['metric_column'], current_value, target_value ) # Present smart bundled confirmation # Update match with calculated target if percentage if match.get('is_percentage'): match['target_value'] = target_value confirmation = adjuster.present_smart_confirmation(match, current_value, strategy) print(confirmation) # Get user decision response = input("Run SQL? [yes/no]: ").strip().lower() if response not in ['yes', 'y']: print("\n❌ Cancelled - no changes made") print("💡 You can try a different adjustment or rephrase") continue # Execute print(f"\n⚙️ Executing SQL...") result = adjuster.execute_sql(strategy['sql']) if result['success']: print(f"\n✅ SUCCESS! Updated {result['rows_affected']} rows") print(f"🔄 Refresh your ThoughtSpot liveboard to see changes") print(f" URL: https://se-thoughtspot-cloud.thoughtspot.cloud/#/pinboard/{liveboard_guid}") else: print(f"\n❌ FAILED: {result['error']}") # Check for common errors if 'out of representable range' in result['error'].lower(): print("\n💡 The number is too large for the database column.") print(" Try a smaller target value (e.g., 40B instead of 50B)") except KeyboardInterrupt: print("\n\n⚠️ Interrupted") break except Exception as e: print(f"\n❌ Error: {e}") import traceback print(traceback.format_exc()) # Cleanup adjuster.close() print("\n✅ Connection closed") if __name__ == "__main__": try: chat_loop() except KeyboardInterrupt: print("\n\n👋 Goodbye!")