from typing import List, Dict from breed_health_info import breed_health_info, default_health_note from breed_noise_info import breed_noise_info from dog_database import get_dog_description from scoring_calculation_system import UserPreferences, calculate_compatibility_score def format_recommendation_html(recommendations: List[Dict]) -> str: """將推薦結果格式化為HTML""" html_content = "
" for rec in recommendations: breed = rec['breed'] scores = rec['scores'] info = rec['info'] rank = rec.get('rank', 0) final_score = rec.get('final_score', scores['overall']) bonus_score = rec.get('bonus_score', 0) health_info = breed_health_info.get(breed, {"health_notes": default_health_note}) noise_info = breed_noise_info.get(breed, { "noise_notes": "Noise information not available", "noise_level": "Unknown", "source": "N/A" }) # 解析噪音資訊 noise_notes = noise_info.get('noise_notes', '').split('\n') noise_characteristics = [] barking_triggers = [] noise_level = '' current_section = None for line in noise_notes: line = line.strip() if 'Typical noise characteristics:' in line: current_section = 'characteristics' elif 'Noise level:' in line: noise_level = line.replace('Noise level:', '').strip() elif 'Barking triggers:' in line: current_section = 'triggers' elif line.startswith('•'): if current_section == 'characteristics': noise_characteristics.append(line[1:].strip()) elif current_section == 'triggers': barking_triggers.append(line[1:].strip()) # 生成特徵和觸發因素的HTML noise_characteristics_html = '\n'.join([f'
  • {item}
  • ' for item in noise_characteristics]) barking_triggers_html = '\n'.join([f'
  • {item}
  • ' for item in barking_triggers]) # 處理健康資訊 health_notes = health_info.get('health_notes', '').split('\n') health_considerations = [] health_screenings = [] current_section = None for line in health_notes: 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('•'): if current_section == 'considerations': health_considerations.append(line[1:].strip()) elif current_section == 'screenings': health_screenings.append(line[1:].strip()) health_considerations_html = '\n'.join([f'
  • {item}
  • ' for item in health_considerations]) health_screenings_html = '\n'.join([f'
  • {item}
  • ' for item in health_screenings]) # 獎勵原因計算 bonus_reasons = [] temperament = info.get('Temperament', '').lower() if any(trait in temperament for trait in ['friendly', 'gentle', 'affectionate']): bonus_reasons.append("Positive temperament traits") if info.get('Good with Children') == 'Yes': bonus_reasons.append("Excellent with children") try: lifespan = info.get('Lifespan', '10-12 years') years = int(lifespan.split('-')[0]) if years > 12: bonus_reasons.append("Above-average lifespan") except: pass html_content += f"""

    🏆 #{rank} {breed.replace('_', ' ')} Overall Match: {final_score*100:.1f}%

    Space Compatibility:
    {scores['space']*100:.1f}%
    Exercise Match:
    {scores['exercise']*100:.1f}%
    Grooming Match:
    {scores['grooming']*100:.1f}%
    Experience Match:
    {scores['experience']*100:.1f}%
    Noise Compatibility: Noise Compatibility Score:
    • Based on your noise tolerance preference
    • Considers breed's typical noise level
    • Accounts for living environment
    {scores['noise']*100:.1f}%
    {f'''
    Breed Bonus: Breed Bonus Points:
    • {('
    • '.join(bonus_reasons)) if bonus_reasons else 'No additional bonus points'}

    Bonus Factors Include:
    • Friendly temperament
    • Child compatibility
    • Longer lifespan
    • Living space adaptability
    {bonus_score*100:.1f}%
    ''' if bonus_score > 0 else ''}

    📋 Breed Details

    📏 Size: Size Categories:
    • Small: Under 20 pounds
    • Medium: 20-60 pounds
    • Large: Over 60 pounds
    {info['Size']}
    🏃 Exercise Needs: Exercise Needs:
    • Low: Short walks
    • Moderate: 1-2 hours daily
    • High: 2+ hours daily
    • Very High: Constant activity
    {info['Exercise Needs']}
    👨‍👩‍👧‍👦 Good with Children: Child Compatibility:
    • Yes: Excellent with kids
    • Moderate: Good with older children
    • No: Better for adult households
    {info['Good with Children']}
    Lifespan: Average Lifespan:
    • Short: 6-8 years
    • Average: 10-15 years
    • Long: 12-20 years
    • Varies by size: Larger breeds typically have shorter lifespans
    {info['Lifespan']}

    📝 Description

    {info.get('Description', '')}

    🔊 Noise Behavior

    Typical noise characteristics:

    Moderate to high barker
    Alert watch dog
    Attention-seeking barks
    Social vocalizations

    Noise level:

    Moderate-High

    Barking triggers:

    Separation anxiety
    Attention needs
    Strange noises
    Excitement

    Source: Compiled from various breed behavior resources, 2024

    Individual dogs may vary in their vocalization patterns.

    Training can significantly influence barking behavior.

    Environmental factors may affect noise levels.

    🏥 Health Insights Health information is compiled from multiple sources including veterinary resources, breed guides, and international canine health databases. Each dog is unique and may vary from these general guidelines.

    Common breed-specific health considerations:

    Patellar luxation
    Progressive retinal atrophy
    Von Willebrand's disease
    Open fontanel

    Recommended health screenings:

    Patella evaluation
    Eye examination
    Blood clotting tests
    Skull development monitoring

    Source: Compiled from various veterinary and breed information resources, 2024

    This information is for reference only and based on breed tendencies.

    Each dog is unique and may not develop any or all of these conditions.

    Always consult with qualified veterinarians for professional advice.

    """ html_content += "
    " return html_content def get_breed_recommendations(user_prefs: UserPreferences, top_n: int = 10) -> List[Dict]: """基於使用者偏好推薦狗品種,確保正確的分數排序""" print("Starting get_breed_recommendations") recommendations = [] seen_breeds = set() try: # 獲取所有品種 conn = sqlite3.connect('animal_detector.db') cursor = conn.cursor() cursor.execute("SELECT Breed FROM AnimalCatalog") all_breeds = cursor.fetchall() conn.close() # 收集所有品種的分數 for breed_tuple in all_breeds: breed = breed_tuple[0] base_breed = breed.split('(')[0].strip() if base_breed in seen_breeds: continue seen_breeds.add(base_breed) # 獲取品種資訊 breed_info = get_dog_description(breed) if not isinstance(breed_info, dict): continue # 獲取噪音資訊 noise_info = breed_noise_info.get(breed, { "noise_notes": "Noise information not available", "noise_level": "Unknown", "source": "N/A" }) # 將噪音資訊整合到品種資訊中 breed_info['noise_info'] = noise_info # 計算基礎相容性分數 compatibility_scores = calculate_compatibility_score(breed_info, user_prefs) # 計算品種特定加分 breed_bonus = 0.0 # 壽命加分 try: lifespan = breed_info.get('Lifespan', '10-12 years') years = [int(x) for x in lifespan.split('-')[0].split()[0:1]] longevity_bonus = min(0.02, (max(years) - 10) * 0.005) breed_bonus += longevity_bonus except: pass # 性格特徵加分 temperament = breed_info.get('Temperament', '').lower() positive_traits = ['friendly', 'gentle', 'affectionate', 'intelligent'] negative_traits = ['aggressive', 'stubborn', 'dominant'] breed_bonus += sum(0.01 for trait in positive_traits if trait in temperament) breed_bonus -= sum(0.01 for trait in negative_traits if trait in temperament) # 與孩童相容性加分 if user_prefs.has_children: if breed_info.get('Good with Children') == 'Yes': breed_bonus += 0.02 elif breed_info.get('Good with Children') == 'No': breed_bonus -= 0.03 # 噪音相關加分 if user_prefs.noise_tolerance == 'low': if noise_info['noise_level'].lower() == 'high': breed_bonus -= 0.03 elif noise_info['noise_level'].lower() == 'low': breed_bonus += 0.02 elif user_prefs.noise_tolerance == 'high': if noise_info['noise_level'].lower() == 'high': breed_bonus += 0.01 # 計算最終分數 breed_bonus = round(breed_bonus, 4) final_score = round(compatibility_scores['overall'] + breed_bonus, 4) recommendations.append({ 'breed': breed, 'base_score': round(compatibility_scores['overall'], 4), 'bonus_score': round(breed_bonus, 4), 'final_score': final_score, 'scores': compatibility_scores, 'info': breed_info, 'noise_info': noise_info # 添加噪音資訊到推薦結果 }) # 嚴格按照 final_score 排序 recommendations.sort(key=lambda x: (round(-x['final_score'], 4), x['breed'] )) # 負號使其降序排列,並確保4位小數 # 選擇前N名並確保正確排序 final_recommendations = [] last_score = None rank = 1 for rec in recommendations: if len(final_recommendations) >= top_n: break current_score = rec['final_score'] # 確保分數遞減 if last_score is not None and current_score > last_score: continue # 添加排名資訊 rec['rank'] = rank final_recommendations.append(rec) last_score = current_score rank += 1 # 驗證最終排序 for i in range(len(final_recommendations)-1): current = final_recommendations[i] next_rec = final_recommendations[i+1] if current['final_score'] < next_rec['final_score']: print(f"Warning: Sorting error detected!") print(f"#{i+1} {current['breed']}: {current['final_score']}") print(f"#{i+2} {next_rec['breed']}: {next_rec['final_score']}") # 交換位置 final_recommendations[i], final_recommendations[i+1] = \ final_recommendations[i+1], final_recommendations[i] # 打印最終結果以供驗證 print("\nFinal Rankings:") for rec in final_recommendations: print(f"#{rec['rank']} {rec['breed']}") print(f"Base Score: {rec['base_score']:.4f}") print(f"Bonus: {rec['bonus_score']:.4f}") print(f"Final Score: {rec['final_score']:.4f}\n") return final_recommendations except Exception as e: print(f"Error in get_breed_recommendations: {str(e)}") print(f"Traceback: {traceback.format_exc()}") return []