PawMatchAI / dimension_score_calculator.py
DawnC's picture
Upload 18 files
1e4c9bc verified
import traceback
from typing import Dict, Any
from breed_health_info import breed_health_info
from breed_noise_info import breed_noise_info
class DimensionScoreCalculator:
"""
維度評分計算器類別
負責計算各個維度的具體評分,包含空間、運動、美容、經驗、健康和噪音等維度
"""
def __init__(self):
"""初始化維度評分計算器"""
pass
def calculate_space_score(self, size: str, living_space: str, has_yard: bool, exercise_needs: str) -> float:
"""
計算空間適配性評分
完整實現原始版本的空間計算邏輯,包含:
1. 動態的基礎分數矩陣
2. 強化空間品質評估
3. 增加極端情況處理
4. 考慮不同空間組合的協同效應
"""
def get_base_score():
# 基礎分數矩陣 - 更極端的分數分配
base_matrix = {
"Small": {
"apartment": {
"no_yard": 0.85, # 小型犬在公寓仍然適合
"shared_yard": 0.90, # 共享院子提供額外活動空間
"private_yard": 0.95 # 私人院子最理想
},
"house_small": {
"no_yard": 0.80,
"shared_yard": 0.85,
"private_yard": 0.90
},
"house_large": {
"no_yard": 0.75,
"shared_yard": 0.80,
"private_yard": 0.85
}
},
"Medium": {
"apartment": {
"no_yard": 0.75,
"shared_yard": 0.85,
"private_yard": 0.90
},
"house_small": {
"no_yard": 0.80,
"shared_yard": 0.90,
"private_yard": 0.90
},
"house_large": {
"no_yard": 0.85,
"shared_yard": 0.90,
"private_yard": 0.95
}
},
"Large": {
"apartment": {
"no_yard": 0.70,
"shared_yard": 0.80,
"private_yard": 0.85
},
"house_small": {
"no_yard": 0.75,
"shared_yard": 0.85,
"private_yard": 0.90
},
"house_large": {
"no_yard": 0.85,
"shared_yard": 0.90,
"private_yard": 1.0
}
},
"Giant": {
"apartment": {
"no_yard": 0.65,
"shared_yard": 0.75,
"private_yard": 0.80
},
"house_small": {
"no_yard": 0.70,
"shared_yard": 0.80,
"private_yard": 0.85
},
"house_large": {
"no_yard": 0.80,
"shared_yard": 0.90,
"private_yard": 1.0
}
}
}
yard_type = "private_yard" if has_yard else "no_yard"
return base_matrix.get(size, base_matrix["Medium"])[living_space][yard_type]
def calculate_exercise_adjustment():
# 運動需求對空間評分的影響
exercise_impact = {
"Very High": {
"apartment": -0.10,
"house_small": -0.05,
"house_large": 0
},
"High": {
"apartment": -0.08,
"house_small": -0.05,
"house_large": 0
},
"Moderate": {
"apartment": -0.05,
"house_small": -0.02,
"house_large": 0
},
"Low": {
"apartment": 0.10,
"house_small": 0.05,
"house_large": 0
}
}
return exercise_impact.get(exercise_needs, exercise_impact["Moderate"])[living_space]
def calculate_yard_bonus():
# 院子效益評估更加細緻
if not has_yard:
return 0
yard_benefits = {
"Giant": {
"Very High": 0.25,
"High": 0.20,
"Moderate": 0.15,
"Low": 0.10
},
"Large": {
"Very High": 0.20,
"High": 0.15,
"Moderate": 0.10,
"Low": 0.05
},
"Medium": {
"Very High": 0.15,
"High": 0.10,
"Moderate": 0.08,
"Low": 0.05
},
"Small": {
"Very High": 0.10,
"High": 0.08,
"Moderate": 0.05,
"Low": 0.03
}
}
size_benefits = yard_benefits.get(size, yard_benefits["Medium"])
return size_benefits.get(exercise_needs, size_benefits["Moderate"])
def apply_extreme_case_adjustments(score):
# 處理極端情況
if size == "Giant" and living_space == "apartment":
return score * 0.85
if size == "Large" and living_space == "apartment" and exercise_needs == "Very High":
return score * 0.85
if size == "Small" and living_space == "house_large" and exercise_needs == "Low":
return score * 0.9 # 低運動需求的小型犬在大房子可能過於寬敞
return score
# 計算最終分數
base_score = get_base_score()
exercise_adj = calculate_exercise_adjustment()
yard_bonus = calculate_yard_bonus()
# 整合所有評分因素
initial_score = base_score + exercise_adj + yard_bonus
# 應用極端情況調整
final_score = apply_extreme_case_adjustments(initial_score)
# 確保分數在有效範圍內,但允許更極端的結果
return max(0.05, min(1.0, final_score))
def calculate_exercise_score(self, breed_needs: str, exercise_time: int, exercise_type: str, breed_size: str, living_space: str, breed_info: dict = None) -> float:
"""
計算品種運動需求與使用者運動條件的匹配度。此函數特別著重:
1. 不同品種的運動耐受度差異
2. 運動時間與類型的匹配度
3. 極端運動量的嚴格限制
Parameters:
breed_needs: 品種的運動需求等級
exercise_time: 使用者計劃的運動時間(分鐘)
exercise_type: 運動類型(輕度/中度/高度)
Returns:
float: 0.1到1.0之間的匹配分數
"""
# 定義每個運動需求等級的具體參數
exercise_levels = {
'VERY HIGH': {
'min': 120, # 最低需求
'ideal': 150, # 理想運動量
'max': 180, # 最大建議量
'type_weights': { # 不同運動類型的權重
'active_training': 1.0,
'moderate_activity': 0.6,
'light_walks': 0.3
}
},
'HIGH': {
'min': 90,
'ideal': 120,
'max': 150,
'type_weights': {
'active_training': 0.9,
'moderate_activity': 0.8,
'light_walks': 0.4
}
},
'MODERATE': {
'min': 45,
'ideal': 60,
'max': 90,
'type_weights': {
'active_training': 0.7,
'moderate_activity': 1.0,
'light_walks': 0.8
}
},
'LOW': {
'min': 15,
'ideal': 30,
'max': 45,
'type_weights': {
'active_training': 0.5,
'moderate_activity': 0.8,
'light_walks': 1.0
}
}
}
# 獲取品種的運動參數
breed_level = exercise_levels.get(breed_needs.upper(), exercise_levels['MODERATE'])
# 計算時間匹配度
def calculate_time_score():
"""計算運動時間的匹配度,特別處理過度運動的情況"""
if exercise_time < breed_level['min']:
# 運動不足的嚴格懲罰
deficit_ratio = exercise_time / breed_level['min']
return max(0.1, deficit_ratio * 0.4)
elif exercise_time <= breed_level['ideal']:
# 理想範圍內的漸進提升
progress = (exercise_time - breed_level['min']) / (breed_level['ideal'] - breed_level['min'])
return 0.6 + (progress * 0.4)
elif exercise_time <= breed_level['max']:
# 理想到最大範圍的平緩下降
excess_ratio = (exercise_time - breed_level['ideal']) / (breed_level['max'] - breed_level['ideal'])
return 1.0 - (excess_ratio * 0.2)
else:
# 過度運動的顯著懲罰
excess = (exercise_time - breed_level['max']) / breed_level['max']
# 低運動需求品種的過度運動懲罰更嚴重
penalty_factor = 1.5 if breed_needs.upper() == 'LOW' else 1.0
return max(0.1, 0.8 - (excess * 0.5 * penalty_factor))
# 計算運動類型匹配度
def calculate_type_score():
"""評估運動類型的適合度,考慮品種特性"""
base_type_score = breed_level['type_weights'].get(exercise_type, 0.5)
# 特殊情況處理
if breed_needs.upper() == 'LOW' and exercise_type == 'active_training':
# 低運動需求品種不適合高強度運動
base_type_score *= 0.5
elif breed_needs.upper() == 'VERY HIGH' and exercise_type == 'light_walks':
# 高運動需求品種需要更多強度
base_type_score *= 0.6
return base_type_score
# 計算最終分數
time_score = calculate_time_score()
type_score = calculate_type_score()
# 根據運動需求等級調整權重
if breed_needs.upper() == 'LOW':
# 低運動需求品種更重視運動類型的合適性
final_score = (time_score * 0.6) + (type_score * 0.4)
elif breed_needs.upper() == 'VERY HIGH':
# 高運動需求品種更重視運動時間的充足性
final_score = (time_score * 0.7) + (type_score * 0.3)
else:
final_score = (time_score * 0.65) + (type_score * 0.35)
if breed_size in ['Large', 'Giant'] and living_space == 'apartment':
if exercise_time >= 120:
final_score = min(1.0, final_score * 1.2)
# 極端情況的最終調整
if breed_needs.upper() == 'LOW' and exercise_time > breed_level['max'] * 2:
# 低運動需求品種的過度運動顯著降分
final_score *= 0.6
elif breed_needs.upper() == 'VERY HIGH' and exercise_time < breed_level['min'] * 0.5:
# 高運動需求品種運動嚴重不足降分
final_score *= 0.5
return max(0.1, min(1.0, final_score))
def calculate_grooming_score(self, 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.20, # 大型犬的美容工作量顯著增加
"medium": -0.10,
"high": -0.05
},
"Large": {
"low": -0.15,
"medium": -0.05,
"high": 0
},
"Medium": {
"low": -0.10,
"medium": -0.05,
"high": 0
},
"Small": {
"low": -0.05,
"medium": 0,
"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(self, care_level: str, user_experience: str, temperament: str) -> float:
"""
計算使用者經驗與品種需求的匹配分數,更平衡的經驗等級影響
改進重點:
1. 提高初學者的基礎分數
2. 縮小經驗等級間的差距
3. 保持適度的區分度
"""
# 基礎分數矩陣 - 更合理的分數分配
base_scores = {
"High": {
"beginner": 0.55, # 提高起始分,讓新手也有機會
"intermediate": 0.80, # 中級玩家有不錯的勝任能力
"advanced": 0.95 # 資深者幾乎完全勝任
},
"Moderate": {
"beginner": 0.65, # 適中難度對新手更友善
"intermediate": 0.85, # 中級玩家相當適合
"advanced": 0.90 # 資深者完全勝任
},
"Low": {
"beginner": 0.85, # 新手友善品種維持高分
"intermediate": 0.90, # 中級玩家幾乎完全勝任
"advanced": 0.90 # 資深者完全勝任
}
}
# 取得基礎分數
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.10,
'aloof': -0.08,
'energetic': -0.08,
'aggressive': -0.20 # 保持較高懲罰,因為安全考慮
}
easy_traits = {
'gentle': 0.08, # 提高獎勵以平衡
'friendly': 0.08,
'eager to please': 0.10,
'patient': 0.08,
'adaptable': 0.08,
'calm': 0.08
}
# 計算特徵調整
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.10 # 降低懲罰
elif 'working' in temperament_lower:
temperament_adjustments -= 0.12
elif 'guard' in temperament_lower:
temperament_adjustments -= 0.12
elif user_experience == "intermediate":
moderate_traits = {
'stubborn': -0.08,
'independent': -0.05,
'intelligent': 0.10,
'athletic': 0.08,
'versatile': 0.08,
'protective': -0.05
}
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.15, min(1.0, score + temperament_adjustments))
return final_score
def calculate_health_score(self, breed_name: str, health_sensitivity: str) -> float:
"""
計算品種健康分數,加強健康問題的影響力和與使用者敏感度的連結
1. 根據使用者的健康敏感度調整分數
2. 更嚴格的健康問題評估
3. 考慮多重健康問題的累積效應
4. 加入遺傳疾病的特別考量
"""
try:
if breed_name not in breed_health_info:
return 0.5
except ImportError:
return 0.5
health_notes = breed_health_info[breed_name]['health_notes'].lower()
# 嚴重健康問題 - 加重扣分
severe_conditions = {
'hip dysplasia': -0.20, # 髖關節發育不良,影響生活品質
'heart disease': -0.15, # 心臟疾病,需要長期治療
'progressive retinal atrophy': -0.15, # 進行性視網膜萎縮,導致失明
'bloat': -0.18, # 胃扭轉,致命風險
'epilepsy': -0.15, # 癲癇,需要長期藥物控制
'degenerative myelopathy': -0.15, # 脊髓退化,影響行動能力
'von willebrand disease': -0.12 # 血液凝固障礙
}
# 中度健康問題 - 適度扣分
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(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(self, breed_name: str, noise_tolerance: str, living_space: str, has_children: bool, children_age: str) -> float:
"""
計算品種噪音分數,特別加強噪音程度與生活環境的關聯性評估,很多人棄養就是因為叫聲
"""
try:
if breed_name not in breed_noise_info:
return 0.5
except ImportError:
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})[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
}
}
# 計算環境相關的吠叫懲罰
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 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(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 living_space == 'apartment':
special_adjustments -= 0.15
elif 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))