from breed_health_info import breed_health_info from breed_noise_info import breed_noise_info @dataclass class UserPreferences: """使用者偏好設定的資料結構""" living_space: str # "apartment", "house_small", "house_large" exercise_time: int # minutes per day grooming_commitment: str # "low", "medium", "high" experience_level: str # "beginner", "intermediate", "advanced" has_children: bool 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 # 壽命加分 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 # 性格特徵加分 temperament = breed_info.get('Temperament', '').lower() if user_prefs.has_children: if 'gentle' in temperament or 'patient' in temperament: bonus += 0.03 # 適應性加分 if breed_info.get('Size') == "Small" and user_prefs.living_space == "apartment": bonus += 0.02 return bonus @staticmethod def calculate_additional_factors(breed_info: dict, user_prefs: 'UserPreferences') -> dict: """計算額外的排序因素""" factors = { 'versatility': 0.0, 'health_score': 0.0, 'adaptability': 0.0 } # 計算多功能性分數 temperament = breed_info.get('Temperament', '').lower() versatile_traits = ['intelligent', 'adaptable', 'versatile', 'trainable'] factors['versatility'] = sum(trait in temperament for trait in versatile_traits) / len(versatile_traits) # 計算健康分數(基於預期壽命) lifespan = breed_info.get('Lifespan', '10-12 years') try: years = [int(x) for x in lifespan.split('-')[0].split()[0:1]] factors['health_score'] = min(1.0, max(years) / 15) # 標準化到0-1範圍 except: factors['health_score'] = 0.5 # 預設值 # 計算適應性分數 size = breed_info.get('Size', 'Medium') factors['adaptability'] = { 'Small': 0.9, 'Medium': 0.7, 'Large': 0.5, 'Giant': 0.3 }.get(size, 0.5) return factors def calculate_compatibility_score(breed_info: dict, user_prefs: UserPreferences) -> dict: """計算品種與使用者條件的相容性分數""" scores = {} try: # 1. 空間相容性計算 def calculate_space_score(size, living_space, has_yard): base_scores = { "Small": {"apartment": 0.95, "house_small": 1.0, "house_large": 0.90}, "Medium": {"apartment": 0.65, "house_small": 0.90, "house_large": 1.0}, "Large": {"apartment": 0.35, "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] adjustments = 0 # 特殊情況調整 if living_space == "apartment": if size == "Small": adjustments += 0.05 elif size in ["Large", "Giant"]: adjustments -= 0.15 if has_yard and living_space in ["house_small", "house_large"]: adjustments += 0.05 return min(1.0, max(0, base_score + adjustments)) # 2. 運動相容性計算 def calculate_exercise_score(breed_exercise_needs, user_exercise_time): exercise_needs = { 'VERY HIGH': 120, 'HIGH': 90, 'MODERATE': 60, 'LOW': 30, 'VARIES': 60 } breed_need = exercise_needs.get(breed_exercise_needs.strip().upper(), 60) difference = abs(user_exercise_time - breed_need) / breed_need if difference == 0: return 1.0 elif difference <= 0.2: return 0.95 elif difference <= 0.4: return 0.85 elif difference <= 0.6: return 0.70 elif difference <= 0.8: return 0.50 else: return 0.30 # 3. 美容需求計算 def calculate_grooming_score(breed_grooming_needs, user_commitment, breed_size): 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.9} } base_score = base_scores.get(breed_grooming_needs, base_scores["Moderate"])[user_commitment] if breed_size == "Large" and user_commitment == "low": base_score *= 0.80 elif breed_size == "Giant" and user_commitment == "low": base_score *= 0.70 return base_score # 4. 經驗等級計算 def calculate_experience_score(care_level, user_experience, temperament): base_scores = { "High": {"beginner": 0.3, "intermediate": 0.7, "advanced": 1.0}, "Moderate": {"beginner": 0.6, "intermediate": 0.9, "advanced": 1.0}, "Low": {"beginner": 0.9, "intermediate": 1.0, "advanced": 1.0} } score = base_scores.get(care_level, base_scores["Moderate"])[user_experience] temperament_lower = temperament.lower() if user_experience == "beginner": if any(trait in temperament_lower for trait in ['stubborn', 'independent', 'intelligent']): score *= 0.80 if any(trait in temperament_lower for trait in ['easy', 'gentle', 'friendly']): score *= 1.15 return min(1.0, 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() # 嚴重健康問題 severe_conditions = [ 'cancer', 'cardiomyopathy', 'epilepsy', 'dysplasia', 'bloat', 'progressive', 'syndrome' ] # 中等健康問題 moderate_conditions = [ 'allergies', 'infections', 'thyroid', 'luxation', 'skin problems', 'ear' ] 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) health_score = 1.0 health_score -= (severe_count * 0.1) health_score -= (moderate_count * 0.05) # 特殊條件調整 if user_prefs.has_children: if 'requires frequent' in health_notes or 'regular monitoring' in health_notes: health_score *= 0.9 if user_prefs.experience_level == 'beginner': if 'requires frequent' in health_notes or 'requires experienced' in health_notes: health_score *= 0.8 return max(0.3, 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_matrix = { 'low': {'low': 1.0, 'medium': 0.8, 'high': 0.6}, 'medium': {'low': 0.7, 'medium': 1.0, 'high': 0.8}, 'high': {'low': 0.4, 'medium': 0.7, 'high': 1.0} } # 從噪音矩陣獲取基礎分數 base_score = noise_matrix.get(noise_level, {'low': 0.7, 'medium': 0.7, 'high': 0.7})[user_noise_tolerance] # 特殊情況調整 special_adjustments = 0 if user_prefs.has_children and noise_level == 'high': special_adjustments -= 0.1 if user_prefs.living_space == 'apartment': if noise_level == 'high': special_adjustments -= 0.15 elif noise_level == 'medium': special_adjustments -= 0.05 final_score = base_score + special_adjustments return max(0.3, min(1.0, final_score)) # 計算所有基礎分數 scores = { 'space': calculate_space_score(breed_info['Size'], user_prefs.living_space, user_prefs.space_for_play), '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.20, 'exercise': 0.20, 'grooming': 0.15, 'experience': 0.15, 'health': 0.15, 'noise': 0.15 } # 基礎分數計算 base_score = sum(score * weights[category] for category, score in scores.items() if category != 'overall') # 額外調整 adjustments = 0 # 1. 適應性加分 if breed_info.get('Adaptability', 'Medium') == 'High': adjustments += 0.02 # 2. 氣候相容性 if user_prefs.climate in breed_info.get('Suitable Climate', '').split(','): adjustments += 0.02 # 3. 其他寵物相容性 if user_prefs.other_pets and breed_info.get('Good with Other Pets') == 'Yes': adjustments += 0.02 final_score = min(1.0, max(0, base_score + adjustments)) scores['overall'] = round(final_score, 4) # 四捨五入所有分數 for key in scores: scores[key] = round(scores[key], 4) return scores except Exception as e: print(f"Error in calculate_compatibility_score: {str(e)}") return {k: 0.5 for k in ['space', 'exercise', 'grooming', 'experience', 'health', 'noise', 'overall']}