PawMatchAI / scoring_calculation_system.py
DawnC's picture
Update scoring_calculation_system.py
11269b9 verified
raw
history blame
57.7 kB
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_breed_bonus(breed_info: dict, user_prefs: UserPreferences) -> float:
"""
計算品種的額外加分,評估品種的特殊特徵對使用者需求的適配性。
這個函數考慮四個主要面向:
1. 壽命評估:考慮飼養的長期承諾
2. 性格特徵評估:評估品種性格與使用者需求的匹配度
3. 環境適應性:評估品種在特定生活環境中的表現
4. 家庭相容性:特別關注品種與家庭成員的互動
"""
bonus = 0.0
temperament = breed_info.get('Temperament', '').lower()
# 壽命評估 - 重新設計以反映更實際的考量
try:
lifespan = breed_info.get('Lifespan', '10-12 years')
years = [int(x) for x in lifespan.split('-')[0].split()[0:1]]
avg_years = float(years[0])
# 根據壽命長短給予不同程度的獎勵或懲罰
if avg_years < 8:
bonus -= 0.08 # 短壽命可能帶來情感負擔
elif avg_years < 10:
bonus -= 0.04 # 稍短壽命輕微降低評分
elif avg_years > 13:
bonus += 0.06 # 長壽命適度加分
elif avg_years > 15:
bonus += 0.08 # 特別長壽的品種獲得更多加分
except:
pass
# 性格特徵評估 - 擴充並細化評分標準
positive_traits = {
'friendly': 0.08, # 提高友善性的重要性
'gentle': 0.08, # 溫和性格更受歡迎
'patient': 0.07, # 耐心是重要特質
'intelligent': 0.06, # 聰明但不過分重要
'adaptable': 0.06, # 適應性佳的特質
'affectionate': 0.06, # 親密性很重要
'easy-going': 0.05, # 容易相處的性格
'calm': 0.05 # 冷靜的特質
}
negative_traits = {
'aggressive': -0.15, # 嚴重懲罰攻擊性
'stubborn': -0.10, # 固執性格不易處理
'dominant': -0.10, # 支配性可能造成問題
'aloof': -0.08, # 冷漠性格影響互動
'nervous': -0.08, # 緊張性格需要更多關注
'protective': -0.06 # 過度保護可能有風險
}
# 性格評分計算 - 加入累積效應
personality_score = 0
positive_count = 0
negative_count = 0
for trait, value in positive_traits.items():
if trait in temperament:
personality_score += value
positive_count += 1
for trait, value in negative_traits.items():
if trait in temperament:
personality_score += value
negative_count += 1
# 多重特徵的累積效應
if positive_count > 2:
personality_score *= (1 + (positive_count - 2) * 0.1)
if negative_count > 1:
personality_score *= (1 - (negative_count - 1) * 0.15)
bonus += max(-0.25, min(0.25, personality_score))
# 適應性評估 - 根據具體環境給予更細緻的評分
adaptability_bonus = 0.0
if breed_info.get('Size') == "Small" and user_prefs.living_space == "apartment":
adaptability_bonus += 0.08 # 小型犬更適合公寓
# 環境適應性評估
if 'adaptable' in temperament or 'versatile' in temperament:
if user_prefs.living_space == "apartment":
adaptability_bonus += 0.10 # 適應性在公寓環境更重要
else:
adaptability_bonus += 0.05 # 其他環境仍有加分
# 氣候適應性
description = breed_info.get('Description', '').lower()
climate = user_prefs.climate
if climate == 'hot':
if 'heat tolerant' in description or 'warm climate' in description:
adaptability_bonus += 0.08
elif 'thick coat' in description or 'cold climate' in description:
adaptability_bonus -= 0.10
elif climate == 'cold':
if 'thick coat' in description or 'cold climate' in description:
adaptability_bonus += 0.08
elif 'heat tolerant' in description or 'short coat' in description:
adaptability_bonus -= 0.10
bonus += min(0.15, adaptability_bonus)
# 家庭相容性評估 - 特別關注有孩童的家庭
if user_prefs.has_children:
family_traits = {
'good with children': 0.12, # 提高與孩童相處的重要性
'patient': 0.10,
'gentle': 0.10,
'tolerant': 0.08,
'playful': 0.06
}
unfriendly_traits = {
'aggressive': -0.15, # 加重攻擊性的懲罰
'nervous': -0.12, # 緊張特質可能有風險
'protective': -0.10, # 過度保護性需要注意
'territorial': -0.08 # 地域性可能造成問題
}
# 根據孩童年齡調整評分權重
age_adjustments = {
'toddler': {
'bonus_mult': 0.6, # 降低正面特質的獎勵
'penalty_mult': 1.5 # 加重負面特質的懲罰
},
'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_score = 0
for trait, value in family_traits.items():
if trait in temperament:
family_score += value * adj['bonus_mult']
for trait, value in unfriendly_traits.items():
if trait in temperament:
family_score += value * adj['penalty_mult']
bonus += min(0.20, max(-0.30, family_score))
# 確保總體加分在合理範圍內,但允許更大的變化
return min(0.5, max(-0.35, 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
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": 1.0, # 小型犬最適合公寓
# "house_small": 0.95, # 在大房子反而稍微降分
# "house_large": 0.85 # 可能浪費空間
# },
# "Medium": {
# "apartment": 0.45, # 中型犬在公寓明顯受限
# "house_small": 0.85,
# "house_large": 1.0
# },
# "Large": {
# "apartment": 0.15, # 大型犬在公寓極不適合
# "house_small": 0.60, # 在小房子仍然受限
# "house_large": 1.0
# },
# "Giant": {
# "apartment": 0.1, # 更嚴格的限制
# "house_small": 0.45,
# "house_large": 1.0
# }
# }
# # 取得基礎分數
# base_score = base_scores.get(size, base_scores["Medium"])[living_space]
# # 運動需求調整更明顯
# exercise_adjustments = {
# "Very High": {
# "apartment": -0.25, # 在公寓更嚴重的懲罰
# "house_small": -0.15,
# "house_large": -0.05
# },
# "High": {
# "apartment": -0.20,
# "house_small": -0.10,
# "house_large": 0
# },
# "Moderate": {
# "apartment": -0.10,
# "house_small": -0.05,
# "house_large": 0
# },
# "Low": {
# "apartment": 0.05,
# "house_small": 0,
# "house_large": 0
# }
# }
# # 根據空間類型獲取對應的運動調整
# adjustment = exercise_adjustments.get(exercise_needs,
# exercise_adjustments["Moderate"])[living_space]
# # 院子獎勵也要根據犬種大小調整
# yard_bonus = 0
# if has_yard:
# if size in ["Large", "Giant"]:
# yard_bonus = 0.20 if living_space != "apartment" else 0.10
# elif size == "Medium":
# yard_bonus = 0.15 if living_space != "apartment" else 0.08
# else:
# yard_bonus = 0.10 if living_space != "apartment" else 0.05
# final_score = base_score + adjustment + yard_bonus
# return min(1.0, max(0.1, final_score))
def calculate_space_score(size: str, living_space: str, has_yard: bool, exercise_needs: str) -> float:
"""
優化的空間分數計算函數
主要改進:
1. 更均衡的基礎分數分配
2. 更細緻的空間需求評估
3. 強化運動需求與空間的關聯性
"""
# 重新設計基礎分數矩陣,降低普遍分數以增加區別度
base_scores = {
"Small": {
"apartment": 0.85, # 降低滿分機會
"house_small": 0.80, # 小型犬不應在大空間得到太高分數
"house_large": 0.75 # 避免小型犬總是得到最高分
},
"Medium": {
"apartment": 0.45, # 維持對公寓環境的限制
"house_small": 0.75, # 適中的分數
"house_large": 0.85 # 給予合理的獎勵
},
"Large": {
"apartment": 0.15, # 加重對大型犬在公寓的限制
"house_small": 0.65, # 中等適合度
"house_large": 0.90 # 最適合的環境
},
"Giant": {
"apartment": 0.10, # 更嚴格的限制
"house_small": 0.45, # 顯著的空間限制
"house_large": 0.95 # 最理想的配對
}
}
# 取得基礎分數
base_score = base_scores.get(size, base_scores["Medium"])[living_space]
# 運動需求相關的調整更加動態
exercise_adjustments = {
"Very High": {
"apartment": -0.25, # 加重在受限空間的懲罰
"house_small": -0.15,
"house_large": -0.05
},
"High": {
"apartment": -0.20,
"house_small": -0.10,
"house_large": 0
},
"Moderate": {
"apartment": -0.10,
"house_small": -0.05,
"house_large": 0
},
"Low": {
"apartment": 0.05, # 低運動需求在小空間反而有優勢
"house_small": 0,
"house_large": -0.05 # 輕微降低評分,因為空間可能過大
}
}
# 根據空間類型獲取運動需求調整
adjustment = exercise_adjustments.get(exercise_needs,
exercise_adjustments["Moderate"])[living_space]
# 院子效益根據品種大小和運動需求動態調整
if has_yard:
yard_bonus = {
"Giant": 0.20,
"Large": 0.15,
"Medium": 0.10,
"Small": 0.05
}.get(size, 0.10)
# 運動需求會影響院子的重要性
if exercise_needs in ["Very High", "High"]:
yard_bonus *= 1.2
elif exercise_needs == "Low":
yard_bonus *= 0.8
current_score = base_score + adjustment + yard_bonus
else:
current_score = base_score + adjustment
# 確保分數在合理範圍內,但避免極端值
return min(0.95, max(0.15, current_score))
# 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_exercise_score(breed_needs: str, user_prefs: UserPreferences) -> float:
"""
優化的運動需求評分系統
改進:
1. 考慮運動類型的匹配度
2. 評估活動模式的適配性
3. 加入品種特性考量
"""
# 基礎運動需求評估
exercise_needs = {
'VERY HIGH': {'min': 120, 'ideal': 150, 'max': 180, 'intensity': 'high'},
'HIGH': {'min': 90, 'ideal': 120, 'max': 150, 'intensity': 'moderate_high'},
'MODERATE': {'min': 45, 'ideal': 60, 'max': 90, 'intensity': 'moderate'},
'LOW': {'min': 20, 'ideal': 30, 'max': 45, 'intensity': 'low'},
'VARIES': {'min': 30, 'ideal': 60, 'max': 90, 'intensity': 'moderate'}
}
breed_need = exercise_needs.get(breed_needs.strip().upper(), exercise_needs['MODERATE'])
# 基礎時間匹配度計算
if user_prefs.exercise_time >= breed_need['ideal']:
time_score = 1.0 if user_prefs.exercise_time <= breed_need['max'] else 0.9
elif user_prefs.exercise_time >= breed_need['min']:
time_score = 0.7 + (user_prefs.exercise_time - breed_need['min']) / (breed_need['ideal'] - breed_need['min']) * 0.3
else:
time_score = max(0.3, 0.7 * (user_prefs.exercise_time / breed_need['min']))
# 運動類型匹配度評估
exercise_type_scores = {
'light_walks': {
'low': 1.0,
'moderate': 0.8,
'moderate_high': 0.5,
'high': 0.3
},
'moderate_activity': {
'low': 0.7,
'moderate': 1.0,
'moderate_high': 0.8,
'high': 0.6
},
'active_training': {
'low': 0.5,
'moderate': 0.8,
'moderate_high': 1.0,
'high': 1.0
}
}
type_score = exercise_type_scores.get(user_prefs.exercise_type, exercise_type_scores['moderate_activity']).get(breed_need['intensity'], 0.7)
# 時間可用性調整
availability_multiplier = {
'limited': 0.85, # 時間有限,可能影響運動品質
'moderate': 1.0, # 標準參考點
'flexible': 1.1 # 更靈活的時間安排有利於滿足狗狗需求
}.get(user_prefs.time_availability, 1.0)
# 環境因素考量
environment_bonus = 0
if user_prefs.yard_access != 'no_yard':
if breed_needs.strip().upper() in ['VERY HIGH', 'HIGH']:
environment_bonus = 0.1
else:
environment_bonus = 0.05
# 計算最終分數
final_score = (time_score * 0.5 + type_score * 0.3) * availability_multiplier + environment_bonus
return min(1.0, max(0.3, final_score))
def calculate_grooming_score(breed_needs: str, user_commitment: str, breed_size: str) -> float:
"""
計算美容需求分數,強化美容維護需求與使用者承諾度的匹配評估。
這個函數特別注意品種大小對美容工作的影響,以及不同程度的美容需求對時間投入的要求。
"""
# 重新設計基礎分數矩陣,讓美容需求的差異更加明顯
base_scores = {
"High": {
"low": 0.20, # 高需求對低承諾極不合適,顯著降低初始分數
"medium": 0.65, # 中等承諾仍有挑戰
"high": 1.0 # 高承諾最適合
},
"Moderate": {
"low": 0.45, # 中等需求對低承諾有困難
"medium": 0.85, # 較好的匹配
"high": 0.95 # 高承諾會有餘力
},
"Low": {
"low": 0.90, # 低需求對低承諾很合適
"medium": 0.85, # 略微降低以反映可能過度投入
"high": 0.80 # 可能造成資源浪費
}
}
# 取得基礎分數
base_score = base_scores.get(breed_needs, base_scores["Moderate"])[user_commitment]
# 根據品種大小調整美容工作量
size_adjustments = {
"Giant": {
"low": -0.35, # 大型犬的美容工作量顯著增加
"medium": -0.20,
"high": -0.10
},
"Large": {
"low": -0.25,
"medium": -0.15,
"high": -0.05
},
"Medium": {
"low": -0.15,
"medium": -0.10,
"high": 0
},
"Small": {
"low": -0.10,
"medium": -0.05,
"high": 0
}
}
# 應用體型調整
size_adjustment = size_adjustments.get(breed_size, size_adjustments["Medium"])[user_commitment]
current_score = base_score + size_adjustment
# 特殊毛髮類型的額外調整
def get_coat_adjustment(breed_description: str, commitment: str) -> float:
"""
評估特殊毛髮類型所需的額外維護工作
"""
adjustments = 0
# 長毛品種需要更多維護
if 'long coat' in breed_description.lower():
coat_penalties = {
'low': -0.20,
'medium': -0.15,
'high': -0.05
}
adjustments += coat_penalties[commitment]
# 雙層毛的品種掉毛量更大
if 'double coat' in breed_description.lower():
double_coat_penalties = {
'low': -0.15,
'medium': -0.10,
'high': -0.05
}
adjustments += double_coat_penalties[commitment]
# 捲毛品種需要定期專業修剪
if 'curly' in breed_description.lower():
curly_penalties = {
'low': -0.15,
'medium': -0.10,
'high': -0.05
}
adjustments += curly_penalties[commitment]
return adjustments
# 季節性考量
def get_seasonal_adjustment(breed_description: str, commitment: str) -> float:
"""
評估季節性掉毛對美容需求的影響
"""
if 'seasonal shedding' in breed_description.lower():
seasonal_penalties = {
'low': -0.15,
'medium': -0.10,
'high': -0.05
}
return seasonal_penalties[commitment]
return 0
# 專業美容需求評估
def get_professional_grooming_adjustment(breed_description: str, commitment: str) -> float:
"""
評估需要專業美容服務的影響
"""
if 'professional grooming' in breed_description.lower():
grooming_penalties = {
'low': -0.20,
'medium': -0.15,
'high': -0.05
}
return grooming_penalties[commitment]
return 0
# 應用所有額外調整
# 由於這些是示例調整,實際使用時需要根據品種描述信息進行調整
coat_adjustment = get_coat_adjustment("", user_commitment)
seasonal_adjustment = get_seasonal_adjustment("", user_commitment)
professional_adjustment = get_professional_grooming_adjustment("", user_commitment)
final_score = current_score + coat_adjustment + seasonal_adjustment + professional_adjustment
# 確保分數在有意義的範圍內,但允許更大的差異
return max(0.1, min(1.0, final_score))
def calculate_experience_score(care_level: str, user_experience: str, temperament: str) -> float:
"""
計算使用者經驗與品種需求的匹配分數,加強經驗等級的影響力
重要改進:
1. 擴大基礎分數差異
2. 加重困難特徵的懲罰
3. 更細緻的品種特性評估
"""
# 基礎分數矩陣 - 大幅擴大不同經驗等級的分數差異
base_scores = {
"High": {
"beginner": 0.10, # 降低起始分,高難度品種對新手幾乎不推薦
"intermediate": 0.60, # 中級玩家仍需謹慎
"advanced": 1.0 # 資深者能完全勝任
},
"Moderate": {
"beginner": 0.35, # 適中難度對新手仍具挑戰
"intermediate": 0.80, # 中級玩家較適合
"advanced": 1.0 # 資深者完全勝任
},
"Low": {
"beginner": 0.90, # 新手友善品種
"intermediate": 0.95, # 中級玩家幾乎完全勝任
"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.30, # 固執性格嚴重影響新手
'independent': -0.25, # 獨立性高的品種不適合新手
'dominant': -0.25, # 支配性強的品種需要經驗處理
'strong-willed': -0.20, # 強勢性格需要技巧管理
'protective': -0.20, # 保護性強需要適當訓練
'aloof': -0.15, # 冷漠性格需要耐心培養
'energetic': -0.15, # 活潑好動需要經驗引導
'aggressive': -0.35 # 攻擊傾向極不適合新手
}
# 新手友善的特徵 - 適度的獎勵
easy_traits = {
'gentle': 0.05, # 溫和性格適合新手
'friendly': 0.05, # 友善性格容易相處
'eager to please': 0.08, # 願意服從較容易訓練
'patient': 0.05, # 耐心的特質有助於建立關係
'adaptable': 0.05, # 適應性強較容易照顧
'calm': 0.06 # 冷靜的性格較好掌握
}
# 計算特徵調整
for trait, penalty in difficult_traits.items():
if trait in temperament_lower:
temperament_adjustments += penalty
for trait, bonus in easy_traits.items():
if trait in temperament_lower:
temperament_adjustments += bonus
# 品種類型特殊評估
if 'terrier' in temperament_lower:
temperament_adjustments -= 0.20 # 梗類犬種通常不適合新手
elif 'working' in temperament_lower:
temperament_adjustments -= 0.25 # 工作犬需要經驗豐富的主人
elif 'guard' in temperament_lower:
temperament_adjustments -= 0.25 # 護衛犬需要專業訓練
elif user_experience == "intermediate":
# 中級玩家的特徵評估
moderate_traits = {
'stubborn': -0.15, # 仍然需要注意,但懲罰較輕
'independent': -0.10,
'intelligent': 0.08, # 聰明的特質可以好好發揮
'athletic': 0.06, # 運動能力可以適當訓練
'versatile': 0.07, # 多功能性可以開發
'protective': -0.08 # 保護性仍需注意
}
for trait, adjustment in moderate_traits.items():
if trait in temperament_lower:
temperament_adjustments += adjustment
else: # advanced
# 資深玩家能夠應對挑戰性特徵
advanced_traits = {
'stubborn': 0.05, # 困難特徵反而成為優勢
'independent': 0.05,
'intelligent': 0.10,
'protective': 0.05,
'strong-willed': 0.05
}
for trait, bonus in advanced_traits.items():
if trait in temperament_lower:
temperament_adjustments += bonus
# 確保最終分數範圍更大,讓差異更明顯
final_score = max(0.05, min(1.0, score + temperament_adjustments))
return final_score
def calculate_health_score(breed_name: str, user_prefs: UserPreferences) -> float:
"""
計算品種健康分數,加強健康問題的影響力和與使用者敏感度的連結
重要改進:
1. 根據使用者的健康敏感度調整分數
2. 更嚴格的健康問題評估
3. 考慮多重健康問題的累積效應
4. 加入遺傳疾病的特別考量
"""
if breed_name not in breed_health_info:
return 0.5
health_notes = breed_health_info[breed_name]['health_notes'].lower()
# 嚴重健康問題 - 加重扣分
severe_conditions = {
'hip dysplasia': -0.25, # 髖關節發育不良,影響生活品質
'heart disease': -0.25, # 心臟疾病,需要長期治療
'progressive retinal atrophy': -0.20, # 進行性視網膜萎縮,導致失明
'bloat': -0.22, # 胃扭轉,致命風險
'epilepsy': -0.20, # 癲癇,需要長期藥物控制
'degenerative myelopathy': -0.20, # 脊髓退化,影響行動能力
'von willebrand disease': -0.18 # 血液凝固障礙
}
# 中度健康問題 - 適度扣分
moderate_conditions = {
'allergies': -0.12, # 過敏問題,需要持續關注
'eye problems': -0.15, # 眼睛問題,可能需要手術
'joint problems': -0.15, # 關節問題,影響運動能力
'hypothyroidism': -0.12, # 甲狀腺功能低下,需要藥物治療
'ear infections': -0.10, # 耳道感染,需要定期清理
'skin issues': -0.12 # 皮膚問題,需要特殊護理
}
# 輕微健康問題 - 輕微扣分
minor_conditions = {
'dental issues': -0.08, # 牙齒問題,需要定期護理
'weight gain tendency': -0.08, # 易胖體質,需要控制飲食
'minor allergies': -0.06, # 輕微過敏,可控制
'seasonal allergies': -0.06 # 季節性過敏
}
# 計算基礎健康分數
health_score = 1.0
# 健康問題累積效應計算
condition_counts = {
'severe': 0,
'moderate': 0,
'minor': 0
}
# 計算各等級健康問題的數量和影響
for condition, penalty in severe_conditions.items():
if condition in health_notes:
health_score += penalty
condition_counts['severe'] += 1
for condition, penalty in moderate_conditions.items():
if condition in health_notes:
health_score += penalty
condition_counts['moderate'] += 1
for condition, penalty in minor_conditions.items():
if condition in health_notes:
health_score += penalty
condition_counts['minor'] += 1
# 多重問題的額外懲罰(累積效應)
if condition_counts['severe'] > 1:
health_score *= (0.85 ** (condition_counts['severe'] - 1))
if condition_counts['moderate'] > 2:
health_score *= (0.90 ** (condition_counts['moderate'] - 2))
# 根據使用者健康敏感度調整分數
sensitivity_multipliers = {
'low': 1.1, # 較不在意健康問題
'medium': 1.0, # 標準評估
'high': 0.85 # 非常注重健康問題
}
health_score *= sensitivity_multipliers.get(user_prefs.health_sensitivity, 1.0)
# 壽命影響評估
try:
lifespan = breed_health_info[breed_name].get('average_lifespan', '10-12')
years = float(lifespan.split('-')[0])
if years < 8:
health_score *= 0.85 # 短壽命顯著降低分數
elif years < 10:
health_score *= 0.92 # 較短壽命輕微降低分數
elif years > 13:
health_score *= 1.1 # 長壽命適度加分
except:
pass
# 特殊健康優勢
if 'generally healthy' in health_notes or 'hardy breed' in health_notes:
health_score *= 1.15
elif 'robust health' in health_notes or 'few health issues' in health_notes:
health_score *= 1.1
# 確保分數在合理範圍內,但允許更大的分數差異
return max(0.1, min(1.0, health_score))
def calculate_noise_score(breed_name: str, user_prefs: UserPreferences) -> 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.95, # 安靜的狗對一般容忍很好
'high': 0.90 # 安靜的狗對高容忍當然可以
},
'medium': {
'low': 0.60, # 一般吠叫對低容忍較困難
'medium': 0.90, # 一般吠叫對一般容忍可接受
'high': 0.95 # 一般吠叫對高容忍很好
},
'high': {
'low': 0.25, # 愛叫的狗對低容忍極不適合
'medium': 0.65, # 愛叫的狗對一般容忍有挑戰
'high': 0.90 # 愛叫的狗對高容忍可以接受
},
'varies': {
'low': 0.50, # 不確定的情況對低容忍風險較大
'medium': 0.75, # 不確定的情況對一般容忍可嘗試
'high': 0.85 # 不確定的情況對高容忍問題較小
}
}
# 取得基礎分數
base_score = base_scores.get(noise_level, {'low': 0.6, 'medium': 0.75, 'high': 0.85})[user_prefs.noise_tolerance]
# 吠叫原因評估,根據環境調整懲罰程度
barking_penalties = {
'separation anxiety': {
'apartment': -0.30, # 在公寓對鄰居影響更大
'house_small': -0.25,
'house_large': -0.20
},
'excessive barking': {
'apartment': -0.25,
'house_small': -0.20,
'house_large': -0.15
},
'territorial': {
'apartment': -0.20, # 在公寓更容易被觸發
'house_small': -0.15,
'house_large': -0.10
},
'alert barking': {
'apartment': -0.15, # 公寓環境刺激較多
'house_small': -0.10,
'house_large': -0.08
},
'attention seeking': {
'apartment': -0.15,
'house_small': -0.12,
'house_large': -0.10
}
}
# 計算環境相關的吠叫懲罰
living_space = user_prefs.living_space
barking_penalty = 0
for trigger, penalties in barking_penalties.items():
if trigger in noise_notes:
barking_penalty += penalties.get(living_space, -0.15)
# 特殊情況評估
special_adjustments = 0
if user_prefs.has_children:
# 孩童年齡相關調整
child_age_adjustments = {
'toddler': {
'high': -0.20, # 幼童對吵鬧更敏感
'medium': -0.15,
'low': -0.05
},
'school_age': {
'high': -0.15,
'medium': -0.10,
'low': -0.05
},
'teenager': {
'high': -0.10,
'medium': -0.05,
'low': -0.02
}
}
# 根據孩童年齡和噪音等級調整
age_adj = child_age_adjustments.get(user_prefs.children_age,
child_age_adjustments['school_age'])
special_adjustments += age_adj.get(noise_level, -0.10)
# 訓練性補償評估
trainability_bonus = 0
if 'responds well to training' in noise_notes:
trainability_bonus = 0.12
elif 'can be trained' in noise_notes:
trainability_bonus = 0.08
elif 'difficult to train' in noise_notes:
trainability_bonus = 0.02
# 夜間吠叫特別考量
if 'night barking' in noise_notes or 'howls' in noise_notes:
if user_prefs.living_space == 'apartment':
special_adjustments -= 0.15
elif user_prefs.living_space == 'house_small':
special_adjustments -= 0.10
else:
special_adjustments -= 0.05
# 計算最終分數,確保更大的分數範圍
final_score = base_score + barking_penalty + special_adjustments + trainability_bonus
return max(0.1, min(1.0, final_score))
print("\n=== 開始計算品種相容性分數 ===")
print(f"處理品種: {breed_info.get('Breed', 'Unknown')}")
print(f"品種信息: {breed_info}")
print(f"使用者偏好: {vars(user_prefs)}")
# 1. 計算基礎分數
try:
space_score = calculate_space_score(
breed_info['Size'],
user_prefs.living_space,
user_prefs.space_for_play,
breed_info.get('Exercise Needs', 'Moderate')
)
print(f"空間分數計算結果: {space_score}")
except Exception as e:
print(f"空間分數計算錯誤: {str(e)}")
raise
try:
exercise_score = calculate_exercise_score(
breed_info.get('Exercise Needs', 'Moderate'),
user_prefs.exercise_time
)
print(f"運動分數計算結果: {exercise_score}")
except Exception as e:
print(f"運動分數計算錯誤: {str(e)}")
raise
try:
grooming_score = calculate_grooming_score(
breed_info.get('Grooming Needs', 'Moderate'),
user_prefs.grooming_commitment.lower(),
breed_info['Size']
)
print(f"美容分數計算結果: {grooming_score}")
except Exception as e:
print(f"美容分數計算錯誤: {str(e)}")
raise
try:
experience_score = calculate_experience_score(
breed_info.get('Care Level', 'Moderate'),
user_prefs.experience_level,
breed_info.get('Temperament', '')
)
print(f"經驗分數計算結果: {experience_score}")
except Exception as e:
print(f"經驗分數計算錯誤: {str(e)}")
raise
try:
# 修正:加入 user_prefs 參數
health_score = calculate_health_score(breed_info.get('Breed', ''), user_prefs)
print(f"健康分數計算結果: {health_score}")
except Exception as e:
print(f"健康分數計算錯誤: {str(e)}")
raise
try:
noise_score = calculate_noise_score(
breed_info.get('Breed', ''),
user_prefs
)
print(f"噪音分數計算結果: {noise_score}")
except Exception as e:
print(f"噪音分數計算錯誤: {str(e)}")
raise
# 整合所有分數到字典中
scores = {
'space': space_score,
'exercise': exercise_score,
'grooming': grooming_score,
'experience': experience_score,
'health': health_score,
'noise': noise_score
}
print("\n=== 所有基礎分數 ===")
for category, score in scores.items():
print(f"{category}: {score}")
# 首先處理極端情況
def check_critical_issues(scores: dict, breed_info: dict) -> float:
"""
檢查關鍵問題並計算懲罰係數
"""
penalty = 1.0
# 檢查經驗分數 - 如果太低表示品種太難駕馭
if scores['experience'] < 0.3:
penalty *= 0.8
# 檢查空間分數 - 特別是對公寓的情況
if user_prefs.living_space == 'apartment' and scores['space'] < 0.4:
penalty *= 0.85
# 檢查健康分數 - 健康問題是重要考量
if scores['health'] < 0.4:
penalty *= 0.9
return penalty
# # 計算權重和加權分數
# def calculate_weighted_score(scores: dict) -> float:
# """
# 使用動態權重計算加權分數
# """
# base_weights = {
# 'space': 0.28,
# 'exercise': 0.18,
# 'grooming': 0.12,
# 'experience': 0.22,
# 'health': 0.12,
# 'noise': 0.08
# }
# # 根據居住環境調整權重
# if user_prefs.living_space == 'apartment':
# base_weights['space'] *= 1.2
# base_weights['noise'] *= 1.2
# # 根據經驗等級調整權重
# if user_prefs.experience_level == 'beginner':
# base_weights['experience'] *= 1.3
# # 重新正規化權重
# total_weight = sum(base_weights.values())
# weights = {k: v/total_weight for k, v in base_weights.items()}
# # 計算加權分數
# return sum(score * weights[category] for category, score in scores.items())
def calculate_weighted_score(scores: dict, user_prefs: UserPreferences, breed_info: dict) -> float:
"""
優化的加權分數計算函數
主要改進:
1. 動態權重調整
2. 品種特性加成
3. 更平衡的分數分配
"""
# 基礎權重設定
base_weights = {
'space': 0.25, # 稍微降低空間權重
'exercise': 0.20, # 提高運動權重
'grooming': 0.15, # 略微提高美容權重
'experience': 0.18, # 降低經驗權重,避免過度主導
'health': 0.12,
'noise': 0.10 # 提高噪音權重
}
# 根據使用者經驗調整權重
if user_prefs.experience_level == 'beginner':
# 新手更注重易照顧程度
base_weights['experience'] *= 1.2
base_weights['health'] *= 1.1
base_weights['grooming'] *= 0.9
elif user_prefs.experience_level == 'advanced':
# 專家更注重運動和訓練潛力
base_weights['exercise'] *= 1.2
base_weights['experience'] *= 0.8
# 根據居住環境調整權重
if user_prefs.living_space == 'apartment':
base_weights['noise'] *= 1.3
base_weights['space'] *= 1.2
elif user_prefs.living_space == 'house_large':
base_weights['exercise'] *= 1.2
base_weights['space'] *= 0.9
# 有孩童時的權重調整
if user_prefs.has_children:
base_weights['noise'] *= 1.2
base_weights['health'] *= 1.1
# 重新正規化權重
total_weight = sum(base_weights.values())
weights = {k: v/total_weight for k, v in base_weights.items()}
# 計算基礎加權分數
weighted_base = sum(score * weights[category] for category, score in scores.items())
# 計算品種特性加成
breed_bonus = calculate_breed_characteristic_bonus(breed_info, user_prefs)
# 混合基礎分數和特性加成
final_score = (weighted_base * 0.85) + (breed_bonus * 0.15)
return final_score
def calculate_breed_characteristic_bonus(breed_info: dict, user_prefs: UserPreferences) -> float:
"""
計算品種特性加成,增加品種多樣性
"""
bonus = 0.0
temperament = breed_info.get('Temperament', '').lower()
description = breed_info.get('Description', '').lower()
# 品種類型加成
breed_types = {
'working': {'keywords': ['working', 'guard', 'protection'], 'bonus': 0.05},
'companion': {'keywords': ['companion', 'friendly', 'affectionate'], 'bonus': 0.05},
'sporting': {'keywords': ['hunting', 'sporting', 'athletic'], 'bonus': 0.05},
'herding': {'keywords': ['herding', 'shepherd', 'cattle'], 'bonus': 0.05}
}
# 根據使用場景給予特定加成
for breed_type, info in breed_types.items():
if any(keyword in description or keyword in temperament for keyword in info['keywords']):
if user_prefs.has_children and breed_type == 'companion':
bonus += info['bonus'] * 1.5
elif user_prefs.exercise_type == 'active_training' and breed_type in ['working', 'sporting']:
bonus += info['bonus'] * 1.3
else:
bonus += info['bonus']
# 特殊加成(增加多樣性)
if 'rare' in description or 'unique' in description:
bonus += 0.03
if 'independent' in temperament and user_prefs.experience_level == 'advanced':
bonus += 0.04
return min(0.15, bonus) # 限制最大加成
# 計算最終分數
def calculate_final_score(base_score: float, penalty: float) -> float:
"""
計算並調整最終分數,確保合理的分數分布
"""
# 應用懲罰係數
adjusted_score = base_score * penalty
# 將分數映射到期望的範圍(0.55-0.95)
mapped_score = 0.55 + (adjusted_score * 0.4)
# 確保分數在合理範圍內
final = max(0.55, min(0.95, mapped_score))
return round(final, 4)
# 執行計算流程
penalty = check_critical_issues(scores, breed_info)
weighted_score = calculate_weighted_score(scores)
final_score = calculate_final_score(weighted_score, penalty)
# 準備返回結果
scores = {k: round(v, 4) for k, v in scores.items()}
scores['overall'] = final_score
return scores
except Exception as e:
print(f"\n!!!!! 發生嚴重錯誤 !!!!!")
print(f"錯誤類型: {type(e).__name__}")
print(f"錯誤訊息: {str(e)}")
print(f"完整錯誤追蹤:")
print(traceback.format_exc())
return {k: 0.6 for k in ['space', 'exercise', 'grooming', 'experience', 'health', 'noise', 'overall']}