Spaces:
Running
Running
Update scoring_calculation_system.py
Browse files- scoring_calculation_system.py +185 -241
scoring_calculation_system.py
CHANGED
@@ -1073,258 +1073,202 @@ def calculate_compatibility_score(breed_info: dict, user_prefs: UserPreferences)
|
|
1073 |
|
1074 |
# 1. 計算基礎分數
|
1075 |
try:
|
1076 |
-
|
1077 |
-
|
1078 |
-
|
1079 |
-
|
1080 |
-
|
1081 |
-
|
1082 |
-
|
1083 |
-
|
1084 |
-
|
1085 |
-
|
1086 |
-
|
1087 |
-
|
1088 |
-
|
1089 |
-
|
1090 |
-
|
1091 |
-
|
1092 |
-
|
1093 |
-
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
1097 |
-
|
1098 |
-
|
1099 |
-
|
1100 |
-
|
1101 |
-
|
1102 |
-
|
1103 |
-
|
1104 |
-
|
1105 |
-
|
1106 |
-
|
1107 |
-
|
1108 |
-
|
1109 |
-
|
1110 |
-
|
1111 |
-
|
1112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1113 |
)
|
1114 |
-
|
1115 |
-
|
1116 |
-
|
1117 |
-
|
1118 |
-
|
1119 |
-
|
1120 |
-
|
1121 |
-
|
1122 |
-
|
1123 |
except Exception as e:
|
1124 |
-
print(f"
|
1125 |
-
|
|
|
|
|
|
|
|
|
1126 |
|
1127 |
-
|
1128 |
-
|
1129 |
-
|
1130 |
-
|
1131 |
-
|
1132 |
-
|
1133 |
-
except Exception as e:
|
1134 |
-
print(f"噪音分數計算錯誤: {str(e)}")
|
1135 |
-
raise
|
1136 |
|
1137 |
-
|
1138 |
-
|
1139 |
-
|
1140 |
-
|
1141 |
-
|
1142 |
-
|
1143 |
-
|
1144 |
-
|
1145 |
-
|
|
|
|
|
|
|
1146 |
|
1147 |
-
|
1148 |
-
for category, score in scores.items():
|
1149 |
-
print(f"{category}: {score}")
|
1150 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1151 |
|
1152 |
-
|
1153 |
-
|
1154 |
-
|
1155 |
-
|
1156 |
-
|
1157 |
-
|
1158 |
-
|
1159 |
-
|
1160 |
-
|
1161 |
-
|
1162 |
-
|
1163 |
-
|
1164 |
-
|
1165 |
-
|
|
|
1166 |
|
1167 |
-
|
1168 |
-
|
1169 |
-
|
1170 |
-
|
1171 |
-
|
1172 |
|
1173 |
-
|
1174 |
-
|
1175 |
-
|
1176 |
-
|
1177 |
-
|
1178 |
-
|
1179 |
-
|
1180 |
-
|
1181 |
-
|
1182 |
-
|
1183 |
-
|
1184 |
-
# 'noise': 0.08
|
1185 |
-
# }
|
1186 |
-
|
1187 |
-
# # 根據居住環境調整權重
|
1188 |
-
# if user_prefs.living_space == 'apartment':
|
1189 |
-
# base_weights['space'] *= 1.2
|
1190 |
-
# base_weights['noise'] *= 1.2
|
1191 |
-
|
1192 |
-
# # 根據經驗等級調整權重
|
1193 |
-
# if user_prefs.experience_level == 'beginner':
|
1194 |
-
# base_weights['experience'] *= 1.3
|
1195 |
-
|
1196 |
-
# # 重新正規化權重
|
1197 |
-
# total_weight = sum(base_weights.values())
|
1198 |
-
# weights = {k: v/total_weight for k, v in base_weights.items()}
|
1199 |
-
|
1200 |
-
# # 計算加權分數
|
1201 |
-
# return sum(score * weights[category] for category, score in scores.items())
|
1202 |
|
1203 |
-
|
1204 |
-
|
1205 |
-
|
1206 |
-
|
1207 |
-
|
1208 |
-
|
1209 |
-
|
1210 |
-
|
1211 |
-
|
1212 |
-
Returns:
|
1213 |
-
float: 加權後的最終分數
|
1214 |
-
"""
|
1215 |
-
# 基礎權重設定
|
1216 |
-
base_weights = {
|
1217 |
-
'space': 0.25,
|
1218 |
-
'exercise': 0.20,
|
1219 |
-
'grooming': 0.15,
|
1220 |
-
'experience': 0.18,
|
1221 |
-
'health': 0.12,
|
1222 |
-
'noise': 0.10
|
1223 |
-
}
|
1224 |
-
|
1225 |
-
# 根據使用者經驗調整權重
|
1226 |
-
if user_prefs.experience_level == 'beginner':
|
1227 |
-
base_weights['experience'] *= 1.2
|
1228 |
-
base_weights['health'] *= 1.1
|
1229 |
-
base_weights['grooming'] *= 0.9
|
1230 |
-
elif user_prefs.experience_level == 'advanced':
|
1231 |
-
base_weights['exercise'] *= 1.2
|
1232 |
-
base_weights['experience'] *= 0.8
|
1233 |
-
|
1234 |
-
# 根據居住環境調整權重
|
1235 |
-
if user_prefs.living_space == 'apartment':
|
1236 |
-
base_weights['noise'] *= 1.3
|
1237 |
-
base_weights['space'] *= 1.2
|
1238 |
-
elif user_prefs.living_space == 'house_large':
|
1239 |
-
base_weights['exercise'] *= 1.2
|
1240 |
-
base_weights['space'] *= 0.9
|
1241 |
-
|
1242 |
-
# 有孩童時的權重調整
|
1243 |
-
if user_prefs.has_children:
|
1244 |
-
base_weights['noise'] *= 1.2
|
1245 |
-
base_weights['health'] *= 1.1
|
1246 |
-
|
1247 |
-
# 重新正規化權重
|
1248 |
-
total_weight = sum(base_weights.values())
|
1249 |
-
weights = {k: v/total_weight for k, v in base_weights.items()}
|
1250 |
-
|
1251 |
-
# 計算加權分數
|
1252 |
-
weighted_score = sum(score * weights[category] for category, score in scores.items())
|
1253 |
-
|
1254 |
-
# 計算品種特性加成
|
1255 |
-
breed_bonus = calculate_breed_characteristic_bonus(breed_info, user_prefs)
|
1256 |
-
|
1257 |
-
# 混合基礎分數和特性加成
|
1258 |
-
final_score = (weighted_score * 0.85) + (breed_bonus * 0.15)
|
1259 |
-
|
1260 |
-
return min(0.95, max(0.55, final_score))
|
1261 |
-
|
1262 |
-
def calculate_breed_characteristic_bonus(breed_info: dict, user_prefs: UserPreferences) -> float:
|
1263 |
-
"""
|
1264 |
-
計算品種特性加成,增加品種多樣性
|
1265 |
-
"""
|
1266 |
-
bonus = 0.0
|
1267 |
-
temperament = breed_info.get('Temperament', '').lower()
|
1268 |
-
description = breed_info.get('Description', '').lower()
|
1269 |
-
|
1270 |
-
# 品種類型加成
|
1271 |
-
breed_types = {
|
1272 |
-
'working': {'keywords': ['working', 'guard', 'protection'], 'bonus': 0.05},
|
1273 |
-
'companion': {'keywords': ['companion', 'friendly', 'affectionate'], 'bonus': 0.05},
|
1274 |
-
'sporting': {'keywords': ['hunting', 'sporting', 'athletic'], 'bonus': 0.05},
|
1275 |
-
'herding': {'keywords': ['herding', 'shepherd', 'cattle'], 'bonus': 0.05}
|
1276 |
-
}
|
1277 |
-
|
1278 |
-
# 根據使用場景給予特定加成
|
1279 |
-
for breed_type, info in breed_types.items():
|
1280 |
-
if any(keyword in description or keyword in temperament for keyword in info['keywords']):
|
1281 |
-
if user_prefs.has_children and breed_type == 'companion':
|
1282 |
-
bonus += info['bonus'] * 1.5
|
1283 |
-
elif user_prefs.exercise_type == 'active_training' and breed_type in ['working', 'sporting']:
|
1284 |
-
bonus += info['bonus'] * 1.3
|
1285 |
-
else:
|
1286 |
-
bonus += info['bonus']
|
1287 |
-
|
1288 |
-
# 特殊加成(增加多樣性)
|
1289 |
-
if 'rare' in description or 'unique' in description:
|
1290 |
-
bonus += 0.03
|
1291 |
-
if 'independent' in temperament and user_prefs.experience_level == 'advanced':
|
1292 |
-
bonus += 0.04
|
1293 |
-
|
1294 |
-
return min(0.15, bonus) # 限制最大加成
|
1295 |
-
|
1296 |
|
1297 |
-
|
1298 |
-
|
1299 |
-
|
1300 |
-
|
1301 |
-
|
1302 |
-
|
1303 |
-
|
1304 |
-
|
1305 |
-
# 將分數映射到期望的範圍(0.55-0.95)
|
1306 |
-
mapped_score = 0.55 + (adjusted_score * 0.4)
|
1307 |
-
|
1308 |
-
# 確保分數在合理範圍內
|
1309 |
-
final = max(0.55, min(0.95, mapped_score))
|
1310 |
-
|
1311 |
-
return round(final, 4)
|
1312 |
|
1313 |
-
|
1314 |
-
|
1315 |
-
|
1316 |
-
|
1317 |
|
1318 |
-
|
1319 |
-
|
1320 |
-
|
1321 |
-
|
1322 |
-
return scores
|
1323 |
|
1324 |
-
|
1325 |
-
|
1326 |
-
|
1327 |
-
|
1328 |
-
|
1329 |
-
|
1330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1073 |
|
1074 |
# 1. 計算基礎分數
|
1075 |
try:
|
1076 |
+
print("\n=== 開始計算品種相容性分數 ===")
|
1077 |
+
print(f"處理品種: {breed_info.get('Breed', 'Unknown')}")
|
1078 |
+
print(f"品種信息: {breed_info}")
|
1079 |
+
print(f"使用者偏好: {vars(user_prefs)}")
|
1080 |
+
|
1081 |
+
# 計算所有基礎分數並整合到字典中
|
1082 |
+
scores = {
|
1083 |
+
'space': calculate_space_score(
|
1084 |
+
breed_info['Size'],
|
1085 |
+
user_prefs.living_space,
|
1086 |
+
user_prefs.yard_access != 'no_yard',
|
1087 |
+
breed_info.get('Exercise Needs', 'Moderate')
|
1088 |
+
),
|
1089 |
+
'exercise': calculate_exercise_score(
|
1090 |
+
breed_info.get('Exercise Needs', 'Moderate'),
|
1091 |
+
user_prefs.exercise_time
|
1092 |
+
),
|
1093 |
+
'grooming': calculate_grooming_score(
|
1094 |
+
breed_info.get('Grooming Needs', 'Moderate'),
|
1095 |
+
user_prefs.grooming_commitment.lower(),
|
1096 |
+
breed_info['Size']
|
1097 |
+
),
|
1098 |
+
'experience': calculate_experience_score(
|
1099 |
+
breed_info.get('Care Level', 'Moderate'),
|
1100 |
+
user_prefs.experience_level,
|
1101 |
+
breed_info.get('Temperament', '')
|
1102 |
+
),
|
1103 |
+
'health': calculate_health_score(
|
1104 |
+
breed_info.get('Breed', ''),
|
1105 |
+
user_prefs
|
1106 |
+
),
|
1107 |
+
'noise': calculate_noise_score(
|
1108 |
+
breed_info.get('Breed', ''),
|
1109 |
+
user_prefs
|
1110 |
+
)
|
1111 |
+
}
|
1112 |
+
|
1113 |
+
# 檢查關鍵不適配情況
|
1114 |
+
critical_issues = check_critical_matches(scores, user_prefs)
|
1115 |
+
if critical_issues['has_critical']:
|
1116 |
+
return apply_critical_penalty(scores, critical_issues)
|
1117 |
+
|
1118 |
+
# 計算環境適應性加成
|
1119 |
+
adaptability_bonus = calculate_environmental_fit(breed_info, user_prefs)
|
1120 |
+
|
1121 |
+
# 計算最終加權分數
|
1122 |
+
final_score = calculate_final_weighted_score(
|
1123 |
+
scores=scores,
|
1124 |
+
user_prefs=user_prefs,
|
1125 |
+
breed_info=breed_info,
|
1126 |
+
adaptability_bonus=adaptability_bonus
|
1127 |
)
|
1128 |
+
|
1129 |
+
# 更新最終結果
|
1130 |
+
scores.update({
|
1131 |
+
'overall': final_score,
|
1132 |
+
'adaptability_bonus': adaptability_bonus
|
1133 |
+
})
|
1134 |
+
|
1135 |
+
return scores
|
1136 |
+
|
1137 |
except Exception as e:
|
1138 |
+
print(f"\n!!!!! 發生嚴重錯誤 !!!!!")
|
1139 |
+
print(f"錯誤類型: {type(e).__name__}")
|
1140 |
+
print(f"錯誤訊息: {str(e)}")
|
1141 |
+
print(f"完整錯誤追蹤:")
|
1142 |
+
print(traceback.format_exc())
|
1143 |
+
return {k: 0.6 for k in ['space', 'exercise', 'grooming', 'experience', 'health', 'noise', 'overall']}
|
1144 |
|
1145 |
+
def check_critical_matches(scores: dict, user_prefs: UserPreferences) -> dict:
|
1146 |
+
"""評估是否存在極端不適配的情況"""
|
1147 |
+
critical_issues = {
|
1148 |
+
'has_critical': False,
|
1149 |
+
'reasons': []
|
1150 |
+
}
|
|
|
|
|
|
|
1151 |
|
1152 |
+
# 檢查極端不適配情況
|
1153 |
+
if scores['space'] < 0.3:
|
1154 |
+
critical_issues['has_critical'] = True
|
1155 |
+
critical_issues['reasons'].append('space_incompatible')
|
1156 |
+
|
1157 |
+
if scores['noise'] < 0.3 and user_prefs.living_space == 'apartment':
|
1158 |
+
critical_issues['has_critical'] = True
|
1159 |
+
critical_issues['reasons'].append('noise_incompatible')
|
1160 |
+
|
1161 |
+
if scores['experience'] < 0.3 and user_prefs.experience_level == 'beginner':
|
1162 |
+
critical_issues['has_critical'] = True
|
1163 |
+
critical_issues['reasons'].append('too_challenging')
|
1164 |
|
1165 |
+
return critical_issues
|
|
|
|
|
1166 |
|
1167 |
+
def apply_critical_penalty(scores: dict, critical_issues: dict) -> dict:
|
1168 |
+
"""當發現關鍵不適配時,調整分數"""
|
1169 |
+
penalized_scores = scores.copy()
|
1170 |
+
penalty_factor = 0.6
|
1171 |
+
|
1172 |
+
for reason in critical_issues['reasons']:
|
1173 |
+
if reason == 'space_incompatible':
|
1174 |
+
penalized_scores['overall'] *= penalty_factor
|
1175 |
+
penalized_scores['space'] *= penalty_factor
|
1176 |
+
elif reason == 'noise_incompatible':
|
1177 |
+
penalized_scores['overall'] *= penalty_factor
|
1178 |
+
penalized_scores['noise'] *= penalty_factor
|
1179 |
+
elif reason == 'too_challenging':
|
1180 |
+
penalized_scores['overall'] *= penalty_factor
|
1181 |
+
penalized_scores['experience'] *= penalty_factor
|
1182 |
+
|
1183 |
+
return penalized_scores
|
1184 |
|
1185 |
+
def calculate_environmental_fit(breed_info: dict, user_prefs: UserPreferences) -> float:
|
1186 |
+
"""計算品種與環境的適應性加成"""
|
1187 |
+
adaptability_score = 0.0
|
1188 |
+
description = breed_info.get('Description', '').lower()
|
1189 |
+
temperament = breed_info.get('Temperament', '').lower()
|
1190 |
+
|
1191 |
+
# 環境適應性評估
|
1192 |
+
if user_prefs.living_space == 'apartment':
|
1193 |
+
if 'adaptable' in temperament or 'apartment' in description:
|
1194 |
+
adaptability_score += 0.1
|
1195 |
+
if breed_info.get('Size') == 'Small':
|
1196 |
+
adaptability_score += 0.05
|
1197 |
+
elif user_prefs.living_space == 'house_large':
|
1198 |
+
if 'active' in temperament or 'energetic' in description:
|
1199 |
+
adaptability_score += 0.1
|
1200 |
|
1201 |
+
# 氣候適應性
|
1202 |
+
if user_prefs.climate in description or user_prefs.climate in temperament:
|
1203 |
+
adaptability_score += 0.05
|
1204 |
+
|
1205 |
+
return min(0.2, adaptability_score)
|
1206 |
|
1207 |
+
def calculate_final_weighted_score(scores: dict, user_prefs: UserPreferences,
|
1208 |
+
breed_info: dict, adaptability_bonus: float) -> float:
|
1209 |
+
"""計算最終加權分數"""
|
1210 |
+
base_weights = {
|
1211 |
+
'space': 0.25,
|
1212 |
+
'exercise': 0.20,
|
1213 |
+
'grooming': 0.15,
|
1214 |
+
'experience': 0.18,
|
1215 |
+
'health': 0.12,
|
1216 |
+
'noise': 0.10
|
1217 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1218 |
|
1219 |
+
# 根據居住環境動態調整權重
|
1220 |
+
if user_prefs.living_space == 'apartment':
|
1221 |
+
base_weights['space'] *= 1.4
|
1222 |
+
base_weights['noise'] *= 1.3
|
1223 |
+
base_weights['exercise'] *= 0.8
|
1224 |
+
elif user_prefs.living_space == 'house_large':
|
1225 |
+
base_weights['exercise'] *= 1.3
|
1226 |
+
base_weights['space'] *= 0.9
|
1227 |
+
base_weights['noise'] *= 0.8
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1228 |
|
1229 |
+
# 根據經驗等級調整權重
|
1230 |
+
if user_prefs.experience_level == 'beginner':
|
1231 |
+
base_weights['experience'] *= 1.5
|
1232 |
+
base_weights['health'] *= 1.2
|
1233 |
+
elif user_prefs.experience_level == 'advanced':
|
1234 |
+
base_weights['exercise'] *= 1.2
|
1235 |
+
base_weights['experience'] *= 0.8
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1236 |
|
1237 |
+
# 有孩童時的特殊調整
|
1238 |
+
if user_prefs.has_children:
|
1239 |
+
base_weights['noise'] *= 1.3
|
1240 |
+
base_weights['health'] *= 1.2
|
1241 |
|
1242 |
+
# 重新正規化權重
|
1243 |
+
total_weight = sum(base_weights.values())
|
1244 |
+
weights = {k: v/total_weight for k, v in base_weights.items()}
|
|
|
|
|
1245 |
|
1246 |
+
# 計算基礎加權分數
|
1247 |
+
weighted_base = sum(score * weights[category] for category, score in scores.items())
|
1248 |
+
|
1249 |
+
# 品種特性加成
|
1250 |
+
breed_bonus = calculate_breed_bonus(breed_info, user_prefs)
|
1251 |
+
|
1252 |
+
# 計算最終分數
|
1253 |
+
final_score = (weighted_base * 0.75) + (breed_bonus * 0.15) + (adaptability_bonus * 0.10)
|
1254 |
+
|
1255 |
+
# 擴大分數範圍
|
1256 |
+
amplified_score = amplify_score_range(final_score)
|
1257 |
+
|
1258 |
+
return round(amplified_score, 4)
|
1259 |
+
|
1260 |
+
def amplify_score_range(score: float) -> float:
|
1261 |
+
"""擴大分數範圍,使差異更明顯"""
|
1262 |
+
min_score = 0.45
|
1263 |
+
max_score = 0.95
|
1264 |
+
|
1265 |
+
# 正規化到 0-1 範圍
|
1266 |
+
normalized = (score - 0.5) / 0.5
|
1267 |
+
|
1268 |
+
# 非線性轉換
|
1269 |
+
amplified = math.pow(abs(normalized), 1.5) * math.copysign(1, normalized)
|
1270 |
+
|
1271 |
+
# 映射回目標範圍
|
1272 |
+
final = min_score + (max_score - min_score) * ((amplified + 1) / 2)
|
1273 |
+
|
1274 |
+
return min(max_score, max(min_score, final))
|