import sqlite3 import traceback import random 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 get_breed_recommendations(user_prefs: UserPreferences, top_n: int = 15) -> List[Dict]: """基於使用者偏好推薦狗品種,確保正確的分數排序""" print(f"Starting get_breed_recommendations with top_n={top_n}") 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() print(f"Total breeds in database: {len(all_breeds)}") # 收集所有品種的分數 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 # 調整品種尺寸過濾邏輯,避免過度限制候選品種 if user_prefs.size_preference != "no_preference": breed_size = breed_info.get('Size', '').lower() user_size = user_prefs.size_preference.lower() # 放寬尺寸匹配條件,允許相鄰尺寸的品種通過篩選 size_compatibility = False if user_size == 'small': size_compatibility = breed_size in ['small', 'medium'] elif user_size == 'medium': size_compatibility = breed_size in ['small', 'medium', 'large'] elif user_size == 'large': size_compatibility = breed_size in ['medium', 'large'] else: size_compatibility = True if not size_compatibility: 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_hash = hash(breed) random.seed(breed_hash) # Add small natural variation to avoid identical scores natural_variation = random.uniform(-0.008, 0.008) breed_bonus = round(breed_bonus + natural_variation, 4) final_score = round(min(1.0, 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 }) print(f"Breeds after filtering: {len(recommendations)}") # 嚴格按照 final_score 排序 recommendations.sort(key=lambda x: (round(-x['final_score'], 4), x['breed'])) # 修正後的推薦選擇邏輯,移除有問題的分數比較 final_recommendations = [] # 直接選取前 top_n 個品種,確保返回完整數量 for i, rec in enumerate(recommendations[:top_n]): rec['rank'] = i + 1 final_recommendations.append(rec) print(f"Final recommendations count: {len(final_recommendations)}") # 驗證最終排序 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()}") def _format_dimension_scores(dimension_scores: Dict) -> str: """Format individual dimension scores as badges""" if not dimension_scores: return "" badges_html = '