File size: 30,568 Bytes
825942f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2337ca9
825942f
2337ca9
 
 
 
 
825942f
 
 
 
 
 
 
 
 
 
 
 
 
2337ca9
825942f
2337ca9
825942f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a2c0a9d
825942f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2337ca9
 
 
 
825942f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
import gradio as gr
from openai import OpenAI
import json
import os
from dotenv import load_dotenv
from Project import * 
from supabase import create_client, Client
import psycopg2
from psycopg2.extras import RealDictCursor
import tempfile
import pandas as pd
from pathlib import Path
import os
from io import StringIO



load_dotenv()
api_key = os.getenv("LANGTRACE_API_KEY")
if api_key is None:
    raise ValueError("Environment variable 'LANGTRACE_API_KEY' is not set. Please set it in your .env file.")
langtrace.init(api_key=api_key)

SUPABASE_URL = os.getenv('SUPABASE_URL')
SUPABASE_KEY = os.getenv('SUPABASE_KEY')
DB_NAME = os.getenv('DB_NAME')

print(SUPABASE_URL, SUPABASE_KEY, DB_NAME)
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)

def get_db_connection():
    """Establishes and returns a new database connection."""
    db_params = {
        'dbname': os.getenv('DB_NAME'),
        'user': os.getenv('DB_USER'),
        'password': os.getenv('DB_PASSWORD'),
        'host': os.getenv('DB_HOST'),
        'port': os.getenv('DB_PORT')
    }
    conn = psycopg2.connect(**db_params)
    return conn

    
def get_latest_components():
    """Fetches the latest project rubric for the project 'Engage'."""
    try:
        conn = get_db_connection()
        cur = conn.cursor(cursor_factory=RealDictCursor)
        
        cur.execute("""
            SELECT base_project_name,module,submodule,unit_type,quantity,mandays_per_unit
            FROM base_project_component pc
            WHERE (pc.base_project_name, pc.component_version) IN (
                SELECT base_project_name, MAX(component_version)
                FROM base_project_component
                GROUP BY base_project_name
            )
            ORDER BY pc.base_project_name;
        """)
        
        component = cur.fetchall()
        cur.close()
        conn.close()

        return component

    except Exception as e:
        return {
            'status': 'error',
            'message': str(e)
        }

    
def get_section_name_and_rubric_list():
    """Fetches the latest project rubric for the project 'Engage'."""
    try:
        conn = get_db_connection()
        cur = conn.cursor(cursor_factory=RealDictCursor)
        
        cur.execute("""
            SELECT section_name, criteria, initial_question,explanation, priority, quantifiable_value
            FROM base_project_rubric
            WHERE LOWER(base_project_name) = LOWER('Engage')
              AND rubric_version = (
                  SELECT MAX(rubric_version)
                  FROM base_project_rubric
                  WHERE LOWER(base_project_name) = LOWER('Engage')
              )
            ORDER BY 
                CASE priority
                    WHEN 'high' THEN 1
                    WHEN 'med' THEN 2
                    WHEN 'low' THEN 3
                    ELSE 4
                END;
        """)
        
        rubric = cur.fetchall()
        cur.close()
        conn.close()

        # Convert feedback to a list of dictionaries for JSON serialization
        rubric_list = [dict(row) for row in rubric]
        section_name_list = {row['section_name']: dict(row) for row in rubric}.keys()
        # print(f"in get_section_name_and_rubric_list: {rubric_list}, {section_name_list}")
        print(f"in get_section_name_and_rubric_list: {section_name_list}")
        return section_name_list, rubric_list

    except Exception as e:
        return {
            'status': 'error',
            'message': str(e)
        }

# Initialize project
# zus_quotation = Project(ProjectType.Engage, zus_coffee, form_v8)
zus_quotation = Project(ProjectType.Engage)

def recalculate_costs(df):
    """Recalculate costs based on modified dataframe values"""
    try:
        # Convert quantity and mandays_per_unit to numeric, replacing non-numeric values with 0
        df['quantity'] = pd.to_numeric(df['quantity'], errors='coerce').fillna(0)
        df['mandays_per_unit'] = pd.to_numeric(df['mandays_per_unit'], errors='coerce').fillna(0)
        
        # Calculate mandays and costs
        df, total_mandays, total_cost = calculate_mandays_and_costs(df)
        csv_string = df.to_csv(index=False)
        # Insert the quotation into the database
        insert_quotation(csv_string, total_cost, total_mandays)
        # df['calculated_mandays'] = df['quantity'] * df['mandays_per_unit']
        # total_mandays = df['calculated_mandays'].sum()
        # total_cost = 1500 * total_mandays

        # Format output string
        cost_summary = f"""
        Total Mandays: {total_mandays:.2f}
        Total Cost: ${total_cost:,.2f}
        """
        return df, cost_summary
    except Exception as e:
        return df, f"Error recalculating costs: {str(e)}"

def recalculate_costs_v2(df):
    """Recalculate costs based on modified dataframe values"""
    try:
        # Convert quantity and mandays_per_unit to numeric, replacing non-numeric values with 0
        df['quantity'] = pd.to_numeric(df['quantity'], errors='coerce').fillna(1)
        df['mandays_per_unit'] = pd.to_numeric(df['mandays_per_unit'], errors='coerce').fillna(0)

        # Calculate mandays and costs
        df, total_mandays, total_cost = calculate_mandays_and_costs(df)

        csv_string = df.to_csv(index=False)
        # Insert the quotation into the database
        insert_quotation(csv_string, total_cost, total_mandays)
        # df['calculated_mandays'] = df['quantity'] * df['mandays_per_unit']
        # total_mandays = df['calculated_mandays'].sum()
        # total_cost = 1500 * total_mandays

        # Format output string
        cost_summary = f"""
        Total Mandays: {total_mandays:.2f}
        Total Cost: ${total_cost:,.2f}
        """
        return df, f"Successfully Updated Quotation. SessionID:{zus_quotation.session_id}", cost_summary
    except Exception as e:
        return df, f"Error recalculating costs: {str(e)}"
    
def recalculate_costs_v3(quantity_df,task_df):
    """Recalculate costs based on modified dataframe values"""
    try:
        # Convert quantity and mandays_per_unit to numeric, replacing non-numeric values with 0
        quantity_df['quantity'] = pd.to_numeric(quantity_df['quantity'], errors='coerce').fillna(1)
        quantity_df['mandays_per_unit'] = pd.to_numeric(quantity_df['mandays_per_unit'], errors='coerce').fillna(0)

        # Calculate mandays and costs
        quantity_df, total_mandays, total_cost = calculate_mandays_and_costs(quantity_df)

        csv_string = quantity_df.to_csv(index=False)
        task_string = task_df.to_csv(index=False)
        # Insert the quotation into the database
        insert_quotation(csv_string, total_cost, total_mandays, details= task_string)
        # df['calculated_mandays'] = df['quantity'] * df['mandays_per_unit']
        # total_mandays = df['calculated_mandays'].sum()
        # total_cost = 1500 * total_mandays
        # Format output string
        cost_summary = f"""
        Total Mandays: {total_mandays:.2f}
        Total Cost: ${total_cost:,.2f}
        """
        return quantity_df, f"Successfully Updated Quotation. SessionID:{zus_quotation.session_id}", cost_summary
    except Exception as e:
        return quantity_df, f"Error recalculating costs: {str(e)}"


def sanitize_text(text):
    """Remove or replace special characters from text"""
    # Replace single quotes with double quotes to avoid string formatting issues
    text = text.replace("'", '')
    # Remove or replace other problematic characters as needed
    # Add more replacements here if needed
    return text

def process_response(answer, history):
    """Process user responses and generate appropriate follow-up questions."""
    try:
        # Convert history to list if it's not already
        if not isinstance(history, list):
            history = []
            
        # Sanitize the answer before processing
        sanitized_answer = sanitize_text(str(answer))
        
        # Add the user's answer to project details
        zus_quotation.add_project_detail(sanitized_answer)
        
        # Update session in database if we have a session_id
        if zus_quotation.session_id:
            try:
                conn = get_db_connection()
                cur = conn.cursor()
                
                # Update project_requirement in sessions table
                cur.execute("""
                    UPDATE sessions 
                    SET project_requirement = %s
                    WHERE session_id = %s
                """, (json.dumps(zus_quotation.project_detail), zus_quotation.session_id))
                
                conn.commit()
                cur.close()
                conn.close()
            except Exception as e:
                print(f"Error updating session: {str(e)}")
        
        # Generate next question based on conversation stage
        if len(history) == 1:  # After first client information question
            next_question = zus_quotation.generate_client_follow_up()
        elif len(history) == 2:  # After client follow-up
            next_question = zus_quotation.generate_questions()
        else:  # Subsequent project requirements questions
            next_question = zus_quotation.generate_follow_up()
        
        # Ensure we're adding a proper tuple to history
        if isinstance(answer, str) and isinstance(next_question, str):
            history.append((answer, next_question))
        
        return history, next_question
    except Exception as e:
        print(f"Error in process_response: {str(e)}")
        return history, "Error in generating follow up questions"

def map_mandays(df):
    mandays_dict = zus_quotation.get_component_mandays()
    # Create a mapping dictionary from mandays_dict
    mandays_mapping = {
        item['submodule']: item['mandays_per_unit'] 
        for item in mandays_dict 
        if item['submodule']
    }
    
    # Initialize mandays_per_unit and remarks columns
    df['mandays_per_unit'] = 0.0
    df['remarks'] = ''
    
    # Map mandays_per_unit and add remarks
    for idx, row in df.iterrows():
        submodule = row['submodule']
        if submodule in mandays_mapping:
            df.at[idx, 'mandays_per_unit'] = float(mandays_mapping[submodule] or 0)
        else:
            df.at[idx, 'remarks'] = 'Mandays estimation needed - submodule not found in reference data'
            df.at[idx, 'mandays_per_unit'] = 0.5  # Default value
    
    return df

def calculate_mandays_and_costs(df):
    try:
        # df['mandays_per_unit'] = pd.to_numeric(df['mandays_per_unit'].replace('', '0'), errors='coerce').fillna(0)
        # Calculate mandays and costs
        df['calculated_mandays'] = df['quantity'] * df['mandays_per_unit']
        total_mandays = df['calculated_mandays'].sum()
        total_cost = 1500 * total_mandays
        return df, total_mandays, total_cost
    except Exception as e:
        print(f"Error calculating mandays and costs: {str(e)}")
        return None, None, None


def generate_csv_v1():
    """Generate CSV file with calculated mandays and costs"""
    try:
        # Get CSV string from quotation
        csv_string = zus_quotation.populate_template_with_units()

        # Create DataFrame from CSV string
        df = pd.read_csv(StringIO(csv_string))
        
        # Convert quantity and mandays_per_unit to numeric, replacing empty strings and errors with 0
        df['quantity'] = pd.to_numeric(df['quantity'].replace('', '0'), errors='coerce').fillna(0)
        df = map_mandays(df)       
        df, total_mandays, total_cost = calculate_mandays_and_costs(df)
        csv_string = df.to_csv(index=False)
        # Insert the quotation into the database
        insert_quotation(csv_string, total_cost, total_mandays)

        # Format output string
        cost_summary = f"""
        Total Mandays: {total_mandays:.2f}
        Total Cost: ${total_cost:,.2f}
        """
        return df, cost_summary
        
    except Exception as e:
        return None, f"Error generating CSV: {str(e)}"

def generate_csv_v2(progress=gr.Progress()):
    # Step 1: Rewrite QA
    progress(0.33, desc="Step 1: Rewriting QA...")
    structured_qa_result = zus_quotation.rewrite_qa()
    
    # Step 2: Flare Tasks
    progress(0.66, desc="Step 2: Calling flare tasks...")
    flare_tasks_result = zus_quotation.flare_tasks()
    
    df = pd.read_csv(StringIO(flare_tasks_result))
    df['quantity'] = pd.to_numeric(df['quantity'].replace('', '1'), errors='coerce').fillna(1)
    df['mandays_per_unit'] = pd.to_numeric(df['mandays_per_unit'].replace('', '0'), errors='coerce').fillna(0)
    df, total_mandays, total_cost = calculate_mandays_and_costs(df)
    csv_string = df.to_csv(index=False)

    insert_quotation(csv_string, total_cost, total_mandays)

    progress(1.0, desc="Complete!")
    return [df, "Process completed!", f"total_man_days: {total_mandays}\n total_costs:{total_cost}"]

def generate_csv_v3(progress=gr.Progress()):
    # Step 1: Rewrite QA
    progress(0, desc="Step 1: Rewriting QA...")
    structured_qa_result = zus_quotation.rewrite_qa()
    
    # Step 2: Flare Tasks
    progress(0.33, desc="Step 2: Calling flare tasks...")
    flare_tasks_result = zus_quotation.flare_tasks(flare_task_quotationv3)
    
    # Step 3: Populate Template with Flared Task
    progress(0.66, desc="Step 3: Populating quantity...")
    organized_qa_result = zus_quotation.populate_template_with_orgranised_qa()
    task_breakdown_df = pd.read_csv(StringIO(flare_tasks_result))
    quantity_df = pd.read_csv(StringIO(organized_qa_result))

    # df['quantity'] = pd.to_numeric(df['quantity'].replace('', '1'), errors='coerce').fillna(1)
    # df['mandays_per_unit'] = pd.to_numeric(df['mandays_per_unit'].replace('', '0'), errors='coerce').fillna(0)
    # df, total_mandays, total_cost = calculate_mandays_and_costs(df)
    # csv_string = df.to_csv(index=False)

    # insert_quotation(csv_string, total_cost, total_mandays)

    progress(1.0, desc="Complete!")
    return [task_breakdown_df,quantity_df, "Process completed!", "total_man_days: {total_mandays}\n total_costs:{total_cost}"]


def create_new_session():
    """Create a new session in the database and return the session_id"""
    try:
        conn = get_db_connection()
        cur = conn.cursor()
        
        # Insert new session with start time
        cur.execute("""
            INSERT INTO sessions (start_time)
            VALUES (CURRENT_TIMESTAMP)
            RETURNING session_id
        """)
        
        session_id = cur.fetchone()[0]
        
        # Insert session_base_project record for "Engage"
        cur.execute("""
            INSERT INTO session_base_project (session_id, base_project_name)
            VALUES (%s, 'Engage')
        """, (session_id,))
        
        conn.commit()
        cur.close()
        conn.close()
        
        return session_id
    except Exception as e:
        print(f"Error creating new session: {str(e)}")
        return None

def start_chat():
    """Initialize chat with first question and create new session"""
    # Create new session and get session_id
    session_id = create_new_session()
    
    # Set the rubric and session_id for the project
    section_name_list, rubric_list = get_section_name_and_rubric_list()
    component_list = get_latest_components()
    
    # Update session_id in Project instance
    zus_quotation.session_id = session_id
    
    zus_quotation.set_rubric(rubric_list)
    zus_quotation.set_rubric_section_names(section_name_list)
    zus_quotation.set_component_list(component_list)
    
    initial_history = [(None, client_initial_question)]
    return client_initial_question, initial_history

def refresh_components():
    """Refresh component list and update quotation"""
    component_list = get_latest_components()
    zus_quotation.set_component_list(component_list)
    print("successfully updated components list")
    # Generate new CSV to reflect updated components
    return

def get_project_state():
    """Get current state of zus_quotation project"""
    # Create status boxes
    status = f"""Session ID: {zus_quotation.session_id}
    Rubric Loaded: {bool(zus_quotation.rubric)}
    Components Loaded: {bool(zus_quotation.component_list)}
    Requirements Loaded: {bool(zus_quotation.project_detail)}"""
    
    # Format requirements as a table if they exist
    requirements_table = ""
    if zus_quotation.project_detail:
        print(f"\n\nrequirements : {type(zus_quotation.project_detail)}")
        # Create markdown box for requirements
        # requirements_table = "\n\n### Project Requirements\n```markdown\n"
        for index,requirement in enumerate(list(zus_quotation.project_detail)):
            requirements_table += f"\n_____________\n"
            requirements_table += f"#Requirement {index+1}\n {requirement}"
    
    return status, requirements_table

def fetch_session(session_id):
    """Fetch session details from database and initialize project state"""
    try:
        # 1. Fetch session details
        conn = get_db_connection()
        cur = conn.cursor(cursor_factory=RealDictCursor)
        
        cur.execute("""
            SELECT project_requirement, start_time
            FROM sessions
            WHERE session_id = %s
        """, (session_id,))
        
        session = cur.fetchone()
        cur.close()
        conn.close()

        print(session)
        if session:
            # 2. Update zus_quotation with session data
            zus_quotation.session_id = session_id
            
            # Set project requirements if they exist
            if session['project_requirement']:
                try:
                    # Check if the project requirement is a string
                    if isinstance(session['project_requirement'], str):
                        # Attempt to parse it as JSON
                        try:
                            requirements = json.loads(session['project_requirement'])
                        except json.JSONDecodeError:
                            # If JSON parsing fails, split the string into a list
                            requirements = session['project_requirement'].split('\n')  # or use another delimiter if needed
                    else:
                        requirements = session['project_requirement']
                    
                    # Clear existing details and set new ones
                    zus_quotation.project_detail = []
                    for requirement in requirements:
                        zus_quotation.add_project_detail(requirement.strip())  # Use strip() to remove any leading/trailing whitespace
                except Exception as e:
                    return "", "", f"Error processing project requirements in session {session_id}: {str(e)}"
            
            # 3. Fetch and set rubric
            section_name_list, rubric_list = get_section_name_and_rubric_list()
            zus_quotation.set_rubric(rubric_list)
            zus_quotation.set_rubric_section_names(section_name_list)
            
            # 4. Fetch and set components
            component_list = get_latest_components()
            zus_quotation.set_component_list(component_list)
            
            return (*get_project_state(), f"Successfully loaded session {session_id} with all data")
                    # "\n".join(rubric_list),  # Return rubric list as a string
                    # component_list)  # Ensure to extract string values
            
        else:
            return "", "", f"Session {session_id} not found"
            # return "", "", f"Session {session_id} not found", "", ""

    except Exception as e:
        return "", "", f"Error fetching session: {str(e)}",
        # return "", "", f"Error fetching session: {str(e)}", "", ""

def insert_quotation(csv_string, total_price, total_mandays, note=None, details = None, tier_level=1):
    """Insert a new quotation into the database with an updated version."""
    try:
        conn = get_db_connection()
        cur = conn.cursor()
        
        # Fetch the current maximum version for the given session_id
        cur.execute("""
            SELECT COALESCE(MAX(version), 0) + 1
            FROM quotations
            WHERE session_id = %s
        """, (zus_quotation.session_id,))
        
        result = cur.fetchone()
        version = result[0] if result else 1  # Default to version 1 if no result
 # Get the next version number
        total_price = float(total_price) if total_price is not None else None
        total_mandays = float(total_mandays) if total_mandays is not None else None
        
        details = f"{json.dumps(zus_quotation.project_detail)} + {details}" if details else json.dumps(zus_quotation.project_detail)
            
        # Insert new quotation
        cur.execute("""
            INSERT INTO quotations (session_id, version, details, quotation_csv, total_price, total_mandays)
            VALUES (%s, %s, %s, %s, %s, %s)
        """, (
            zus_quotation.session_id,
            version,
            details,  # Convert project details to JSON string
            csv_string,
            total_price,
            total_mandays
        ))
        
        conn.commit()
        cur.close()
        conn.close()

        print("Successfully inserted quotation")

        
    except Exception as e:
        print(f"Error inserting quotation: {str(e)}")

def save_csv(df):
    """Save the DataFrame as a CSV file."""
    if df is not None:
        csv_file_path = "task_list.csv"  # Specify your desired file path
        df.to_csv(csv_file_path, index=False)
        print(f"CSV saved to {csv_file_path}")
        return f"CSV saved to {csv_file_path}"
    return "No data to save."


def save_csv_v3(df, df2):
    """Save the DataFrame as a CSV file."""
    retval = ""
    if df is not None :
        csv_file_path = "task_list.csv"  # Specify your desired file path
        df.to_csv(csv_file_path, index=False)
        retval += f"Task CSV saved to {csv_file_path}\n"
    if df2 is not None :
        quantity_file_path = "quantity_list.csv"  # Specify your desired file path
        df2.to_csv(quantity_file_path, index=False)
        retval += f"Task CSV saved to {quantity_file_path}\n"
    
    with open("project_requirement.txt", "w") as file:
        file.write(zus_quotation.structured_qa)
        retval += f"Project Requirement saved to project_requirement.txt"

    if retval:
        return retval
    return "No data to save."


gr.set_static_paths(["temp/"])
with gr.Blocks(title="Requirements Gathering Chatbot") as demo:

    gr.Markdown("# Requirements Gathering Chatbot")
    with gr.Tab(label= "Main"):
        gr.Markdown("### Instructions for Use - 2 options :")
        gr.Markdown("1. **Start a New Session**: Begin answering questions for a new project. Please make sure to include the original questions in your replies.")
        gr.Markdown("2. **Load an Existing Project**: Go to the **Project Status** tab. Select a session using its **Session ID** (e.g. id: 7) to review previous details.")
        gr.Markdown("**Current Limitation**: Cannot add new answers to exisiting session")

        with gr.Row():
            start_btn = gr.Button("Start New Session")
            clear_btn = gr.Button("Clear")

        with gr.Row():
            with gr.Row():
                chatbot = gr.Chatbot(height=510)
                with gr.Column():
                    current_question = gr.Textbox(label="Edit Area", lines= 20)
                    with gr.Row():
                        submit_btn = gr.Button("Submit")
        
        with gr.Tab(label= "Quotation Generator V1"):
            gr.Markdown("## Quotation 1 - Generate a Quotation")
            with gr.Row():

                # current_question = gr.Textbox(label="Edit Area", lines= 15)
                # score_display = gr.Textbox(label="Progress", interactive=False)
                with gr.Column(scale = 4):
                    units_table_v1 = gr.Dataframe(interactive=True, col_count=7)  # New table component

                with gr.Column(scale = 1):
                    generate_btn_v1 = gr.Button("Generate Quotation V1")  # New button
                    units_output_v1 = gr.Textbox(label="Cost Summary", lines=3, interactive=False)
                    recalc_btn_v1 = gr.Button("Recalculate")  # New recalculate button
                    # refresh_components_btn = gr.Button("Get Latest Component List")  # New refresh button
        
        with gr.Tab(label= "Quotation Generator V2"):
            gr.Markdown("#Logic : Rewrites Project Requirement -> Flare Task,Quantity,Mandays -> Calculate")
            gr.Markdown("#No sessions, CSV download pending")

            with gr.Row():
                # current_question = gr.Textbox(label="Edit Area", lines= 15)
                # score_display = gr.Textbox(label="Progress", interactive=False)
                with gr.Column(scale = 4):
                    units_table_v2 = gr.Dataframe(interactive=True, col_count=7)  # New table component

                with gr.Column(scale = 1):
                    generate_btn_v2 = gr.Button("Generate Task List V2")  # New button
                    units_output_v2 = gr.Textbox(label="Cost Summary", lines=3, interactive=False)
                    progress_update = gr.Textbox(label="Progress Update", lines=2, interactive=False)
                    recalc_btn_v2 = gr.Button("Recalculate")  # New recalculate button
                    save_csv_btn = gr.Button("Save CSV")  # New Save CSV button
                    # refresh_components_btn = gr.Button("Get Latest Component List")  # New refresh button
        
        with gr.Tab(label= "Quotation Generator V3"):
            gr.Markdown("#Logic : Rewrites Project Requirement -> Flare Task,Mandays -> Identify Quantity & Remarks -> Calculate")
            #pending Gap Report
            with gr.Row():
                with gr.Column(scale=4):

                    gr.Markdown("# Task Breakdown Table")
                    tasks_table_v3 = gr.Dataframe(interactive=True, col_count=7)  # New table component
                    gr.Markdown("# Inferred Quantity Table")
                    quantity_table_v3 = gr.Dataframe(interactive=True, col_count=7)  # New table component

                with gr.Column(scale=1):
                    generate_btn_v3 = gr.Button("Generate Task List V3")  # New button
                    units_output_v3 = gr.Textbox(label="Cost Summary", lines=3, interactive=False)
                    units_output_v3 = gr.Textbox(label="Cost Summary", lines=3, interactive=False)
                    progress_update_v3 = gr.Textbox(label="Progress Update", lines=2, interactive=False)
                    recalc_btn_v3 = gr.Button("Recalculate")  # New recalculate button
                    save_csv_btn_v3 = gr.Button("Save CSV")  # New Save CSV button

            generate_btn_v3.click(
                fn=generate_csv_v3,  # Assuming the same function is used
                outputs=[tasks_table_v3,quantity_table_v3, progress_update_v3, units_output_v3]
            )

            recalc_btn_v3.click(
                fn=recalculate_costs_v3,  # Assuming the same function is used
                inputs=[quantity_table_v3,tasks_table_v3],
                outputs=[quantity_table_v3,progress_update_v3, units_output_v3]
            )

            save_csv_btn_v3.click(
                fn=save_csv_v3,
                inputs=[tasks_table_v3,quantity_table_v3],
                outputs=progress_update_v3
            )

            # save_csv_btn_v3.click(
            #     fn=save_csv,
            #     inputs=tasks_table_v3,
            #     outputs=progress_update_v3
            # )

    # Replace single textbox with separate components
    with gr.Tab(label= "Project Status"):
        gr.Markdown("### Past submissions")
        gr.Markdown("Quick hack to load past submissions to regenarate quotations (This page displays Q&A only; previous quotations are not shown yet).")
        gr.Markdown("Use Session ID 7 for test")

        with gr.Row():
            session_input = gr.Number(label="Session ID", precision=0)
            message_box = gr.Textbox(label="Message", interactive=False)
            status_box = gr.Textbox(
                label="Project Status", 
                value="",
                interactive=False
            )
        fetch_btn = gr.Button("Fetch Session")
        
        with gr.Tab(label= "Requirement"):
            fetched_requirements_box = gr.Markdown(
                value=""
            )
       
    # Event handlers
    start_btn.click(
        fn=lambda: (*start_chat(), *get_project_state()),
        outputs=[current_question, chatbot, status_box, fetched_requirements_box]
    )
    
    submit_btn.click(
        fn=lambda answer, history: (*process_response(answer, history), *get_project_state()),
        inputs=[current_question, chatbot],
        outputs=[chatbot, current_question, status_box, fetched_requirements_box]
    )

    
    
    clear_btn.click(
        fn=lambda: ([], ""),
        outputs=[chatbot, current_question]
    )

    generate_btn_v1.click(
        fn=generate_csv_v1,
        outputs=[units_table_v1, units_output_v1]
    )
    
    recalc_btn_v1.click(
        fn=recalculate_costs,
        inputs=[units_table_v1],
        outputs=[units_table_v1, units_output_v1]
    )

    # refresh_components_btn.click(
    #     fn=refresh_components
    # )

    fetch_btn.click(
        fn=fetch_session,
        inputs=[session_input],
        outputs=[status_box, fetched_requirements_box, message_box]
        # outputs=[status_box, fetched_requirements_box, message_box, fetched_rubric_box, fetched_component_box]
    )
    # Update the button to call the new function
    generate_btn_v2.click(
                generate_csv_v2,
                outputs=[units_table_v2, progress_update,units_output_v2]
        )

    recalc_btn_v2.click(
        fn=recalculate_costs_v2,
        inputs=[units_table_v2],
        outputs=[units_table_v2, progress_update,units_output_v2]
    )

    # save_csv_btn.click(
    #     fn=save_csv,
    #     inputs=units_table_v2,
    #     outputs=progress_update
    # )
    save_csv_btn.click(
        fn=save_csv,
        inputs=tasks_table_v3,
        outputs=progress_update
    )
if __name__ == "__main__":
    # Assign interface to demo for hot reloading
    demo.launch(share=True)
    # print(get_latest_components())