File size: 24,712 Bytes
111f3b5
 
 
 
 
 
 
 
533e893
 
111f3b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import gradio as gr
import matplotlib.pyplot as plt
import numpy as np
import sqlite3
from matplotlib.figure import Figure
from typing import Dict, List, Optional, Tuple
import pandas as pd
from PIL import Image
from dog_database import get_dog_description
from scoring_calculation_system import UserPreferences, calculate_compatibility_score

def create_visualization_tab(dog_breeds, get_dog_description, calculate_compatibility_score, UserPreferences):
    """Create a visualization tab for breed characteristic analysis"""
    
    # Create shared state container
    shared_preferences = gr.State({
        "living_space": "apartment",
        "yard_access": "no_yard",
        "exercise_time": 60,
        "exercise_type": "moderate_activity",
        "grooming_commitment": "medium",
        "experience_level": "beginner",
        "noise_tolerance": "medium",
        "has_children": False,
        "children_age": "school_age",
        "climate": "moderate"
    })
    
    gr.HTML("""
        <div style='
            text-align: center;
            padding: 20px 0;
            margin: 15px 0;
            background: linear-gradient(to right, rgba(66, 153, 225, 0.1), rgba(72, 187, 120, 0.1));
            border-radius: 10px;
        '>
            <p style='
                font-size: 1.2em;
                margin: 0;
                padding: 0 20px;
                line-height: 1.5;
                background: linear-gradient(90deg, #4299e1, #48bb78);
                -webkit-background-clip: text;
                -webkit-text-fill-color: transparent;
                font-weight: 600;
            '>
                Gain deeper insight into dog breed characteristics through visualization to help you make a more informed choice.
            </p>
        </div>
    """)
    
    with gr.Tabs():
        # Single breed radar chart analysis tab
        with gr.TabItem("Breed Radar Chart Analysis"):
            with gr.Row():
                with gr.Column(scale=1):
                    # User interface components - Left side
                    breed_choices = [(breed.replace('_', ' '), breed) for breed in sorted(dog_breeds)]
                    
                    breed_dropdown = gr.Dropdown(
                        label="Select Breed",
                        choices=breed_choices,
                        value=breed_choices[0][1] if breed_choices else None,
                        info="Select a breed to view its characteristics radar chart"
                    )
                    
                    with gr.Accordion("User Preferences (Affects Scoring)", open=False):
                        living_space = gr.Radio(
                            label="Living Space",
                            choices=["apartment", "house_small", "house_large"],
                            value="apartment",
                            info="Your residential environment type"
                        )
                        
                        yard_access = gr.Radio(
                            label="Yard Condition",
                            choices=["no_yard", "shared_yard", "private_yard"],
                            value="no_yard",
                            info="Whether you have yard space"
                        )
                        
                        exercise_time = gr.Slider(
                            label="Daily Exercise Time (minutes)",
                            minimum=15,
                            maximum=180,
                            value=60,
                            step=15,
                            info="Daily exercise time you can provide"
                        )
                        
                        exercise_type = gr.Radio(
                            label="Exercise Type",
                            choices=["light_walks", "moderate_activity", "active_training"],
                            value="moderate_activity",
                            info="Your preferred exercise method"
                        )
                        
                        grooming_commitment = gr.Radio(
                            label="Grooming Commitment",
                            choices=["low", "medium", "high"],
                            value="medium",
                            info="Level of grooming care you're willing to provide"
                        )
                        
                        experience_level = gr.Radio(
                            label="Experience Level",
                            choices=["beginner", "intermediate", "advanced"],
                            value="beginner",
                            info="Your level of dog owning experience"
                        )
                        
                        noise_tolerance = gr.Radio(
                            label="Noise Tolerance",
                            choices=["low", "medium", "high"],
                            value="medium",
                            info="Your acceptance level of dog barking"
                        )
                        
                        has_children = gr.Checkbox(
                            label="Have Children",
                            value=False,
                            info="Whether you have children at home"
                        )
                        
                        children_age = gr.Radio(
                            label="Children's Age",
                            choices=["toddler", "school_age", "teenager"],
                            value="school_age",
                            visible=False,
                            info="Age group of children at home"
                        )
                        
                        climate = gr.Radio(
                            label="Climate Environment",
                            choices=["cold", "moderate", "hot"],
                            value="moderate",
                            info="Climate characteristics of your living area"
                        )
                        
                        # Listen for has_children changes to control children_age display
                        has_children.change(
                            fn=lambda x: gr.update(visible=x),
                            inputs=has_children,
                            outputs=children_age
                        )
                        
                        # Add function to update shared preferences
                        def update_shared_preferences(*args):
                            return {
                                "living_space": args[0],
                                "yard_access": args[1],
                                "exercise_time": args[2],
                                "exercise_type": args[3],
                                "grooming_commitment": args[4],
                                "experience_level": args[5],
                                "noise_tolerance": args[6],
                                "has_children": args[7],
                                "children_age": args[8],
                                "climate": args[9]
                            }
                        
                        # Monitor preference changes and update shared state
                        all_preferences = [living_space, yard_access, exercise_time, 
                                          exercise_type, grooming_commitment, experience_level, 
                                          noise_tolerance, has_children, children_age, climate]
                        
                        for pref in all_preferences:
                            pref.change(
                                update_shared_preferences,
                                inputs=all_preferences,
                                outputs=shared_preferences
                            )
                    
                    generate_btn = gr.Button("Generate Radar Chart", variant="primary")
                
                with gr.Column(scale=2):
                    # Right display area
                    radar_plot = gr.Plot(label="Breed Characteristics Radar Chart")
                    breed_details = gr.JSON(label="Breed Detailed Information")
            
            # Button click event
            generate_btn.click(
                fn=lambda *args: generate_radar_chart(
                    args[0], create_user_preferences(*args[1:]), 
                    get_dog_description, calculate_compatibility_score
                ),
                inputs=[breed_dropdown, living_space, yard_access, exercise_time, 
                       exercise_type, grooming_commitment, experience_level, 
                       noise_tolerance, has_children, children_age, climate],
                outputs=[radar_plot, breed_details]
            )
        
        # Breed comparison analysis tab - Improved version
        with gr.TabItem("Breed Comparison Analysis"):
            with gr.Row():
                breed1_dropdown = gr.Dropdown(
                    label="Select First Breed",
                    choices=breed_choices,
                    value=breed_choices[0][1] if breed_choices else None
                )
                
                breed2_dropdown = gr.Dropdown(
                    label="Select Second Breed",
                    choices=breed_choices,
                    value=breed_choices[1][1] if len(breed_choices) > 1 else None
                )
            
            with gr.Row():
                use_shared_settings = gr.Checkbox(
                    label="Use Radar Chart Analysis Settings",
                    value=True,
                    info="Check to use the same preferences from the Radar Chart Analysis tab"
                )
            
            # Custom settings container - only visible when not using shared settings
            with gr.Column(visible=False) as custom_settings:
                with gr.Accordion("Custom Preferences", open=True):
                    comp_living_space = gr.Radio(
                        label="Living Space",
                        choices=["apartment", "house_small", "house_large"],
                        value="apartment"
                    )
                    
                    comp_yard_access = gr.Radio(
                        label="Yard Condition",
                        choices=["no_yard", "shared_yard", "private_yard"],
                        value="no_yard"
                    )
                    
                    comp_exercise_time = gr.Slider(
                        label="Daily Exercise Time (minutes)",
                        minimum=15,
                        maximum=180,
                        value=60,
                        step=15
                    )
                    
                    comp_exercise_type = gr.Radio(
                        label="Exercise Type",
                        choices=["light_walks", "moderate_activity", "active_training"],
                        value="moderate_activity"
                    )
            
            # Toggle custom settings visibility based on checkbox
            use_shared_settings.change(
                fn=lambda x: gr.update(visible=not x),
                inputs=use_shared_settings,
                outputs=custom_settings
            )
            
            compare_btn = gr.Button("Compare Breeds", variant="primary")
            comparison_plot = gr.Plot(label="Breed Characteristics Comparison")
            
            # Improved comparison function that handles both shared and custom settings
            def get_comparison_settings(use_shared, shared_prefs, *custom_prefs):
                """
                Select appropriate settings based on user choice
                
                Args:
                    use_shared: Boolean indicating whether to use shared settings
                    shared_prefs: Dictionary of shared preferences
                    custom_prefs: Custom preference values if not using shared
                    
                Returns:
                    UserPreferences object with the selected settings
                """
                if use_shared:
                    # Use settings from Radar Chart tab
                    return create_user_preferences_from_dict(shared_prefs)
                else:
                    # Use custom settings from Comparison tab
                    return create_user_preferences(
                        custom_prefs[0], custom_prefs[1], custom_prefs[2], custom_prefs[3],
                        "medium", "beginner", "medium", False, "school_age", "moderate"
                    )
            
            # Connect the comparison button
            compare_btn.click(
                fn=lambda breed1, breed2, use_shared, shared_prefs, *custom_prefs: generate_comparison_chart(
                    breed1, breed2, 
                    get_comparison_settings(use_shared, shared_prefs, *custom_prefs),
                    get_dog_description, calculate_compatibility_score
                ),
                inputs=[
                    breed1_dropdown, breed2_dropdown, 
                    use_shared_settings, shared_preferences,
                    comp_living_space, comp_yard_access, 
                    comp_exercise_time, comp_exercise_type
                ],
                outputs=comparison_plot
            )

    return None

def create_user_preferences(living_space, yard_access, exercise_time, exercise_type, 
                          grooming_commitment, experience_level, noise_tolerance, 
                          has_children, children_age, climate):
    """
    Create UserPreferences object from UI inputs
    
    Args:
        living_space: Type of living environment
        yard_access: Yard availability
        exercise_time: Minutes of daily exercise
        exercise_type: Type of exercise activity
        grooming_commitment: Level of grooming commitment
        experience_level: Dog owner experience level
        noise_tolerance: Tolerance for barking
        has_children: Whether there are children in the home
        children_age: Age group of children
        climate: Climate type of the living area
        
    Returns:
        UserPreferences object with the specified settings
    """
    return UserPreferences(
        living_space=living_space,
        yard_access=yard_access,
        exercise_time=exercise_time,
        exercise_type=exercise_type,
        grooming_commitment=grooming_commitment,
        experience_level=experience_level,
        time_availability="moderate",  # Default value
        has_children=has_children,
        children_age=children_age if has_children else "school_age",
        noise_tolerance=noise_tolerance,
        space_for_play=True,  # Default value
        other_pets=False,  # Default value
        climate=climate
    )

def create_user_preferences_from_dict(prefs_dict):
    """
    Create UserPreferences object from a dictionary
    
    Args:
        prefs_dict: Dictionary containing preference values
        
    Returns:
        UserPreferences object populated with the dictionary values
    """
    return UserPreferences(
        living_space=prefs_dict["living_space"],
        yard_access=prefs_dict["yard_access"],
        exercise_time=prefs_dict["exercise_time"],
        exercise_type=prefs_dict["exercise_type"],
        grooming_commitment=prefs_dict["grooming_commitment"],
        experience_level=prefs_dict["experience_level"],
        time_availability="moderate",  # Default value
        has_children=prefs_dict["has_children"],
        children_age=prefs_dict["children_age"],
        noise_tolerance=prefs_dict["noise_tolerance"],
        space_for_play=True,  # Default value
        other_pets=False,  # Default value
        climate=prefs_dict["climate"]
    )

def generate_radar_chart(breed_name, user_prefs, get_dog_description, calculate_compatibility_score):
    """
    Generate radar chart for a single breed
    
    Args:
        breed_name: Dog breed name
        user_prefs: UserPreferences object
        get_dog_description: Function to get breed description
        calculate_compatibility_score: Function to calculate compatibility score
    
    Returns:
        tuple: (matplotlib figure, breed description dict)
    """
    try:
        # Get breed description
        breed_info = get_dog_description(breed_name)
        
        if not breed_info:
            # Create empty figure with error message
            fig = Figure(figsize=(8, 8))
            ax = fig.add_subplot(111)
            ax.text(0.5, 0.5, f"No information found for breed: {breed_name}",
                   horizontalalignment='center', verticalalignment='center', 
                   transform=ax.transAxes, fontsize=14)
            ax.axis('off')
            return fig, {"error": f"No information found for breed: {breed_name}"}
        
        # Calculate compatibility scores
        scores = calculate_compatibility_score(breed_info, user_prefs)
        
        # Prepare data for radar chart
        categories = ['Space Compatibility', 'Exercise Needs', 'Grooming', 
                     'Experience Required', 'Health', 'Noise Level']
        values = [scores['space'], scores['exercise'], scores['grooming'], 
                 scores['experience'], scores['health'], scores['noise']]
        
        # Close the polygon by appending first value
        values_closed = values + [values[0]]
        categories_closed = categories + [categories[0]]
        
        # Calculate angles for each category
        angles = np.linspace(0, 2*np.pi, len(categories), endpoint=False).tolist()
        angles += angles[:1]  # Close the loop
        
        # Create figure and polar axis
        fig = Figure(figsize=(10, 8))
        ax = fig.add_subplot(111, polar=True)
        
        # Plot data
        ax.fill(angles, values_closed, color='skyblue', alpha=0.25)
        ax.plot(angles, values_closed, color='blue', linewidth=2)
        
        # Add category labels
        ax.set_xticks(angles[:-1])
        ax.set_xticklabels(categories, fontsize=12)
        
        # Configure y-axis
        ax.set_yticks([0.2, 0.4, 0.6, 0.8, 1.0])
        ax.set_yticklabels(['0.2', '0.4', '0.6', '0.8', '1.0'], fontsize=10)
        ax.set_ylim(0, 1)
        
        # Add a title
        breed_display_name = breed_name.replace('_', ' ')
        ax.set_title(f"{breed_display_name} Characteristic Scores", fontsize=16, pad=20)
        
        # Add value labels at each point
        for i, (angle, value) in enumerate(zip(angles[:-1], values)):
            ax.text(angle, value + 0.05, f"{value:.2f}", 
                   ha='center', va='center', fontsize=10,
                   bbox=dict(facecolor='white', alpha=0.7, boxstyle="round,pad=0.3"))
        
        # Add grid
        ax.grid(True, linestyle='--', alpha=0.7)
        
        # Add overall score text
        overall_score = scores.get('overall', 0)
        fig.text(0.5, 0.02, f"Overall Match Score: {overall_score:.2f}", 
                ha='center', fontsize=14, 
                bbox=dict(facecolor='lightgreen', alpha=0.3, boxstyle="round,pad=0.5"))
        
        # Enhance aesthetics
        fig.patch.set_facecolor('#f8f9fa')
        ax.set_facecolor('#f0f0f0')
        
        # Print debug information
        print(f"Generated radar chart for {breed_name}")
        print(f"Scores: {scores}")
        
        return fig, breed_info
    
    except Exception as e:
        # Create empty figure with error message
        fig = Figure(figsize=(8, 8))
        ax = fig.add_subplot(111)
        ax.text(0.5, 0.5, f"Error generating chart: {str(e)}",
               horizontalalignment='center', verticalalignment='center', 
               transform=ax.transAxes, fontsize=14)
        ax.axis('off')
        print(f"Error in generate_radar_chart: {str(e)}")
        return fig, {"error": f"Error generating chart: {str(e)}"}

def generate_comparison_chart(breed1, breed2, user_prefs, get_dog_description, calculate_compatibility_score):
    """
    Generate comparison chart for two breeds
    
    Args:
        breed1, breed2: Dog breed names
        user_prefs: UserPreferences object
        get_dog_description: Function to get breed description
        calculate_compatibility_score: Function to calculate compatibility score
    
    Returns:
        matplotlib figure: Comparison chart
    """
    try:
        # Get breed descriptions
        breed1_info = get_dog_description(breed1)
        breed2_info = get_dog_description(breed2)
        
        if not breed1_info or not breed2_info:
            # Create empty figure with error message
            fig = Figure(figsize=(10, 6))
            ax = fig.add_subplot(111)
            ax.text(0.5, 0.5, f"Missing breed information. Please check both breeds.",
                   horizontalalignment='center', verticalalignment='center', 
                   transform=ax.transAxes, fontsize=14)
            ax.axis('off')
            return fig
        
        # Calculate compatibility scores
        scores1 = calculate_compatibility_score(breed1_info, user_prefs)
        scores2 = calculate_compatibility_score(breed2_info, user_prefs)
        
        # Prepare data for bar chart
        categories = ['Space Compatibility', 'Exercise Needs', 'Grooming', 
                     'Experience Required', 'Health', 'Noise Level']
        values1 = [scores1['space'], scores1['exercise'], scores1['grooming'], 
                  scores1['experience'], scores1['health'], scores1['noise']]
        values2 = [scores2['space'], scores2['exercise'], scores2['grooming'], 
                  scores2['experience'], scores2['health'], scores2['noise']]
        
        # Create figure
        fig = Figure(figsize=(12, 7))
        ax = fig.add_subplot(111)
        
        # Set width of bars
        x = np.arange(len(categories))
        width = 0.35
        
        # Plot bars
        breed1_display = breed1.replace('_', ' ')
        breed2_display = breed2.replace('_', ' ')
        
        rects1 = ax.bar(x - width/2, values1, width, label=breed1_display, color='#4299e1')
        rects2 = ax.bar(x + width/2, values2, width, label=breed2_display, color='#f56565')
        
        # Add labels and title
        ax.set_xlabel('Scoring Dimensions', fontsize=12)
        ax.set_ylabel('Score (0-1)', fontsize=12)
        ax.set_title(f'{breed1_display} vs {breed2_display} Breed Comparison', fontsize=15)
        ax.set_xticks(x)
        ax.set_xticklabels(categories, rotation=30, ha='right')
        ax.legend(loc='upper right')
        
        # Add value labels on top of bars
        def add_labels(rects):
            for rect in rects:
                height = rect.get_height()
                ax.annotate(f'{height:.2f}',
                            xy=(rect.get_x() + rect.get_width() / 2, height),
                            xytext=(0, 3),  # 3 points vertical offset
                            textcoords="offset points",
                            ha='center', va='bottom',
                            fontsize=9, fontweight='bold')
        
        add_labels(rects1)
        add_labels(rects2)
        
        # Set y-axis limit
        ax.set_ylim(0, 1.1)
        
        # Add grid
        ax.grid(True, linestyle='--', alpha=0.3, axis='y')
        
        # Add overall score comparison
        overall1 = scores1.get('overall', 0)
        overall2 = scores2.get('overall', 0)
        
        fig.text(0.5, 0.02, 
                f"Overall Match Scores:  {breed1_display}: {overall1:.2f}  |  {breed2_display}: {overall2:.2f}", 
                ha='center', fontsize=13, 
                bbox=dict(facecolor='#edf2f7', alpha=0.7, boxstyle="round,pad=0.5"))
        
        # Enhance aesthetics
        fig.patch.set_facecolor('#f8f9fa')
        ax.set_facecolor('#f0f0f0')
        
        # Add a tight layout to ensure everything fits
        fig.tight_layout(rect=[0, 0.05, 1, 0.95])
        
        # Print debug information
        print(f"Generated comparison chart for {breed1} vs {breed2}")
        
        return fig
    
    except Exception as e:
        # Create empty figure with error message
        fig = Figure(figsize=(10, 6))
        ax = fig.add_subplot(111)
        ax.text(0.5, 0.5, f"Error generating comparison: {str(e)}",
               horizontalalignment='center', verticalalignment='center', 
               transform=ax.transAxes, fontsize=14)
        ax.axis('off')
        print(f"Error in generate_comparison_chart: {str(e)}")
        return fig