import gradio as gr import sqlite3 from dog_database import get_dog_description from breed_health_info import breed_health_info from breed_noise_info import breed_noise_info def create_comparison_tab(dog_breeds, get_dog_description, breed_noise_info, breed_health_info): breed_display_mapping = { breed: breed.replace('_', ' ').title() for breed in dog_breeds} reversed_mapping = {v, k for k ,v in breed_display_mapping.items()} sorted_display_names = sorted(breed_display_mapping.values()) with gr.TabItem("Breed Comparison"): gr.HTML("

Select two dog breeds to compare their characteristics and care requirements.

") with gr.Row(): breed1_dropdown = gr.Dropdown( choices=dog_breeds, label="Select First Breed", value="Golden_Retriever" ) breed2_dropdown = gr.Dropdown( choices=dog_breeds, label="Select Second Breed", value="Border_Collie" ) compare_btn = gr.Button("Compare Breeds") comparison_output = gr.HTML(label="Comparison Results") def format_noise_data(notes): characteristics = [] triggers = [] noise_level = "Moderate" # 預設值 if isinstance(notes, str): lines = notes.strip().split('\n') section = "" for line in lines: line = line.strip() if "Typical noise characteristics:" in line: section = "characteristics" elif "Barking triggers:" in line: section = "triggers" elif "Noise level:" in line: noise_level = line.split(':')[1].strip() elif line.startswith('•'): if section == "characteristics": characteristics.append(line[1:].strip()) elif section == "triggers": triggers.append(line[1:].strip()) return { 'characteristics': characteristics, 'triggers': triggers, 'noise_level': noise_level } def format_health_data(notes): considerations = [] screenings = [] if isinstance(notes, str): lines = notes.strip().split('\n') current_section = None for line in lines: line = line.strip() # 修正字串比對 if "Common breed-specific health considerations" in line: current_section = "considerations" elif "Recommended health screenings:" in line: current_section = "screenings" elif line.startswith('•'): item = line[1:].strip() if current_section == "considerations": considerations.append(item) elif current_section == "screenings": screenings.append(item) # 只有當真的沒有資料時才返回 "Information not available" if not considerations and not screenings: return { 'considerations': ["Information not available"], 'screenings': ["Information not available"] } return { 'considerations': considerations, 'screenings': screenings } def get_comparison_styles(): return """ .comparison-container { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; padding: 20px; } .breed-column { background: white; border-radius: 10px; padding: 24px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .section-title { font-size: 24px; color: #2D3748; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 2px solid #E2E8F0; } .info-section { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; margin-bottom: 24px; } .info-item { position: relative; background: #F8FAFC; padding: 16px; border-radius: 8px; border: 1px solid #E2E8F0; } .info-label { display: flex; align-items: center; gap: 8px; color: #4A5568; font-size: 0.9em; margin-bottom: 4px; } .info-icon { cursor: help; background: #E2E8F0; width: 18px; height: 18px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; color: #4A5568; margin-left: 4px; } .info-icon:hover + .tooltip-content { display: block; } .tooltip-content { display: none; position: absolute; background: #2D3748; color: #FFFFFF; padding: 8px 12px; border-radius: 6px; font-size: 14px; line-height: 1.3; width: max-content; max-width: 280px; z-index: 1000; top: 0; /* 修改位置 */ left: 100%; margin-left: 10px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); white-space: normal; /* 允許文字換行 */ } .tooltip-content, .tooltip-content *, .tooltip-content strong, .tooltip-content li, .tooltip-content ul, .tooltip-content p, .tooltip-content span, .tooltip-content div { color: #FFFFFF !important; } .tooltip-content::before { content: ''; position: absolute; left: -6px; top: 14px; /* 配合上方位置調整 */ border-width: 6px; border-style: solid; border-color: transparent #2D3748 transparent transparent; } .tooltip-content strong { color: #FFFFFF; display: block; margin-bottom: 4px; font-weight: 600; } .tooltip-content ul { margin: 0; padding-left: 16px; color: #FFFFFF; } .tooltip-content * { color: #FFFFFF; } .tooltip-content li { margin-bottom: 2px; color: #FFFFFF; } .tooltip-content li::before { color: #FFFFFF !important; } .tooltip-content br { display: block; margin: 2px 0; } .info-value { color: #2D3748; font-weight: 500; } .characteristic-section { background: #F8FAFC; padding: 20px; border-radius: 8px; margin-bottom: 20px; } .subsection-title { font-size: 18px; color: #2D3748; margin-bottom: 16px; display: flex; align-items: center; gap: 8px; } .noise-level { background: #EDF2F7; padding: 16px; border-radius: 6px; margin: 16px 0; border: 1px solid #CBD5E0; } .level-label { color: #4A5568; font-size: 1.1em; font-weight: 500; margin-bottom: 8px; } .level-value { color: #2D3748; font-size: 1.2em; font-weight: 600; } .characteristics-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-top: 12px; } .characteristic-item { background: white; padding: 12px; border-radius: 6px; border: 1px solid #E2E8F0; color: #4A5568; } .health-insights { margin-top: 24px; } .health-grid { display: grid; grid-template-columns: 1fr; gap: 8px; } .health-item { background: white; padding: 12px; border-radius: 6px; border: 1px solid #E2E8F0; color: #E53E3E; } .screening-item { background: white; padding: 12px; border-radius: 6px; border: 1px solid #E2E8F0; color: #38A169; } .learn-more-btn { display: inline-block; margin-top: 20px; padding: 12px 24px; background: linear-gradient(135deg, #2B6CB0, #2C5282); color: white; text-decoration: none; border-radius: 6px; transition: all 0.3s ease; text-align: center; width: 100%; font-weight: 500; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .learn-more-btn:hover { background: linear-gradient(135deg, #2C5282, #2B6CB0); transform: translateY(-2px); box-shadow: 0 4px 6px rgba(0,0,0,0.1); } .info-disclaimer { margin-top: 24px; padding: 16px; background: #F7FAFC; border-radius: 8px; font-size: 0.9em; color: #4A5568; line-height: 1.5; border-left: 4px solid #4299E1; } @media (max-width: 768px) { .comparison-container { grid-template-columns: 1fr; } .info-section { grid-template-columns: 1fr; } .characteristics-grid { grid-template-columns: 1fr; } } """ def show_comparison(breed1, breed2): if not breed1 or not breed2: return "Please select two breeds to compare" breed1_info = get_dog_description(breed1) breed2_info = get_dog_description(breed2) breed1_noise = breed_noise_info.get(breed1, {}) breed2_noise = breed_noise_info.get(breed2, {}) breed1_health = breed_health_info.get(breed1, {}) breed2_health = breed_health_info.get(breed2, {}) def create_info_item(label, value, tooltip_text=""): tooltip = f"""
{label}
i
{tooltip_text}
""" if tooltip_text else f'
{label}
' return f"""
{tooltip}
{value}
""" def create_breed_section(breed, info, noise_info, health_info): # 建立提示文字 section_tooltips = { 'Size': """ Size Categories:
• Small: Under 20 pounds
• Medium: 20-60 pounds
• Large: Over 60 pounds """, 'Exercise': """ Exercise Needs:
• Low: Short walks suffice
• Moderate: 1-2 hours daily
• High: 2+ hours daily activity
• Very High: Intensive daily exercise """, 'Grooming': """ Grooming Requirements:
• Low: Occasional brushing
• Moderate: Weekly grooming
• High: Daily maintenance needed """, 'Children': """ Compatibility with Children:
• Yes: Excellent with kids
• Moderate: Good with supervision
• No: Better with older children """, 'Lifespan': """ Average Lifespan Range:
Typical lifespan for this breed with proper care and genetics """, 'noise': """ Noise Behavior Information:
• Noise Level indicates typical vocalization intensity
• Characteristics describe common vocal behaviors
• Triggers list common causes of barking or vocalization """, 'health': """ Health Information:
• Health considerations are breed-specific concerns
• Screenings are recommended preventive tests
• Always consult with veterinary professionals """ } noise_data = format_noise_data(noise_info.get('noise_notes', '')) health_data = format_health_data(health_info.get('health_notes', '')) def create_section_header(title, icon, tooltip_text): return f"""
{icon} {title} {tooltip_text}
""" return f"""

🐕 {breed.replace('_', ' ')}

{create_info_item('Size', info['Size'], section_tooltips['Size'])} {create_info_item('Exercise', info['Exercise Needs'], section_tooltips['Exercise'])} {create_info_item('Grooming', info['Grooming Needs'], section_tooltips['Grooming'])} {create_info_item('With Children', info['Good with Children'], section_tooltips['Children'])} {create_info_item('Lifespan', info['Lifespan'], section_tooltips['Lifespan'])}
{create_section_header('Noise Behavior', '🔊', section_tooltips['noise'])}
Noise Level
{noise_data['noise_level'].upper()}

Typical Characteristics

{' '.join([f'
{char}
' for char in noise_data['characteristics']])}

Barking Triggers

{' '.join([f'
{trigger}
' for trigger in noise_data['triggers']])}
{create_section_header('Health Insights', '🏥', section_tooltips['health'])}

Health Considerations

{' '.join([f'
{item}
' for item in health_data['considerations']])}

Recommended Screenings

{' '.join([f'
{item}
' for item in health_data['screenings']])}
🌐 Learn more about {breed.replace('_', ' ')} on AKC
""" html_output = f"""
{create_breed_section(breed1, breed1_info, breed1_noise, breed1_health)} {create_breed_section(breed2, breed2_info, breed2_noise, breed2_health)}
Note: The health and behavioral information provided is for general reference only. Individual dogs may vary, and characteristics can be influenced by training, socialization, and genetics. Always consult with veterinary professionals for specific health advice and professional trainers for behavioral guidance.
""" return html_output compare_btn.click( show_comparison, inputs=[breed1_dropdown, breed2_dropdown], outputs=comparison_output ) return { 'breed1_dropdown': breed1_dropdown, 'breed2_dropdown': breed2_dropdown, 'compare_btn': compare_btn, 'comparison_output': comparison_output }