File size: 8,633 Bytes
b91d31b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
"""
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!")