DawnC commited on
Commit
b4e520b
1 Parent(s): 68e4cf6

Update scoring_calculation_system.py

Browse files
Files changed (1) hide show
  1. scoring_calculation_system.py +405 -192
scoring_calculation_system.py CHANGED
@@ -4,6 +4,7 @@ from breed_noise_info import breed_noise_info
4
 
5
  @dataclass
6
  class UserPreferences:
 
7
  """使用者偏好設定的資料結構"""
8
  living_space: str # "apartment", "house_small", "house_large"
9
  exercise_time: int # minutes per day
@@ -14,277 +15,489 @@ class UserPreferences:
14
  space_for_play: bool
15
  other_pets: bool
16
  climate: str # "cold", "moderate", "hot"
17
- health_sensitivity: str = "medium" # 設置默認值
18
  barking_acceptance: str = None
 
 
 
 
19
 
20
  def __post_init__(self):
21
  """在初始化後運行,用於設置派生值"""
22
  if self.barking_acceptance is None:
23
  self.barking_acceptance = self.noise_tolerance
24
 
25
- @staticmethod
26
- def calculate_breed_bonus(breed_info: dict, user_prefs: 'UserPreferences') -> float:
27
- """計算品種額外加分"""
28
- bonus = 0.0
29
-
30
- # 壽命加分
31
- try:
32
- lifespan = breed_info.get('Lifespan', '10-12 years')
33
- years = [int(x) for x in lifespan.split('-')[0].split()[0:1]]
34
- longevity_bonus = min(0.05, (max(years) - 10) * 0.01)
35
- bonus += longevity_bonus
36
- except:
37
- pass
38
-
39
- # 性格特徵加分
40
- temperament = breed_info.get('Temperament', '').lower()
41
- if user_prefs.has_children:
42
- if 'gentle' in temperament or 'patient' in temperament:
43
- bonus += 0.03
44
-
45
- # 適應性加分
46
- if breed_info.get('Size') == "Small" and user_prefs.living_space == "apartment":
47
- bonus += 0.02
48
-
49
- return bonus
50
-
51
- @staticmethod
52
- def calculate_additional_factors(breed_info: dict, user_prefs: 'UserPreferences') -> dict:
53
- """計算額外的排序因素"""
54
- factors = {
55
- 'versatility': 0.0,
56
- 'health_score': 0.0,
57
- 'adaptability': 0.0
58
- }
59
-
60
- # 計算多功能性分數
61
- temperament = breed_info.get('Temperament', '').lower()
62
- versatile_traits = ['intelligent', 'adaptable', 'versatile', 'trainable']
63
- factors['versatility'] = sum(trait in temperament for trait in versatile_traits) / len(versatile_traits)
64
 
65
- # 計算健康分數(基於預期壽命)
 
 
 
 
 
 
 
66
  lifespan = breed_info.get('Lifespan', '10-12 years')
67
- try:
68
- years = [int(x) for x in lifespan.split('-')[0].split()[0:1]]
69
- factors['health_score'] = min(1.0, max(years) / 15) # 標準化到0-1範圍
70
- except:
71
- factors['health_score'] = 0.5 # 預設值
72
-
73
- # 計算適應性分數
74
- size = breed_info.get('Size', 'Medium')
75
- factors['adaptability'] = {
76
- 'Small': 0.9,
77
- 'Medium': 0.7,
78
- 'Large': 0.5,
79
- 'Giant': 0.3
80
- }.get(size, 0.5)
81
-
82
- return factors
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
 
85
  def calculate_compatibility_score(breed_info: dict, user_prefs: UserPreferences) -> dict:
86
- """計算品種與使用者條件的相容性分數"""
87
- scores = {}
88
  try:
89
- # 1. 空間相容性計算
90
- def calculate_space_score(size, living_space, has_yard):
 
91
  base_scores = {
92
  "Small": {"apartment": 0.95, "house_small": 1.0, "house_large": 0.90},
93
- "Medium": {"apartment": 0.65, "house_small": 0.90, "house_large": 1.0},
94
- "Large": {"apartment": 0.35, "house_small": 0.75, "house_large": 1.0},
95
  "Giant": {"apartment": 0.15, "house_small": 0.55, "house_large": 1.0}
96
  }
97
-
 
98
  base_score = base_scores.get(size, base_scores["Medium"])[living_space]
99
- adjustments = 0
100
-
101
- # 特殊情況調整
102
- if living_space == "apartment":
103
- if size == "Small":
104
- adjustments += 0.05
105
- elif size in ["Large", "Giant"]:
106
- adjustments -= 0.15
107
-
108
- if has_yard and living_space in ["house_small", "house_large"]:
 
 
 
 
 
109
  adjustments += 0.05
 
 
110
 
111
- return min(1.0, max(0, base_score + adjustments))
112
-
113
- # 2. 運動相容性計算
114
- def calculate_exercise_score(breed_exercise_needs, user_exercise_time):
115
  exercise_needs = {
116
- 'VERY HIGH': 120,
117
- 'HIGH': 90,
118
- 'MODERATE': 60,
119
- 'LOW': 30,
120
- 'VARIES': 60
121
  }
122
-
123
- breed_need = exercise_needs.get(breed_exercise_needs.strip().upper(), 60)
124
- difference = abs(user_exercise_time - breed_need) / breed_need
125
-
126
- if difference == 0:
 
 
127
  return 1.0
128
- elif difference <= 0.2:
129
- return 0.95
130
- elif difference <= 0.4:
131
- return 0.85
132
- elif difference <= 0.6:
133
- return 0.70
134
- elif difference <= 0.8:
135
- return 0.50
136
  else:
137
- return 0.30
138
 
139
- # 3. 美容需求計算
140
- def calculate_grooming_score(breed_grooming_needs, user_commitment, breed_size):
 
141
  base_scores = {
142
  "High": {"low": 0.3, "medium": 0.7, "high": 1.0},
143
  "Moderate": {"low": 0.5, "medium": 0.9, "high": 1.0},
144
- "Low": {"low": 1.0, "medium": 0.95, "high": 0.9}
145
  }
146
-
147
- base_score = base_scores.get(breed_grooming_needs, base_scores["Moderate"])[user_commitment]
148
-
149
- if breed_size == "Large" and user_commitment == "low":
150
- base_score *= 0.80
151
- elif breed_size == "Giant" and user_commitment == "low":
152
- base_score *= 0.70
153
-
 
 
 
 
 
 
154
  return base_score
155
 
156
- # 4. 經驗等級計算
157
- def calculate_experience_score(care_level, user_experience, temperament):
 
158
  base_scores = {
159
- "High": {"beginner": 0.3, "intermediate": 0.7, "advanced": 1.0},
160
- "Moderate": {"beginner": 0.6, "intermediate": 0.9, "advanced": 1.0},
161
- "Low": {"beginner": 0.9, "intermediate": 1.0, "advanced": 1.0}
162
  }
163
-
164
  score = base_scores.get(care_level, base_scores["Moderate"])[user_experience]
165
-
 
166
  temperament_lower = temperament.lower()
 
167
  if user_experience == "beginner":
168
- if any(trait in temperament_lower for trait in ['stubborn', 'independent', 'intelligent']):
169
- score *= 0.80
170
- if any(trait in temperament_lower for trait in ['easy', 'gentle', 'friendly']):
171
- score *= 1.15
172
-
173
- return min(1.0, score)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
  def calculate_health_score(breed_name: str) -> float:
 
176
  if breed_name not in breed_health_info:
177
  return 0.5
178
 
179
  health_notes = breed_health_info[breed_name]['health_notes'].lower()
180
-
181
- # 嚴重健康問題
182
  severe_conditions = [
183
- 'cancer', 'cardiomyopathy', 'epilepsy', 'dysplasia',
184
- 'bloat', 'progressive', 'syndrome'
 
 
 
 
 
185
  ]
186
-
187
- # 中等健康問題
188
  moderate_conditions = [
189
- 'allergies', 'infections', 'thyroid', 'luxation',
190
- 'skin problems', 'ear'
 
 
 
 
 
 
 
 
 
 
 
 
191
  ]
192
 
 
 
 
 
193
  severe_count = sum(1 for condition in severe_conditions if condition in health_notes)
194
  moderate_count = sum(1 for condition in moderate_conditions if condition in health_notes)
195
-
196
- health_score = 1.0
197
- health_score -= (severe_count * 0.1)
198
- health_score -= (moderate_count * 0.05)
199
-
200
- # 特殊條件調��
201
- if user_prefs.has_children:
202
- if 'requires frequent' in health_notes or 'regular monitoring' in health_notes:
 
 
 
203
  health_score *= 0.9
 
 
 
 
204
 
205
- if user_prefs.experience_level == 'beginner':
206
- if 'requires frequent' in health_notes or 'requires experienced' in health_notes:
207
- health_score *= 0.8
208
 
209
- return max(0.3, min(1.0, health_score))
210
 
211
  def calculate_noise_score(breed_name: str, user_noise_tolerance: str) -> float:
 
212
  if breed_name not in breed_noise_info:
213
  return 0.5
214
 
215
  noise_info = breed_noise_info[breed_name]
216
  noise_level = noise_info['noise_level'].lower()
217
-
218
 
219
  # 基礎噪音分數矩陣
220
- noise_matrix = {
221
- 'low': {'low': 1.0, 'medium': 0.8, 'high': 0.6},
222
- 'medium': {'low': 0.7, 'medium': 1.0, 'high': 0.8},
223
- 'high': {'low': 0.4, 'medium': 0.7, 'high': 1.0}
 
224
  }
225
 
226
- # 從噪音矩陣獲取基礎分數
227
- base_score = noise_matrix.get(noise_level, {'low': 0.7, 'medium': 0.7, 'high': 0.7})[user_noise_tolerance]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
 
229
- # 特殊情況調整
230
  special_adjustments = 0
231
- if user_prefs.has_children and noise_level == 'high':
 
 
232
  special_adjustments -= 0.1
233
- if user_prefs.living_space == 'apartment':
234
- if noise_level == 'high':
235
- special_adjustments -= 0.15
236
- elif noise_level == 'medium':
237
- special_adjustments -= 0.05
238
 
239
- final_score = base_score + special_adjustments
240
- return max(0.3, min(1.0, final_score))
 
241
 
242
  # 計算所有基礎分數
243
  scores = {
244
- 'space': calculate_space_score(breed_info['Size'], user_prefs.living_space, user_prefs.space_for_play),
245
- 'exercise': calculate_exercise_score(breed_info.get('Exercise Needs', 'Moderate'), user_prefs.exercise_time),
246
- 'grooming': calculate_grooming_score(breed_info.get('Grooming Needs', 'Moderate'), user_prefs.grooming_commitment.lower(), breed_info['Size']),
247
- 'experience': calculate_experience_score(breed_info.get('Care Level', 'Moderate'), user_prefs.experience_level, breed_info.get('Temperament', '')),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  'health': calculate_health_score(breed_info.get('Breed', '')),
249
  'noise': calculate_noise_score(breed_info.get('Breed', ''), user_prefs.noise_tolerance)
250
  }
251
 
252
- # 更新權重配置
253
  weights = {
254
- 'space': 0.20,
255
- 'exercise': 0.20,
256
- 'grooming': 0.15,
257
- 'experience': 0.15,
258
- 'health': 0.15,
259
- 'noise': 0.15
260
  }
261
 
262
- # 基礎分數計算
263
- base_score = sum(score * weights[category]
264
- for category, score in scores.items()
265
- if category != 'overall')
266
-
267
- # 額外調整
268
- adjustments = 0
269
-
270
- # 1. 適應性加分
271
- if breed_info.get('Adaptability', 'Medium') == 'High':
272
- adjustments += 0.02
273
-
274
- # 2. 氣候相容性
275
- if user_prefs.climate in breed_info.get('Suitable Climate', '').split(','):
276
- adjustments += 0.02
277
-
278
- # 3. 其他寵物相容性
279
- if user_prefs.other_pets and breed_info.get('Good with Other Pets') == 'Yes':
280
- adjustments += 0.02
281
-
282
- final_score = min(1.0, max(0, base_score + adjustments))
283
- scores['overall'] = round(final_score, 4)
284
 
285
  # 四捨五入所有分數
286
- for key in scores:
287
- scores[key] = round(scores[key], 4)
288
 
289
  return scores
290
 
 
4
 
5
  @dataclass
6
  class UserPreferences:
7
+
8
  """使用者偏好設定的資料結構"""
9
  living_space: str # "apartment", "house_small", "house_large"
10
  exercise_time: int # minutes per day
 
15
  space_for_play: bool
16
  other_pets: bool
17
  climate: str # "cold", "moderate", "hot"
18
+ health_sensitivity: str = "medium"
19
  barking_acceptance: str = None
20
+ time_away: str = "moderate" # 離家時間,影響某些品種的適合度
21
+ physical_activity: str = "moderate" # 活動類型,影響運動需求匹配
22
+ training_commitment: str = "moderate" # 訓練投入,影響經驗需求
23
+ children_age: str = None
24
 
25
  def __post_init__(self):
26
  """在初始化後運行,用於設置派生值"""
27
  if self.barking_acceptance is None:
28
  self.barking_acceptance = self.noise_tolerance
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ @staticmethod
32
+ def calculate_breed_bonus(breed_info: dict, user_prefs: 'UserPreferences') -> float:
33
+ """計算品種額外加分"""
34
+ bonus = 0.0
35
+ temperament = breed_info.get('Temperament', '').lower()
36
+
37
+ # 1. 壽命加分(最高0.05)
38
+ try:
39
  lifespan = breed_info.get('Lifespan', '10-12 years')
40
+ years = [int(x) for x in lifespan.split('-')[0].split()[0:1]]
41
+ longevity_bonus = min(0.05, (max(years) - 10) * 0.01)
42
+ bonus += longevity_bonus
43
+ except:
44
+ pass
45
+
46
+ # 2. 性格特徵加分(最高0.15)
47
+ positive_traits = {
48
+ 'friendly': 0.05,
49
+ 'gentle': 0.05,
50
+ 'patient': 0.05,
51
+ 'intelligent': 0.04,
52
+ 'adaptable': 0.04,
53
+ 'affectionate': 0.04,
54
+ 'easy-going': 0.03,
55
+ 'calm': 0.03
56
+ }
57
+
58
+ negative_traits = {
59
+ 'aggressive': -0.08,
60
+ 'stubborn': -0.06,
61
+ 'dominant': -0.06,
62
+ 'aloof': -0.04,
63
+ 'nervous': -0.05,
64
+ 'protective': -0.04
65
+ }
66
+
67
+ personality_score = sum(value for trait, value in positive_traits.items() if trait in temperament)
68
+ personality_score += sum(value for trait, value in negative_traits.items() if trait in temperament)
69
+ bonus += max(-0.15, min(0.15, personality_score))
70
+
71
+ # 3. 適應性加分(最高0.1)
72
+ adaptability_bonus = 0.0
73
+ if breed_info.get('Size') == "Small" and user_prefs.living_space == "apartment":
74
+ adaptability_bonus += 0.05
75
+ if 'adaptable' in temperament or 'versatile' in temperament:
76
+ adaptability_bonus += 0.05
77
+ bonus += min(0.1, adaptability_bonus)
78
+
79
+ # 4. 家庭相容性(最高0.1)
80
+ if user_prefs.has_children:
81
+ family_traits = {
82
+ 'good with children': 0.06,
83
+ 'patient': 0.05,
84
+ 'gentle': 0.05,
85
+ 'tolerant': 0.04,
86
+ 'playful': 0.03
87
+ }
88
+ unfriendly_traits = {
89
+ 'aggressive': -0.08,
90
+ 'nervous': -0.07,
91
+ 'protective': -0.06,
92
+ 'territorial': -0.05
93
+ }
94
+
95
+ # 年齡評估這樣能更細緻
96
+ age_adjustments = {
97
+ 'toddler': {'bonus_mult': 0.7, 'penalty_mult': 1.3},
98
+ 'school_age': {'bonus_mult': 1.0, 'penalty_mult': 1.0},
99
+ 'teenager': {'bonus_mult': 1.2, 'penalty_mult': 0.8}
100
+ }
101
+
102
+ adj = age_adjustments.get(user_prefs.children_age,
103
+ {'bonus_mult': 1.0, 'penalty_mult': 1.0})
104
+
105
+ family_bonus = sum(value for trait, value in family_traits.items()
106
+ if trait in temperament) * adj['bonus_mult']
107
+ family_penalty = sum(value for trait, value in unfriendly_traits.items()
108
+ if trait in temperament) * adj['penalty_mult']
109
+
110
+ bonus += min(0.15, max(-0.2, family_bonus + family_penalty))
111
+
112
+
113
+ # 5. 專門技能加分(最高0.1)
114
+ skill_bonus = 0.0
115
+ special_abilities = {
116
+ 'working': 0.03,
117
+ 'herding': 0.03,
118
+ 'hunting': 0.03,
119
+ 'tracking': 0.03,
120
+ 'agility': 0.02
121
+ }
122
+ for ability, value in special_abilities.items():
123
+ if ability in temperament.lower():
124
+ skill_bonus += value
125
+ bonus += min(0.1, skill_bonus)
126
+
127
+ return min(0.5, max(-0.25, bonus))
128
+
129
+
130
+ @staticmethod
131
+ def calculate_additional_factors(breed_info: dict, user_prefs: 'UserPreferences') -> dict:
132
+ """計算額外的評估因素"""
133
+ factors = {
134
+ 'versatility': 0.0, # 多功能性
135
+ 'trainability': 0.0, # 可訓練度
136
+ 'energy_level': 0.0, # 能量水平
137
+ 'grooming_needs': 0.0, # 美容需求
138
+ 'social_needs': 0.0, # 社交需求
139
+ 'weather_adaptability': 0.0 # 氣候適應性
140
+ }
141
+
142
+ temperament = breed_info.get('Temperament', '').lower()
143
+ size = breed_info.get('Size', 'Medium')
144
+
145
+ # 1. 多功能性評估
146
+ versatile_traits = ['intelligent', 'adaptable', 'trainable', 'athletic']
147
+ working_roles = ['working', 'herding', 'hunting', 'sporting', 'companion']
148
+
149
+ trait_score = sum(0.2 for trait in versatile_traits if trait in temperament)
150
+ role_score = sum(0.2 for role in working_roles if role in breed_info.get('Description', '').lower())
151
+
152
+ factors['versatility'] = min(1.0, trait_score + role_score)
153
+
154
+ # 2. 可訓練度評估
155
+ trainable_traits = {
156
+ 'intelligent': 0.3,
157
+ 'eager to please': 0.3,
158
+ 'trainable': 0.2,
159
+ 'quick learner': 0.2
160
+ }
161
+ factors['trainability'] = min(1.0, sum(value for trait, value in trainable_traits.items()
162
+ if trait in temperament))
163
+
164
+ # 3. 能量水平評估
165
+ exercise_needs = breed_info.get('Exercise Needs', 'MODERATE').upper()
166
+ energy_levels = {
167
+ 'VERY HIGH': 1.0,
168
+ 'HIGH': 0.8,
169
+ 'MODERATE': 0.6,
170
+ 'LOW': 0.4,
171
+ 'VARIES': 0.6
172
+ }
173
+ factors['energy_level'] = energy_levels.get(exercise_needs, 0.6)
174
+
175
+ # 4. 美容需求評估
176
+ grooming_needs = breed_info.get('Grooming Needs', 'MODERATE').upper()
177
+ grooming_levels = {
178
+ 'HIGH': 1.0,
179
+ 'MODERATE': 0.6,
180
+ 'LOW': 0.3
181
+ }
182
+ coat_penalty = 0.2 if any(term in breed_info.get('Description', '').lower()
183
+ for term in ['long coat', 'double coat']) else 0
184
+ factors['grooming_needs'] = min(1.0, grooming_levels.get(grooming_needs, 0.6) + coat_penalty)
185
+
186
+ # 5. 社交需求評估
187
+ social_traits = ['friendly', 'social', 'affectionate', 'people-oriented']
188
+ antisocial_traits = ['independent', 'aloof', 'reserved']
189
+
190
+ social_score = sum(0.25 for trait in social_traits if trait in temperament)
191
+ antisocial_score = sum(-0.2 for trait in antisocial_traits if trait in temperament)
192
+ factors['social_needs'] = min(1.0, max(0.0, social_score + antisocial_score))
193
+
194
+ # 6. 氣候適應性評估
195
+ climate_terms = {
196
+ 'cold': ['thick coat', 'winter', 'cold climate'],
197
+ 'hot': ['short coat', 'warm climate', 'heat tolerant'],
198
+ 'moderate': ['adaptable', 'all climate']
199
+ }
200
+
201
+ climate_matches = sum(1 for term in climate_terms[user_prefs.climate]
202
+ if term in breed_info.get('Description', '').lower())
203
+ factors['weather_adaptability'] = min(1.0, climate_matches * 0.3 + 0.4) # 基礎分0.4
204
+
205
+ return factors
206
 
207
 
208
  def calculate_compatibility_score(breed_info: dict, user_prefs: UserPreferences) -> dict:
209
+ """計算品種與使用者條件的相容性分數的優化版本"""
 
210
  try:
211
+ def calculate_space_score(size: str, living_space: str, has_yard: bool, exercise_needs: str) -> float:
212
+ """空間分數計算"""
213
+ # 基礎空間需求矩陣
214
  base_scores = {
215
  "Small": {"apartment": 0.95, "house_small": 1.0, "house_large": 0.90},
216
+ "Medium": {"apartment": 0.60, "house_small": 0.90, "house_large": 1.0},
217
+ "Large": {"apartment": 0.30, "house_small": 0.75, "house_large": 1.0},
218
  "Giant": {"apartment": 0.15, "house_small": 0.55, "house_large": 1.0}
219
  }
220
+
221
+ # 取得基礎分數
222
  base_score = base_scores.get(size, base_scores["Medium"])[living_space]
223
+
224
+ # 運動需求調整
225
+ exercise_adjustments = {
226
+ "Very High": -0.15 if living_space == "apartment" else 0,
227
+ "High": -0.10 if living_space == "apartment" else 0,
228
+ "Moderate": 0,
229
+ "Low": 0.05 if living_space == "apartment" else 0
230
+ }
231
+
232
+ adjustments = exercise_adjustments.get(exercise_needs.strip(), 0)
233
+
234
+ # 院子獎勵
235
+ if has_yard and size in ["Large", "Giant"]:
236
+ adjustments += 0.10
237
+ elif has_yard:
238
  adjustments += 0.05
239
+
240
+ return min(1.0, max(0.1, base_score + adjustments))
241
 
242
+ def calculate_exercise_score(breed_needs: str, user_time: int) -> float:
243
+ """運動需求計算"""
244
+ # 更精確的運動需求定義
 
245
  exercise_needs = {
246
+ 'VERY HIGH': {'min': 120, 'ideal': 150, 'max': 180},
247
+ 'HIGH': {'min': 90, 'ideal': 120, 'max': 150},
248
+ 'MODERATE': {'min': 45, 'ideal': 60, 'max': 90},
249
+ 'LOW': {'min': 20, 'ideal': 30, 'max': 45},
250
+ 'VARIES': {'min': 30, 'ideal': 60, 'max': 90}
251
  }
252
+
253
+ breed_need = exercise_needs.get(breed_needs.strip().upper(), exercise_needs['MODERATE'])
254
+
255
+ # 計算匹配度
256
+ if user_time >= breed_need['ideal']:
257
+ if user_time > breed_need['max']:
258
+ return 0.9 # 稍微降分,因為可能過度運動
259
  return 1.0
260
+ elif user_time >= breed_need['min']:
261
+ return 0.8 + (user_time - breed_need['min']) / (breed_need['ideal'] - breed_need['min']) * 0.2
 
 
 
 
 
 
262
  else:
263
+ return max(0.3, 0.8 * (user_time / breed_need['min']))
264
 
265
+ def calculate_grooming_score(breed_needs: str, user_commitment: str, breed_size: str) -> float:
266
+ """美容需求計算"""
267
+ # 基礎分數矩陣
268
  base_scores = {
269
  "High": {"low": 0.3, "medium": 0.7, "high": 1.0},
270
  "Moderate": {"low": 0.5, "medium": 0.9, "high": 1.0},
271
+ "Low": {"low": 1.0, "medium": 0.95, "high": 0.8}
272
  }
273
+
274
+ # 取得基礎分數
275
+ base_score = base_scores.get(breed_needs, base_scores["Moderate"])[user_commitment]
276
+
277
+ # 體型影響調整
278
+ size_adjustments = {
279
+ "Large": {"low": -0.2, "medium": -0.1, "high": 0},
280
+ "Giant": {"low": -0.3, "medium": -0.15, "high": 0},
281
+ }
282
+
283
+ if breed_size in size_adjustments:
284
+ adjustment = size_adjustments[breed_size].get(user_commitment, 0)
285
+ base_score = max(0.2, base_score + adjustment)
286
+
287
  return base_score
288
 
289
+ def calculate_experience_score(care_level: str, user_experience: str, temperament: str) -> float:
290
+ """飼養經驗需求計算"""
291
+ # 降低初學者的基礎分數
292
  base_scores = {
293
+ "High": {"beginner": 0.15, "intermediate": 0.70, "advanced": 1.0},
294
+ "Moderate": {"beginner": 0.40, "intermediate": 0.85, "advanced": 1.0},
295
+ "Low": {"beginner": 0.75, "intermediate": 0.95, "advanced": 1.0}
296
  }
297
+
298
  score = base_scores.get(care_level, base_scores["Moderate"])[user_experience]
299
+
300
+ # 擴展性格特徵評估
301
  temperament_lower = temperament.lower()
302
+
303
  if user_experience == "beginner":
304
+ # 增加更多特徵評估
305
+ difficult_traits = {
306
+ 'stubborn': -0.12,
307
+ 'independent': -0.10,
308
+ 'dominant': -0.10,
309
+ 'strong-willed': -0.08,
310
+ 'protective': -0.06,
311
+ 'energetic': -0.05
312
+ }
313
+
314
+ easy_traits = {
315
+ 'gentle': 0.06,
316
+ 'friendly': 0.06,
317
+ 'eager to please': 0.06,
318
+ 'patient': 0.05,
319
+ 'adaptable': 0.05,
320
+ 'calm': 0.04
321
+ }
322
+
323
+ # 更精確的特徵影響計算
324
+ temperament_adjustments = sum(value for trait, value in easy_traits.items() if trait in temperament_lower)
325
+ temperament_adjustments += sum(value for trait, value in difficult_traits.items() if trait in temperament_lower)
326
+
327
+ # 品種特定調整
328
+ if "terrier" in breed_info['Description'].lower():
329
+ temperament_adjustments -= 0.1 # 梗類犬對新手不友善
330
+
331
+ final_score = max(0.2, min(1.0, score + temperament_adjustments))
332
+ return final_score
333
 
334
  def calculate_health_score(breed_name: str) -> float:
335
+ """計算品種健康分數"""
336
  if breed_name not in breed_health_info:
337
  return 0.5
338
 
339
  health_notes = breed_health_info[breed_name]['health_notes'].lower()
340
+
341
+ # 嚴重健康問題(降低0.15分)
342
  severe_conditions = [
343
+ 'hip dysplasia',
344
+ 'heart disease',
345
+ 'progressive retinal atrophy',
346
+ 'bloat',
347
+ 'epilepsy',
348
+ 'degenerative myelopathy',
349
+ 'von willebrand disease'
350
  ]
351
+
352
+ # 中度健康問題(降低0.1分)
353
  moderate_conditions = [
354
+ 'allergies',
355
+ 'eye problems',
356
+ 'joint problems',
357
+ 'hypothyroidism',
358
+ 'ear infections',
359
+ 'skin issues'
360
+ ]
361
+
362
+ # 輕微健康問題(降低0.05分)
363
+ minor_conditions = [
364
+ 'dental issues',
365
+ 'weight gain tendency',
366
+ 'minor allergies',
367
+ 'seasonal allergies'
368
  ]
369
 
370
+ # 計算基礎健康分數
371
+ health_score = 1.0
372
+
373
+ # 根據問題嚴重程度扣分
374
  severe_count = sum(1 for condition in severe_conditions if condition in health_notes)
375
  moderate_count = sum(1 for condition in moderate_conditions if condition in health_notes)
376
+ minor_count = sum(1 for condition in minor_conditions if condition in health_notes)
377
+
378
+ health_score -= (severe_count * 0.15)
379
+ health_score -= (moderate_count * 0.1)
380
+ health_score -= (minor_count * 0.05)
381
+
382
+ # 壽命影響
383
+ try:
384
+ lifespan = breed_health_info[breed_name].get('average_lifespan', '10-12')
385
+ years = float(lifespan.split('-')[0])
386
+ if years < 8:
387
  health_score *= 0.9
388
+ elif years > 13:
389
+ health_score *= 1.1
390
+ except:
391
+ pass
392
 
393
+ # 特殊健康優勢
394
+ if 'generally healthy' in health_notes or 'hardy breed' in health_notes:
395
+ health_score *= 1.1
396
 
397
+ return max(0.2, min(1.0, health_score))
398
 
399
  def calculate_noise_score(breed_name: str, user_noise_tolerance: str) -> float:
400
+ """計算品種噪音分數"""
401
  if breed_name not in breed_noise_info:
402
  return 0.5
403
 
404
  noise_info = breed_noise_info[breed_name]
405
  noise_level = noise_info['noise_level'].lower()
406
+ noise_notes = noise_info['noise_notes'].lower()
407
 
408
  # 基礎噪音分數矩陣
409
+ base_scores = {
410
+ 'low': {'low': 1.0, 'medium': 0.9, 'high': 0.8},
411
+ 'medium': {'low': 0.7, 'medium': 1.0, 'high': 0.9},
412
+ 'high': {'low': 0.4, 'medium': 0.7, 'high': 1.0},
413
+ 'varies': {'low': 0.6, 'medium': 0.8, 'high': 0.9}
414
  }
415
 
416
+ # 獲取基礎分數
417
+ base_score = base_scores.get(noise_level, {'low': 0.7, 'medium': 0.8, 'high': 0.6})[user_noise_tolerance]
418
+
419
+ # 吠叫原因評估
420
+ barking_reasons_penalty = 0
421
+ problematic_triggers = [
422
+ ('separation anxiety', -0.15),
423
+ ('excessive barking', -0.12),
424
+ ('territorial', -0.08),
425
+ ('alert barking', -0.05),
426
+ ('attention seeking', -0.05)
427
+ ]
428
+
429
+ for trigger, penalty in problematic_triggers:
430
+ if trigger in noise_notes:
431
+ barking_reasons_penalty += penalty
432
+
433
+ # 可訓練性補償
434
+ trainability_bonus = 0
435
+ if 'responds well to training' in noise_notes:
436
+ trainability_bonus = 0.1
437
+ elif 'can be trained' in noise_notes:
438
+ trainability_bonus = 0.05
439
 
440
+ # 特殊情況
441
  special_adjustments = 0
442
+ if 'rarely barks' in noise_notes:
443
+ special_adjustments += 0.1
444
+ if 'howls' in noise_notes and user_noise_tolerance == 'low':
445
  special_adjustments -= 0.1
 
 
 
 
 
446
 
447
+ final_score = base_score + barking_reasons_penalty + trainability_bonus + special_adjustments
448
+
449
+ return max(0.2, min(1.0, final_score))
450
 
451
  # 計算所有基礎分數
452
  scores = {
453
+ 'space': calculate_space_score(
454
+ breed_info['Size'],
455
+ user_prefs.living_space,
456
+ user_prefs.space_for_play,
457
+ breed_info.get('Exercise Needs', 'Moderate')
458
+ ),
459
+ 'exercise': calculate_exercise_score(
460
+ breed_info.get('Exercise Needs', 'Moderate'),
461
+ user_prefs.exercise_time
462
+ ),
463
+ 'grooming': calculate_grooming_score(
464
+ breed_info.get('Grooming Needs', 'Moderate'),
465
+ user_prefs.grooming_commitment.lower(),
466
+ breed_info['Size']
467
+ ),
468
+ 'experience': calculate_experience_score(
469
+ breed_info.get('Care Level', 'Moderate'),
470
+ user_prefs.experience_level,
471
+ breed_info.get('Temperament', '')
472
+ ),
473
  'health': calculate_health_score(breed_info.get('Breed', '')),
474
  'noise': calculate_noise_score(breed_info.get('Breed', ''), user_prefs.noise_tolerance)
475
  }
476
 
477
+ # 優化權重配置
478
  weights = {
479
+ 'space': 0.28,
480
+ 'exercise': 0.18,
481
+ 'grooming': 0.12,
482
+ 'experience': 0.22,
483
+ 'health': 0.12,
484
+ 'noise': 0.08
485
  }
486
 
487
+ # 計算加權總分
488
+ weighted_score = sum(score * weights[category] for category, score in scores.items())
489
+
490
+ # 擴大分數差異
491
+ def amplify_score(score):
492
+ # 使用指數函數擴大差異
493
+ amplified = pow((score - 0.5) * 2, 3) / 8 + score
494
+ return max(0.65, min(0.95, amplified)) # 限制在65%-95%範圍內
495
+
496
+ final_score = amplify_score(weighted_score)
 
 
 
 
 
 
 
 
 
 
 
 
497
 
498
  # 四捨五入所有分數
499
+ scores = {k: round(v, 4) for k, v in scores.items()}
500
+ scores['overall'] = round(final_score, 4)
501
 
502
  return scores
503