from dataclasses import dataclass from breed_health_info import breed_health_info from breed_noise_info import breed_noise_info import traceback @dataclass class UserPreferences: """使用者偏好設定的資料結構""" living_space: str # "apartment", "house_small", "house_large" yard_access: str # "no_yard", "shared_yard", "private_yard" exercise_time: int # minutes per day exercise_type: str # "light_walks", "moderate_activity", "active_training" grooming_commitment: str # "low", "medium", "high" experience_level: str # "beginner", "intermediate", "advanced" time_availability: str # "limited", "moderate", "flexible" has_children: bool children_age: str # "toddler", "school_age", "teenager" noise_tolerance: str # "low", "medium", "high" space_for_play: bool other_pets: bool climate: str # "cold", "moderate", "hot" health_sensitivity: str = "medium" barking_acceptance: str = None def __post_init__(self): """在初始化後運行,用於設置派生值""" if self.barking_acceptance is None: self.barking_acceptance = self.noise_tolerance @staticmethod def calculate_breed_bonus(breed_info: dict, user_prefs: 'UserPreferences') -> float: """計算品種額外加分""" bonus = 0.0 temperament = breed_info.get('Temperament', '').lower() # 1. 壽命加分(最高0.05) 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.05, (max(years) - 10) * 0.01) bonus += longevity_bonus except: pass # 2. 性格特徵加分(最高0.15) positive_traits = { 'friendly': 0.05, 'gentle': 0.05, 'patient': 0.05, 'intelligent': 0.04, 'adaptable': 0.04, 'affectionate': 0.04, 'easy-going': 0.03, 'calm': 0.03 } negative_traits = { 'aggressive': -0.08, 'stubborn': -0.06, 'dominant': -0.06, 'aloof': -0.04, 'nervous': -0.05, 'protective': -0.04 } personality_score = sum(value for trait, value in positive_traits.items() if trait in temperament) personality_score += sum(value for trait, value in negative_traits.items() if trait in temperament) bonus += max(-0.15, min(0.15, personality_score)) # 3. 適應性加分(最高0.1) adaptability_bonus = 0.0 if breed_info.get('Size') == "Small" and user_prefs.living_space == "apartment": adaptability_bonus += 0.05 if 'adaptable' in temperament or 'versatile' in temperament: adaptability_bonus += 0.05 bonus += min(0.1, adaptability_bonus) # 4. 家庭相容性(最高0.1) if user_prefs.has_children: family_traits = { 'good with children': 0.06, 'patient': 0.05, 'gentle': 0.05, 'tolerant': 0.04, 'playful': 0.03 } unfriendly_traits = { 'aggressive': -0.08, 'nervous': -0.07, 'protective': -0.06, 'territorial': -0.05 } # 年齡評估這樣能更細緻 age_adjustments = { 'toddler': {'bonus_mult': 0.7, 'penalty_mult': 1.3}, 'school_age': {'bonus_mult': 1.0, 'penalty_mult': 1.0}, 'teenager': {'bonus_mult': 1.2, 'penalty_mult': 0.8} } adj = age_adjustments.get(user_prefs.children_age, {'bonus_mult': 1.0, 'penalty_mult': 1.0}) family_bonus = sum(value for trait, value in family_traits.items() if trait in temperament) * adj['bonus_mult'] family_penalty = sum(value for trait, value in unfriendly_traits.items() if trait in temperament) * adj['penalty_mult'] bonus += min(0.15, max(-0.2, family_bonus + family_penalty)) # 5. 專門技能加分(最高0.1) skill_bonus = 0.0 special_abilities = { 'working': 0.03, 'herding': 0.03, 'hunting': 0.03, 'tracking': 0.03, 'agility': 0.02 } for ability, value in special_abilities.items(): if ability in temperament.lower(): skill_bonus += value bonus += min(0.1, skill_bonus) return min(0.5, max(-0.25, bonus)) @staticmethod def calculate_additional_factors(breed_info: dict, user_prefs: 'UserPreferences') -> dict: """計算額外的評估因素""" factors = { 'versatility': 0.0, # 多功能性 'trainability': 0.0, # 可訓練度 'energy_level': 0.0, # 能量水平 'grooming_needs': 0.0, # 美容需求 'social_needs': 0.0, # 社交需求 'weather_adaptability': 0.0 # 氣候適應性 } temperament = breed_info.get('Temperament', '').lower() size = breed_info.get('Size', 'Medium') # 1. 多功能性評估 versatile_traits = ['intelligent', 'adaptable', 'trainable', 'athletic'] working_roles = ['working', 'herding', 'hunting', 'sporting', 'companion'] trait_score = sum(0.2 for trait in versatile_traits if trait in temperament) role_score = sum(0.2 for role in working_roles if role in breed_info.get('Description', '').lower()) factors['versatility'] = min(1.0, trait_score + role_score) # 2. 可訓練度評估 trainable_traits = { 'intelligent': 0.3, 'eager to please': 0.3, 'trainable': 0.2, 'quick learner': 0.2 } factors['trainability'] = min(1.0, sum(value for trait, value in trainable_traits.items() if trait in temperament)) # 3. 能量水平評估 exercise_needs = breed_info.get('Exercise Needs', 'MODERATE').upper() energy_levels = { 'VERY HIGH': 1.0, 'HIGH': 0.8, 'MODERATE': 0.6, 'LOW': 0.4, 'VARIES': 0.6 } factors['energy_level'] = energy_levels.get(exercise_needs, 0.6) # 4. 美容需求評估 grooming_needs = breed_info.get('Grooming Needs', 'MODERATE').upper() grooming_levels = { 'HIGH': 1.0, 'MODERATE': 0.6, 'LOW': 0.3 } coat_penalty = 0.2 if any(term in breed_info.get('Description', '').lower() for term in ['long coat', 'double coat']) else 0 factors['grooming_needs'] = min(1.0, grooming_levels.get(grooming_needs, 0.6) + coat_penalty) # 5. 社交需求評估 social_traits = ['friendly', 'social', 'affectionate', 'people-oriented'] antisocial_traits = ['independent', 'aloof', 'reserved'] social_score = sum(0.25 for trait in social_traits if trait in temperament) antisocial_score = sum(-0.2 for trait in antisocial_traits if trait in temperament) factors['social_needs'] = min(1.0, max(0.0, social_score + antisocial_score)) # 6. 氣候適應性評估 climate_terms = { 'cold': ['thick coat', 'winter', 'cold climate'], 'hot': ['short coat', 'warm climate', 'heat tolerant'], 'moderate': ['adaptable', 'all climate'] } climate_matches = sum(1 for term in climate_terms[user_prefs.climate] if term in breed_info.get('Description', '').lower()) factors['weather_adaptability'] = min(1.0, climate_matches * 0.3 + 0.4) # 基礎分0.4 return factors @staticmethod def calculate_family_safety_score(breed_info: dict, children_age: str) -> float: temperament = breed_info.get('Temperament', '').lower() size = breed_info.get('Size', 'Medium') # 基礎安全分數必須根據孩童年齡有所不同 base_safety_scores = { 'toddler': { "Small": 0.85, # 幼童與小型犬相對安全 "Medium": 0.60, # 中型犬需要更多注意 "Large": 0.40, # 大型犬風險較高 "Giant": 0.30 # 巨型犬風險最高 }, 'school_age': { "Small": 0.90, # 學齡兒童與小型犬很合適 "Medium": 0.75, # 中型犬可以接受 "Large": 0.55, # 大型犬需要注意 "Giant": 0.45 # 巨型犬仍需謹慎 }, 'teenager': { "Small": 0.95, # 青少年幾乎能應付所有小型犬 "Medium": 0.85, # 中型犬很合適 "Large": 0.70, # 大型犬可以考慮 "Giant": 0.60 # 巨型犬仍需小心 } } # 根據孩童年齡選擇對應的基礎分數 safety_score = base_safety_scores[children_age][size] # 年齡特定的危險特徵評估 age_specific_dangerous_traits = { 'toddler': { 'aggressive': -0.40, # 幼童最危險 'territorial': -0.35, 'protective': -0.30, 'nervous': -0.30, 'dominant': -0.25, 'energetic': -0.20 # 過度活潑對幼童也是風險 }, 'school_age': { 'aggressive': -0.30, 'territorial': -0.25, 'protective': -0.20, 'nervous': -0.20, 'dominant': -0.15, 'energetic': -0.10 }, 'teenager': { 'aggressive': -0.20, 'territorial': -0.15, 'protective': -0.10, 'nervous': -0.15, 'dominant': -0.10, 'energetic': -0.05 } } # 套用年齡特定的特徵評估 for trait, penalty in age_specific_dangerous_traits[children_age].items(): if trait in temperament: safety_score += penalty # 正面特徵評估(根據年齡調整獎勵程度) positive_traits_by_age = { 'toddler': { 'gentle': 0.15, 'patient': 0.15, 'calm': 0.12, 'tolerant': 0.12 }, 'school_age': { 'gentle': 0.12, 'patient': 0.12, 'playful': 0.10, 'friendly': 0.10 }, 'teenager': { 'friendly': 0.10, 'playful': 0.10, 'adaptable': 0.08, 'trainable': 0.08 } } # 套用正面特徵評估 for trait, bonus in positive_traits_by_age[children_age].items(): if trait in temperament: safety_score += bonus # 特殊風險評估(對所有年齡都很重要) description = breed_info.get('Description', '').lower() if 'history of' in description: safety_score -= 0.25 if 'requires experienced' in description: safety_score -= 0.15 # 確保分數在合理範圍內 return max(0.2, min(0.95, safety_score)) # def calculate_family_safety_score(breed_info: dict, children_age: str) -> float: # """ # 計算品種與家庭/兒童的安全相容性分數,作為calculate_compatibility_score的一部分 # 參數: # breed_info (dict): 品種資訊 # children_age (str): 兒童年齡組別 ('toddler', 'school_age', 'teenager') # 返回: # float: 0.2-0.95之間的安全分數 # """ # temperament = breed_info.get('Temperament', '').lower() # size = breed_info.get('Size', 'Medium') # # 基礎安全分數(根據體型) # base_safety_scores = { # "Small": 0.80, # 從 0.85 降至 0.80 # "Medium": 0.65, # 從 0.75 降至 0.65 # "Large": 0.50, # 從 0.65 降至 0.50 # "Giant": 0.40 # 從 0.55 降至 0.40 # } # safety_score = base_safety_scores.get(size, 0.60) # # 加強年齡相關的調整力度 # age_factors = { # 'toddler': { # 'base_modifier': -0.25, # 從 -0.15 降至 -0.25 # 'size_penalty': { # "Small": -0.10, # 從 -0.05 降至 -0.10 # "Medium": -0.20, # 從 -0.10 降至 -0.20 # "Large": -0.30, # 從 -0.20 降至 -0.30 # "Giant": -0.35 # 從 -0.25 降至 -0.35 # } # }, # 'school_age': { # 'base_modifier': -0.15, # 從 -0.08 降至 -0.15 # 'size_penalty': { # "Small": -0.05, # "Medium": -0.10, # "Large": -0.20, # "Giant": -0.25 # } # }, # 'teenager': { # 'base_modifier': -0.08, # 從 -0.05 降至 -0.08 # 'size_penalty': { # "Small": -0.02, # "Medium": -0.05, # "Large": -0.10, # "Giant": -0.15 # } # } # } # # 加強對危險特徵的評估 # dangerous_traits = { # 'aggressive': -0.35, # 從 -0.25 加重到 -0.35 # 'territorial': -0.30, # 從 -0.20 加重到 -0.30 # 'protective': -0.25, # 從 -0.15 加重到 -0.25 # 'nervous': -0.25, # 從 -0.15 加重到 -0.25 # 'dominant': -0.20, # 從 -0.15 加重到 -0.20 # 'strong-willed': -0.18, # 從 -0.12 加重到 -0.18 # 'independent': -0.15, # 從 -0.10 加重到 -0.15 # 'energetic': -0.12 # 從 -0.08 加重到 -0.12 # } # # 特殊風險評估加重 # if 'history of' in breed_info.get('Description', '').lower(): # safety_score -= 0.25 # 從 -0.15 加重到 -0.25 # if 'requires experienced' in breed_info.get('Description', '').lower(): # safety_score -= 0.20 # 從 -0.10 加重到 -0.20 # # 計算特徵分數 # for trait, bonus in positive_traits.items(): # if trait in temperament: # safety_score += bonus * 0.8 # 降低正面特徵的影響力 # for trait, penalty in dangerous_traits.items(): # if trait in temperament: # # 對幼童加重懲罰 # if children_age == 'toddler': # safety_score += penalty * 1.3 # # 對青少年略微減輕懲罰 # elif children_age == 'teenager': # safety_score += penalty * 0.8 # else: # safety_score += penalty # # 特殊風險評估 # description = breed_info.get('Description', '').lower() # if 'history of' in description: # safety_score -= 0.15 # if 'requires experienced' in description: # safety_score -= 0.10 # # 將分數限制在合理範圍內 # return max(0.2, min(0.95, safety_score)) def calculate_compatibility_score(breed_info: dict, user_prefs: UserPreferences) -> dict: """計算品種與使用者條件的相容性分數的優化版本""" try: print(f"Processing breed: {breed_info.get('Breed', 'Unknown')}") print(f"Breed info keys: {breed_info.keys()}") if 'Size' not in breed_info: print("Missing Size information") raise KeyError("Size information missing") def calculate_space_score(size: str, living_space: str, has_yard: bool, exercise_needs: str) -> float: """空間分數計算""" # 基礎空間需求矩陣 base_scores = { "Small": {"apartment": 0.95, "house_small": 1.0, "house_large": 0.90}, "Medium": {"apartment": 0.60, "house_small": 0.90, "house_large": 1.0}, "Large": {"apartment": 0.30, "house_small": 0.75, "house_large": 1.0}, "Giant": {"apartment": 0.15, "house_small": 0.55, "house_large": 1.0} } # 取得基礎分數 base_score = base_scores.get(size, base_scores["Medium"])[living_space] # 運動需求調整 exercise_adjustments = { "Very High": -0.15 if living_space == "apartment" else 0, "High": -0.10 if living_space == "apartment" else 0, "Moderate": 0, "Low": 0.05 if living_space == "apartment" else 0 } adjustments = exercise_adjustments.get(exercise_needs.strip(), 0) # 院子獎勵 if has_yard and size in ["Large", "Giant"]: adjustments += 0.10 elif has_yard: adjustments += 0.05 return min(1.0, max(0.1, base_score + adjustments)) def calculate_exercise_score(breed_needs: str, user_time: int) -> float: """運動需求計算""" exercise_needs = { 'VERY HIGH': {'min': 120, 'ideal': 150, 'max': 180}, 'HIGH': {'min': 90, 'ideal': 120, 'max': 150}, 'MODERATE': {'min': 45, 'ideal': 60, 'max': 90}, 'LOW': {'min': 20, 'ideal': 30, 'max': 45}, 'VARIES': {'min': 30, 'ideal': 60, 'max': 90} } breed_need = exercise_needs.get(breed_needs.strip().upper(), exercise_needs['MODERATE']) # 計算匹配度 if user_time >= breed_need['ideal']: if user_time > breed_need['max']: return 0.9 # 稍微降分,因為可能過度運動 return 1.0 elif user_time >= breed_need['min']: return 0.8 + (user_time - breed_need['min']) / (breed_need['ideal'] - breed_need['min']) * 0.2 else: return max(0.3, 0.8 * (user_time / breed_need['min'])) def calculate_grooming_score(breed_needs: str, user_commitment: str, breed_size: str) -> float: """美容需求計算""" # 基礎分數矩陣 base_scores = { "High": {"low": 0.3, "medium": 0.7, "high": 1.0}, "Moderate": {"low": 0.5, "medium": 0.9, "high": 1.0}, "Low": {"low": 1.0, "medium": 0.95, "high": 0.8} } # 取得基礎分數 base_score = base_scores.get(breed_needs, base_scores["Moderate"])[user_commitment] # 體型影響調整 size_adjustments = { "Large": {"low": -0.2, "medium": -0.1, "high": 0}, "Giant": {"low": -0.3, "medium": -0.15, "high": 0}, } if breed_size in size_adjustments: adjustment = size_adjustments[breed_size].get(user_commitment, 0) base_score = max(0.2, base_score + adjustment) return base_score # def calculate_experience_score(care_level: str, user_experience: str, temperament: str) -> float: # """ # 計算使用者經驗與品種需求的匹配分數 # 參數說明: # care_level: 品種的照顧難度 ("High", "Moderate", "Low") # user_experience: 使用者經驗等級 ("beginner", "intermediate", "advanced") # temperament: 品種的性格特徵描述 # 返回: # float: 0.2-1.0 之間的匹配分數 # """ # # 基礎分數矩陣 - 更大的分數差異來反映經驗重要性 # base_scores = { # "High": { # "beginner": 0.12, # 降低起始分,反映高難度品種對新手的挑戰 # "intermediate": 0.65, # 中級玩家可以應付,但仍有改善空間 # "advanced": 1.0 # 資深者能完全勝任 # }, # "Moderate": { # "beginner": 0.35, # 適中難度對新手來說仍具挑戰 # "intermediate": 0.82, # 中級玩家有很好的勝任能力 # "advanced": 1.0 # 資深者完全勝任 # }, # "Low": { # "beginner": 0.72, # 低難度品種適合新手 # "intermediate": 0.92, # 中級玩家幾乎完全勝任 # "advanced": 1.0 # 資深者完全勝任 # } # } # # 取得基礎分數 # score = base_scores.get(care_level, base_scores["Moderate"])[user_experience] # # 性格特徵評估 - 根據經驗等級調整權重 # temperament_lower = temperament.lower() # temperament_adjustments = 0.0 # if user_experience == "beginner": # # 新手不適合的特徵 - 更嚴格的懲罰 # difficult_traits = { # 'stubborn': -0.15, # 加重固執的懲罰 # 'independent': -0.12, # 加重獨立性的懲罰 # 'dominant': -0.12, # 加重支配性的懲罰 # 'strong-willed': -0.10, # 加重強勢的懲罰 # 'protective': -0.08, # 加重保護性的懲罰 # 'aloof': -0.08, # 加重冷漠的懲罰 # 'energetic': -0.06 # 輕微懲罰高能量 # } # # 新手友善的特徵 - 提供更多獎勵 # easy_traits = { # 'gentle': 0.08, # 增加溫和的獎勵 # 'friendly': 0.08, # 增加友善的獎勵 # 'eager to please': 0.08, # 增加順從的獎勵 # 'patient': 0.06, # 獎勵耐心 # 'adaptable': 0.06, # 獎勵適應性 # 'calm': 0.05 # 獎勵冷靜 # } # # 計算特徵調整 # for trait, penalty in difficult_traits.items(): # if trait in temperament_lower: # temperament_adjustments += penalty * 1.2 # 加重新手的懲罰 # for trait, bonus in easy_traits.items(): # if trait in temperament_lower: # temperament_adjustments += bonus # # 品種特殊調整 # if any(term in temperament_lower for term in ['terrier', 'working', 'guard']): # temperament_adjustments -= 0.12 # 加重對特定類型品種的懲罰 # elif user_experience == "intermediate": # # 中級玩家的調整更加平衡 # moderate_traits = { # 'intelligent': 0.05, # 獎勵聰明 # 'athletic': 0.04, # 獎勵運動能力 # 'versatile': 0.04, # 獎勵多功能性 # 'stubborn': -0.06, # 輕微懲罰固執 # 'independent': -0.05, # 輕微懲罰獨立性 # 'protective': -0.04 # 輕微懲罰保護性 # } # for trait, adjustment in moderate_traits.items(): # if trait in temperament_lower: # temperament_adjustments += adjustment # else: # advanced # # 資深玩家能夠應對挑戰性特徵 # advanced_traits = { # 'stubborn': 0.04, # 反轉為優勢 # 'independent': 0.04, # 反轉為優勢 # 'intelligent': 0.05, # 獎勵聰明 # 'protective': 0.04, # 獎勵保護性 # 'strong-willed': 0.03 # 獎勵強勢 # } # for trait, bonus in advanced_traits.items(): # if trait in temperament_lower: # temperament_adjustments += bonus # # 確保最終分數在合理範圍內 # final_score = max(0.2, min(1.0, score + temperament_adjustments)) # return final_score def calculate_experience_score(care_level: str, user_experience: str, temperament: str) -> float: """ 計算使用者經驗與品種需求的匹配分數 參數說明: care_level: 品種的照顧難度 ("High", "Moderate", "Low") user_experience: 使用者經驗等級 ("beginner", "intermediate", "advanced") temperament: 品種的性格特徵描述 返回: float: 0.2-1.0 之間的匹配分數 """ # 基礎分數矩陣 - 更大的分數差異來反映經驗重要性 base_scores = { "High": { "beginner": 0.12, # 降低起始分,反映高難度品種對新手的挑戰 "intermediate": 0.65, # 中級玩家可以應付,但仍有改善空間 "advanced": 1.0 # 資深者能完全勝任 }, "Moderate": { "beginner": 0.35, # 適中難度對新手來說仍具挑戰 "intermediate": 0.82, # 中級玩家有很好的勝任能力 "advanced": 1.0 # 資深者完全勝任 }, "Low": { "beginner": 0.72, # 低難度品種適合新手 "intermediate": 0.92, # 中級玩家幾乎完全勝任 "advanced": 1.0 # 資深者完全勝任 } } # 取得基礎分數 score = base_scores.get(care_level, base_scores["Moderate"])[user_experience] # 性格特徵評估 - 根據經驗等級調整權重 temperament_lower = temperament.lower() temperament_adjustments = 0.0 if user_experience == "beginner": # 新手不適合的特徵 - 更嚴格的懲罰 difficult_traits = { 'stubborn': -0.15, # 加重固執的懲罰 'independent': -0.12, # 加重獨立性的懲罰 'dominant': -0.12, # 加重支配性的懲罰 'strong-willed': -0.10, # 加重強勢的懲罰 'protective': -0.08, # 加重保護性的懲罰 'aloof': -0.08, # 加重冷漠的懲罰 'energetic': -0.06 # 輕微懲罰高能量 } # 新手友善的特徵 - 提供更多獎勵 easy_traits = { 'gentle': 0.08, # 增加溫和的獎勵 'friendly': 0.08, # 增加友善的獎勵 'eager to please': 0.08, # 增加順從的獎勵 'patient': 0.06, # 獎勵耐心 'adaptable': 0.06, # 獎勵適應性 'calm': 0.05 # 獎勵冷靜 } # 計算特徵調整 for trait, penalty in difficult_traits.items(): if trait in temperament_lower: temperament_adjustments += penalty * 1.2 # 加重新手的懲罰 for trait, bonus in easy_traits.items(): if trait in temperament_lower: temperament_adjustments += bonus # 品種特殊調整 if any(term in temperament_lower for term in ['terrier', 'working', 'guard']): temperament_adjustments -= 0.12 # 加重對特定類型品種的懲罰 elif user_experience == "intermediate": # 中級玩家的調整更加平衡 moderate_traits = { 'intelligent': 0.05, # 獎勵聰明 'athletic': 0.04, # 獎勵運動能力 'versatile': 0.04, # 獎勵多功能性 'stubborn': -0.06, # 輕微懲罰固執 'independent': -0.05, # 輕微懲罰獨立性 'protective': -0.04 # 輕微懲罰保護性 } for trait, adjustment in moderate_traits.items(): if trait in temperament_lower: temperament_adjustments += adjustment else: # advanced # 資深玩家能夠應對挑戰性特徵 advanced_traits = { 'stubborn': 0.02, # 降低加分幅度 'independent': 0.02, 'intelligent': 0.05, 'protective': 0.02, 'strong-willed': 0.02, 'aggressive': -0.04, # 新增負面特徵 'nervous': -0.03, 'dominant': -0.02 } for trait, bonus in advanced_traits.items(): if trait in temperament_lower: # 加入條件評估 if bonus > 0: # 正面特徵 # 限制正面特徵的累積加分不超過0.15 if temperament_adjustments + bonus <= 0.15: temperament_adjustments += bonus else: # 負面特徵 # 負面特徵一定要計算 temperament_adjustments += bonus # 確保最終分數在合理範圍內 final_score = max(0.2, min(1.0, score + temperament_adjustments)) return final_score def calculate_health_score(breed_name: str) -> float: """計算品種健康分數""" if breed_name not in breed_health_info: return 0.5 health_notes = breed_health_info[breed_name]['health_notes'].lower() # 嚴重健康問題(降低0.15分) severe_conditions = [ 'hip dysplasia', 'heart disease', 'progressive retinal atrophy', 'bloat', 'epilepsy', 'degenerative myelopathy', 'von willebrand disease' ] # 中度健康問題(降低0.1分) moderate_conditions = [ 'allergies', 'eye problems', 'joint problems', 'hypothyroidism', 'ear infections', 'skin issues' ] # 輕微健康問題(降低0.05分) minor_conditions = [ 'dental issues', 'weight gain tendency', 'minor allergies', 'seasonal allergies' ] # 計算基礎健康分數 health_score = 1.0 # 根據問題嚴重程度扣分 severe_count = sum(1 for condition in severe_conditions if condition in health_notes) moderate_count = sum(1 for condition in moderate_conditions if condition in health_notes) minor_count = sum(1 for condition in minor_conditions if condition in health_notes) health_score -= (severe_count * 0.15) health_score -= (moderate_count * 0.1) health_score -= (minor_count * 0.05) # 壽命影響 try: lifespan = breed_health_info[breed_name].get('average_lifespan', '10-12') years = float(lifespan.split('-')[0]) if years < 8: health_score *= 0.9 elif years > 13: health_score *= 1.1 except: pass # 特殊健康優勢 if 'generally healthy' in health_notes or 'hardy breed' in health_notes: health_score *= 1.1 return max(0.2, min(1.0, health_score)) def calculate_noise_score(breed_name: str, user_noise_tolerance: str) -> float: """計算品種噪音分數""" if breed_name not in breed_noise_info: return 0.5 noise_info = breed_noise_info[breed_name] noise_level = noise_info['noise_level'].lower() noise_notes = noise_info['noise_notes'].lower() # 基礎噪音分數矩陣 base_scores = { 'low': {'low': 1.0, 'medium': 0.9, 'high': 0.8}, 'medium': {'low': 0.7, 'medium': 1.0, 'high': 0.9}, 'high': {'low': 0.4, 'medium': 0.7, 'high': 1.0}, 'varies': {'low': 0.6, 'medium': 0.8, 'high': 0.9} } # 獲取基礎分數 base_score = base_scores.get(noise_level, {'low': 0.7, 'medium': 0.8, 'high': 0.6})[user_noise_tolerance] # 吠叫原因評估 barking_reasons_penalty = 0 problematic_triggers = [ ('separation anxiety', -0.15), ('excessive barking', -0.12), ('territorial', -0.08), ('alert barking', -0.05), ('attention seeking', -0.05) ] for trigger, penalty in problematic_triggers: if trigger in noise_notes: barking_reasons_penalty += penalty # 可訓練性補償 trainability_bonus = 0 if 'responds well to training' in noise_notes: trainability_bonus = 0.1 elif 'can be trained' in noise_notes: trainability_bonus = 0.05 # 特殊情況 special_adjustments = 0 if 'rarely barks' in noise_notes: special_adjustments += 0.1 if 'howls' in noise_notes and user_noise_tolerance == 'low': special_adjustments -= 0.1 final_score = base_score + barking_reasons_penalty + trainability_bonus + special_adjustments return max(0.2, min(1.0, final_score)) # # 計算所有基礎分數 # scores = { # 'space': calculate_space_score( # breed_info['Size'], # user_prefs.living_space, # user_prefs.space_for_play, # breed_info.get('Exercise Needs', 'Moderate') # ), # 'exercise': calculate_exercise_score( # breed_info.get('Exercise Needs', 'Moderate'), # user_prefs.exercise_time # ), # 'grooming': calculate_grooming_score( # breed_info.get('Grooming Needs', 'Moderate'), # user_prefs.grooming_commitment.lower(), # breed_info['Size'] # ), # 'experience': calculate_experience_score( # breed_info.get('Care Level', 'Moderate'), # user_prefs.experience_level, # breed_info.get('Temperament', '') # ), # 'health': calculate_health_score(breed_info.get('Breed', '')), # 'noise': calculate_noise_score(breed_info.get('Breed', ''), user_prefs.noise_tolerance) # } # # 優化權重配置 # weights = { # 'space': 0.28, # 'exercise': 0.18, # 'grooming': 0.12, # 'experience': 0.22, # 'health': 0.12, # 'noise': 0.08 # } # # 計算加權總分 # weighted_score = sum(score * weights[category] for category, score in scores.items()) # def amplify_score(score): # """ # 優化分數放大函數,確保分數範圍合理且結果一致 # """ # # 基礎調整 # adjusted = (score - 0.35) * 1.8 # # 使用 3.2 次方使曲線更平滑 # amplified = pow(adjusted, 3.2) / 5.8 + score # # 特別處理高分區間,確保不超過95% # if amplified > 0.90: # # 壓縮高分區間,確保最高到95% # amplified = 0.90 + (amplified - 0.90) * 0.5 # # 確保最終分數在合理範圍內(0.55-0.95) # final_score = max(0.55, min(0.95, amplified)) # # 四捨五入到小數點後第三位 # return round(final_score, 3) # final_score = amplify_score(weighted_score) # # 四捨五入所有分數 # scores = {k: round(v, 4) for k, v in scores.items()} # scores['overall'] = round(final_score, 4) # return scores # 計算所有基礎分數 scores = { 'space': calculate_space_score( breed_info['Size'], user_prefs.living_space, user_prefs.space_for_play, breed_info.get('Exercise Needs', 'Moderate') ), 'exercise': calculate_exercise_score( breed_info.get('Exercise Needs', 'Moderate'), user_prefs.exercise_time ), 'grooming': calculate_grooming_score( breed_info.get('Grooming Needs', 'Moderate'), user_prefs.grooming_commitment.lower(), breed_info['Size'] ), 'experience': calculate_experience_score( breed_info.get('Care Level', 'Moderate'), user_prefs.experience_level, breed_info.get('Temperament', '') ), 'health': calculate_health_score(breed_info.get('Breed', '')), 'noise': calculate_noise_score(breed_info.get('Breed', ''), user_prefs.noise_tolerance) } weights = { 'space': 0.28, 'exercise': 0.18, 'grooming': 0.12, 'experience': 0.22, 'health': 0.12, 'noise': 0.08 } # 計算基礎加權分數 weighted_score = sum(score * weights[category] for category, score in scores.items()) # 如果有孩童,加入家庭安全考量 if user_prefs.has_children: try: family_safety = calculate_family_safety_score(breed_info, user_prefs.children_age) # family_safety 作為調整因子,而不是新的分數項目 # 這裡的 0.4 表示 family_safety 最多可以降低 60% 的分數 safety_modifier = (family_safety * 0.6) + 0.4 weighted_score *= safety_modifier except Exception as e: print(f"Family safety calculation error: {str(e)}") # 發生錯誤時使用較保守的預設值 weighted_score *= 0.7 # 加入品種加分的影響 try: breed_bonus = calculate_breed_bonus(breed_info, user_prefs) # breed_bonus 作為加成效果,但限制其影響範圍 bonus_modifier = 1 + (breed_bonus * 0.3) # 品種加分最多提升 30% weighted_score *= bonus_modifier except Exception as e: print(f"Breed bonus calculation error: {str(e)}") def amplify_score(score): """ 優化後的分數放大函數,確保分數範圍合理且結果一致。 主要目的是將分數轉換到更容易理解的範圍,並增加差異性。 """ # 基礎調整 - 降低基準點使差異更明顯 adjusted = (score - 0.25) * 1.8 # 使用較溫和的指數來放大差異,但不會過度誇大 amplified = pow(adjusted, 2.2) / 3.5 + score # 處理高分區間,避免分數過度集中 if amplified > 0.85: amplified = 0.85 + (amplified - 0.85) * 0.6 # 確保分數在合理範圍內(0.45-0.95) final_score = max(0.45, min(0.95, amplified)) return round(final_score, 3) # 計算最終分數 final_score = amplify_score(weighted_score) # 準備回傳結果 scores = {k: round(v, 4) for k, v in scores.items()} scores['overall'] = round(final_score, 4) return scores # except Exception as e: # print(f"Error details: {str(e)}") # print(f"breed_info: {breed_info}") # # print(f"Error in calculate_compatibility_score: {str(e)}") # return {k: 0.5 for k in ['space', 'exercise', 'grooming', 'experience', 'health', 'noise', 'overall']} except Exception as e: print(f"Critical error in compatibility score calculation:") print(f"Error type: {type(e).__name__}") print(f"Error message: {str(e)}") print(f"Breed info: {breed_info}") print(f"User preferences: {user_prefs.__dict__}") # 嘗試返回已計算的分數,若完全失敗則返回預設值 try: return scores except: return { 'space': 0.7, 'exercise': 0.7, 'grooming': 0.7, 'experience': 0.7, 'health': 0.7, 'noise': 0.7, 'overall': 0.7 }