Spaces:
Running
on
Zero
Running
on
Zero
Delete recommendation_html_format.py
Browse files- recommendation_html_format.py +0 -496
recommendation_html_format.py
DELETED
|
@@ -1,496 +0,0 @@
|
|
| 1 |
-
import sqlite3
|
| 2 |
-
import traceback
|
| 3 |
-
from typing import List, Dict
|
| 4 |
-
from breed_health_info import breed_health_info, default_health_note
|
| 5 |
-
from breed_noise_info import breed_noise_info
|
| 6 |
-
from dog_database import get_dog_description
|
| 7 |
-
from scoring_calculation_system import (
|
| 8 |
-
UserPreferences,
|
| 9 |
-
calculate_compatibility_score
|
| 10 |
-
)
|
| 11 |
-
|
| 12 |
-
def format_recommendation_html(recommendations: List[Dict]) -> str:
|
| 13 |
-
"""將推薦結果格式化為HTML"""
|
| 14 |
-
html_content = "<div class='recommendations-container'>"
|
| 15 |
-
|
| 16 |
-
for rec in recommendations:
|
| 17 |
-
breed = rec['breed']
|
| 18 |
-
scores = rec['scores']
|
| 19 |
-
info = rec['info']
|
| 20 |
-
rank = rec.get('rank', 0)
|
| 21 |
-
final_score = rec.get('final_score', scores['overall'])
|
| 22 |
-
bonus_score = rec.get('bonus_score', 0)
|
| 23 |
-
|
| 24 |
-
health_info = breed_health_info.get(breed, {"health_notes": default_health_note})
|
| 25 |
-
noise_info = breed_noise_info.get(breed, {
|
| 26 |
-
"noise_notes": "Noise information not available",
|
| 27 |
-
"noise_level": "Unknown",
|
| 28 |
-
"source": "N/A"
|
| 29 |
-
})
|
| 30 |
-
|
| 31 |
-
# 解析噪音資訊
|
| 32 |
-
noise_notes = noise_info.get('noise_notes', '').split('\n')
|
| 33 |
-
noise_characteristics = []
|
| 34 |
-
barking_triggers = []
|
| 35 |
-
noise_level = ''
|
| 36 |
-
|
| 37 |
-
current_section = None
|
| 38 |
-
for line in noise_notes:
|
| 39 |
-
line = line.strip()
|
| 40 |
-
if 'Typical noise characteristics:' in line:
|
| 41 |
-
current_section = 'characteristics'
|
| 42 |
-
elif 'Noise level:' in line:
|
| 43 |
-
noise_level = line.replace('Noise level:', '').strip()
|
| 44 |
-
elif 'Barking triggers:' in line:
|
| 45 |
-
current_section = 'triggers'
|
| 46 |
-
elif line.startswith('•'):
|
| 47 |
-
if current_section == 'characteristics':
|
| 48 |
-
noise_characteristics.append(line[1:].strip())
|
| 49 |
-
elif current_section == 'triggers':
|
| 50 |
-
barking_triggers.append(line[1:].strip())
|
| 51 |
-
|
| 52 |
-
# 生成特徵和觸發因素的HTML
|
| 53 |
-
noise_characteristics_html = '\n'.join([f'<li>{item}</li>' for item in noise_characteristics])
|
| 54 |
-
barking_triggers_html = '\n'.join([f'<li>{item}</li>' for item in barking_triggers])
|
| 55 |
-
|
| 56 |
-
# 處理健康資訊
|
| 57 |
-
health_notes = health_info.get('health_notes', '').split('\n')
|
| 58 |
-
health_considerations = []
|
| 59 |
-
health_screenings = []
|
| 60 |
-
|
| 61 |
-
current_section = None
|
| 62 |
-
for line in health_notes:
|
| 63 |
-
line = line.strip()
|
| 64 |
-
if 'Common breed-specific health considerations' in line:
|
| 65 |
-
current_section = 'considerations'
|
| 66 |
-
elif 'Recommended health screenings:' in line:
|
| 67 |
-
current_section = 'screenings'
|
| 68 |
-
elif line.startswith('•'):
|
| 69 |
-
if current_section == 'considerations':
|
| 70 |
-
health_considerations.append(line[1:].strip())
|
| 71 |
-
elif current_section == 'screenings':
|
| 72 |
-
health_screenings.append(line[1:].strip())
|
| 73 |
-
|
| 74 |
-
health_considerations_html = '\n'.join([f'<li>{item}</li>' for item in health_considerations])
|
| 75 |
-
health_screenings_html = '\n'.join([f'<li>{item}</li>' for item in health_screenings])
|
| 76 |
-
|
| 77 |
-
# 獎勵原因計算
|
| 78 |
-
bonus_reasons = []
|
| 79 |
-
temperament = info.get('Temperament', '').lower()
|
| 80 |
-
if any(trait in temperament for trait in ['friendly', 'gentle', 'affectionate']):
|
| 81 |
-
bonus_reasons.append("Positive temperament traits")
|
| 82 |
-
if info.get('Good with Children') == 'Yes':
|
| 83 |
-
bonus_reasons.append("Excellent with children")
|
| 84 |
-
try:
|
| 85 |
-
lifespan = info.get('Lifespan', '10-12 years')
|
| 86 |
-
years = int(lifespan.split('-')[0])
|
| 87 |
-
if years > 12:
|
| 88 |
-
bonus_reasons.append("Above-average lifespan")
|
| 89 |
-
except:
|
| 90 |
-
pass
|
| 91 |
-
|
| 92 |
-
html_content += f"""
|
| 93 |
-
<div class="dog-info-card recommendation-card">
|
| 94 |
-
<div class="breed-info">
|
| 95 |
-
<h2 class="section-title">
|
| 96 |
-
<span class="icon">🏆</span> #{rank} {breed.replace('_', ' ')}
|
| 97 |
-
<span class="score-badge">
|
| 98 |
-
Overall Match: {final_score*100:.1f}%
|
| 99 |
-
</span>
|
| 100 |
-
</h2>
|
| 101 |
-
<div class="compatibility-scores">
|
| 102 |
-
<div class="score-item">
|
| 103 |
-
<span class="label">Space Compatibility:</span>
|
| 104 |
-
<div class="progress-bar">
|
| 105 |
-
<div class="progress" style="width: {scores['space']*100}%"></div>
|
| 106 |
-
</div>
|
| 107 |
-
<span class="percentage">{scores['space']*100:.1f}%</span>
|
| 108 |
-
</div>
|
| 109 |
-
<div class="score-item">
|
| 110 |
-
<span class="label">Exercise Match:</span>
|
| 111 |
-
<div class="progress-bar">
|
| 112 |
-
<div class="progress" style="width: {scores['exercise']*100}%"></div>
|
| 113 |
-
</div>
|
| 114 |
-
<span class="percentage">{scores['exercise']*100:.1f}%</span>
|
| 115 |
-
</div>
|
| 116 |
-
<div class="score-item">
|
| 117 |
-
<span class="label">Grooming Match:</span>
|
| 118 |
-
<div class="progress-bar">
|
| 119 |
-
<div class="progress" style="width: {scores['grooming']*100}%"></div>
|
| 120 |
-
</div>
|
| 121 |
-
<span class="percentage">{scores['grooming']*100:.1f}%</span>
|
| 122 |
-
</div>
|
| 123 |
-
<div class="score-item">
|
| 124 |
-
<span class="label">Experience Match:</span>
|
| 125 |
-
<div class="progress-bar">
|
| 126 |
-
<div class="progress" style="width: {scores['experience']*100}%"></div>
|
| 127 |
-
</div>
|
| 128 |
-
<span class="percentage">{scores['experience']*100:.1f}%</span>
|
| 129 |
-
</div>
|
| 130 |
-
<div class="score-item">
|
| 131 |
-
<span class="label">
|
| 132 |
-
Noise Compatibility:
|
| 133 |
-
<span class="tooltip">
|
| 134 |
-
<span class="tooltip-icon">ⓘ</span>
|
| 135 |
-
<span class="tooltip-text">
|
| 136 |
-
<strong>Noise Compatibility Score:</strong><br>
|
| 137 |
-
• Based on your noise tolerance preference<br>
|
| 138 |
-
• Considers breed's typical noise level<br>
|
| 139 |
-
• Accounts for living environment
|
| 140 |
-
</span>
|
| 141 |
-
</span>
|
| 142 |
-
</span>
|
| 143 |
-
<div class="progress-bar">
|
| 144 |
-
<div class="progress" style="width: {scores['noise']*100}%"></div>
|
| 145 |
-
</div>
|
| 146 |
-
<span class="percentage">{scores['noise']*100:.1f}%</span>
|
| 147 |
-
</div>
|
| 148 |
-
{f'''
|
| 149 |
-
<div class="score-item bonus-score">
|
| 150 |
-
<span class="label">
|
| 151 |
-
Breed Bonus:
|
| 152 |
-
<span class="tooltip">
|
| 153 |
-
<span class="tooltip-icon">ⓘ</span>
|
| 154 |
-
<span class="tooltip-text">
|
| 155 |
-
<strong>Breed Bonus Points:</strong><br>
|
| 156 |
-
• {('<br>• '.join(bonus_reasons)) if bonus_reasons else 'No additional bonus points'}<br>
|
| 157 |
-
<br>
|
| 158 |
-
<strong>Bonus Factors Include:</strong><br>
|
| 159 |
-
• Friendly temperament<br>
|
| 160 |
-
• Child compatibility<br>
|
| 161 |
-
• Longer lifespan<br>
|
| 162 |
-
• Living space adaptability
|
| 163 |
-
</span>
|
| 164 |
-
</span>
|
| 165 |
-
</span>
|
| 166 |
-
<div class="progress-bar">
|
| 167 |
-
<div class="progress" style="width: {bonus_score*100}%"></div>
|
| 168 |
-
</div>
|
| 169 |
-
<span class="percentage">{bonus_score*100:.1f}%</span>
|
| 170 |
-
</div>
|
| 171 |
-
''' if bonus_score > 0 else ''}
|
| 172 |
-
</div>
|
| 173 |
-
<div class="breed-details-section">
|
| 174 |
-
<h3 class="subsection-title">
|
| 175 |
-
<span class="icon">📋</span> Breed Details
|
| 176 |
-
</h3>
|
| 177 |
-
<div class="details-grid">
|
| 178 |
-
<div class="detail-item">
|
| 179 |
-
<span class="tooltip">
|
| 180 |
-
<span class="icon">📏</span>
|
| 181 |
-
<span class="label">Size:</span>
|
| 182 |
-
<span class="tooltip-icon">ⓘ</span>
|
| 183 |
-
<span class="tooltip-text">
|
| 184 |
-
<strong>Size Categories:</strong><br>
|
| 185 |
-
• Small: Under 20 pounds<br>
|
| 186 |
-
• Medium: 20-60 pounds<br>
|
| 187 |
-
• Large: Over 60 pounds
|
| 188 |
-
</span>
|
| 189 |
-
<span class="value">{info['Size']}</span>
|
| 190 |
-
</span>
|
| 191 |
-
</div>
|
| 192 |
-
<div class="detail-item">
|
| 193 |
-
<span class="tooltip">
|
| 194 |
-
<span class="icon">🏃</span>
|
| 195 |
-
<span class="label">Exercise Needs:</span>
|
| 196 |
-
<span class="tooltip-icon">ⓘ</span>
|
| 197 |
-
<span class="tooltip-text">
|
| 198 |
-
<strong>Exercise Needs:</strong><br>
|
| 199 |
-
• Low: Short walks<br>
|
| 200 |
-
• Moderate: 1-2 hours daily<br>
|
| 201 |
-
• High: 2+ hours daily<br>
|
| 202 |
-
• Very High: Constant activity
|
| 203 |
-
</span>
|
| 204 |
-
<span class="value">{info['Exercise Needs']}</span>
|
| 205 |
-
</span>
|
| 206 |
-
</div>
|
| 207 |
-
<div class="detail-item">
|
| 208 |
-
<span class="tooltip">
|
| 209 |
-
<span class="icon">👨👩👧👦</span>
|
| 210 |
-
<span class="label">Good with Children:</span>
|
| 211 |
-
<span class="tooltip-icon">ⓘ</span>
|
| 212 |
-
<span class="tooltip-text">
|
| 213 |
-
<strong>Child Compatibility:</strong><br>
|
| 214 |
-
• Yes: Excellent with kids<br>
|
| 215 |
-
• Moderate: Good with older children<br>
|
| 216 |
-
• No: Better for adult households
|
| 217 |
-
</span>
|
| 218 |
-
<span class="value">{info['Good with Children']}</span>
|
| 219 |
-
</span>
|
| 220 |
-
</div>
|
| 221 |
-
<div class="detail-item">
|
| 222 |
-
<span class="tooltip">
|
| 223 |
-
<span class="icon">⏳</span>
|
| 224 |
-
<span class="label">Lifespan:</span>
|
| 225 |
-
<span class="tooltip-icon">ⓘ</span>
|
| 226 |
-
<span class="tooltip-text">
|
| 227 |
-
<strong>Average Lifespan:</strong><br>
|
| 228 |
-
• Short: 6-8 years<br>
|
| 229 |
-
• Average: 10-15 years<br>
|
| 230 |
-
• Long: 12-20 years<br>
|
| 231 |
-
• Varies by size: Larger breeds typically have shorter lifespans
|
| 232 |
-
</span>
|
| 233 |
-
</span>
|
| 234 |
-
<span class="value">{info['Lifespan']}</span>
|
| 235 |
-
</div>
|
| 236 |
-
</div>
|
| 237 |
-
</div>
|
| 238 |
-
<div class="description-section">
|
| 239 |
-
<h3 class="subsection-title">
|
| 240 |
-
<span class="icon">📝</span> Description
|
| 241 |
-
</h3>
|
| 242 |
-
<p class="description-text">{info.get('Description', '')}</p>
|
| 243 |
-
</div>
|
| 244 |
-
<div class="noise-section">
|
| 245 |
-
<h3 class="section-header">
|
| 246 |
-
<span class="icon">🔊</span> Noise Behavior
|
| 247 |
-
<span class="tooltip">
|
| 248 |
-
<span class="tooltip-icon">ⓘ</span>
|
| 249 |
-
<span class="tooltip-text">
|
| 250 |
-
<strong>Noise Behavior:</strong><br>
|
| 251 |
-
• Typical vocalization patterns<br>
|
| 252 |
-
• Common triggers and frequency<br>
|
| 253 |
-
• Based on breed characteristics
|
| 254 |
-
</span>
|
| 255 |
-
</span>
|
| 256 |
-
</h3>
|
| 257 |
-
<div class="noise-info">
|
| 258 |
-
<div class="noise-details">
|
| 259 |
-
<h4 class="section-header">Typical noise characteristics:</h4>
|
| 260 |
-
<div class="characteristics-list">
|
| 261 |
-
<div class="list-item">Moderate to high barker</div>
|
| 262 |
-
<div class="list-item">Alert watch dog</div>
|
| 263 |
-
<div class="list-item">Attention-seeking barks</div>
|
| 264 |
-
<div class="list-item">Social vocalizations</div>
|
| 265 |
-
</div>
|
| 266 |
-
|
| 267 |
-
<div class="noise-level-display">
|
| 268 |
-
<h4 class="section-header">Noise level:</h4>
|
| 269 |
-
<div class="level-indicator">
|
| 270 |
-
<span class="level-text">Moderate-High</span>
|
| 271 |
-
<div class="level-bars">
|
| 272 |
-
<span class="bar"></span>
|
| 273 |
-
<span class="bar"></span>
|
| 274 |
-
<span class="bar"></span>
|
| 275 |
-
</div>
|
| 276 |
-
</div>
|
| 277 |
-
</div>
|
| 278 |
-
|
| 279 |
-
<h4 class="section-header">Barking triggers:</h4>
|
| 280 |
-
<div class="triggers-list">
|
| 281 |
-
<div class="list-item">Separation anxiety</div>
|
| 282 |
-
<div class="list-item">Attention needs</div>
|
| 283 |
-
<div class="list-item">Strange noises</div>
|
| 284 |
-
<div class="list-item">Excitement</div>
|
| 285 |
-
</div>
|
| 286 |
-
</div>
|
| 287 |
-
<div class="noise-disclaimer">
|
| 288 |
-
<p class="disclaimer-text source-text">Source: Compiled from various breed behavior resources, 2024</p>
|
| 289 |
-
<p class="disclaimer-text">Individual dogs may vary in their vocalization patterns.</p>
|
| 290 |
-
<p class="disclaimer-text">Training can significantly influence barking behavior.</p>
|
| 291 |
-
<p class="disclaimer-text">Environmental factors may affect noise levels.</p>
|
| 292 |
-
</div>
|
| 293 |
-
</div>
|
| 294 |
-
</div>
|
| 295 |
-
|
| 296 |
-
<div class="health-section">
|
| 297 |
-
<h3 class="section-header">
|
| 298 |
-
<span class="icon">🏥</span> Health Insights
|
| 299 |
-
<span class="tooltip">
|
| 300 |
-
<span class="tooltip-icon">ⓘ</span>
|
| 301 |
-
<span class="tooltip-text">
|
| 302 |
-
Health information is compiled from multiple sources including veterinary resources, breed guides, and international canine health databases.
|
| 303 |
-
Each dog is unique and may vary from these general guidelines.
|
| 304 |
-
</span>
|
| 305 |
-
</span>
|
| 306 |
-
</h3>
|
| 307 |
-
<div class="health-info">
|
| 308 |
-
<div class="health-details">
|
| 309 |
-
<div class="health-block">
|
| 310 |
-
<h4 class="section-header">Common breed-specific health considerations:</h4>
|
| 311 |
-
<div class="health-grid">
|
| 312 |
-
<div class="health-item">Patellar luxation</div>
|
| 313 |
-
<div class="health-item">Progressive retinal atrophy</div>
|
| 314 |
-
<div class="health-item">Von Willebrand's disease</div>
|
| 315 |
-
<div class="health-item">Open fontanel</div>
|
| 316 |
-
</div>
|
| 317 |
-
</div>
|
| 318 |
-
|
| 319 |
-
<div class="health-block">
|
| 320 |
-
<h4 class="section-header">Recommended health screenings:</h4>
|
| 321 |
-
<div class="health-grid">
|
| 322 |
-
<div class="health-item screening">Patella evaluation</div>
|
| 323 |
-
<div class="health-item screening">Eye examination</div>
|
| 324 |
-
<div class="health-item screening">Blood clotting tests</div>
|
| 325 |
-
<div class="health-item screening">Skull development monitoring</div>
|
| 326 |
-
</div>
|
| 327 |
-
</div>
|
| 328 |
-
</div>
|
| 329 |
-
<div class="health-disclaimer">
|
| 330 |
-
<p class="disclaimer-text source-text">Source: Compiled from various veterinary and breed information resources, 2024</p>
|
| 331 |
-
<p class="disclaimer-text">This information is for reference only and based on breed tendencies.</p>
|
| 332 |
-
<p class="disclaimer-text">Each dog is unique and may not develop any or all of these conditions.</p>
|
| 333 |
-
<p class="disclaimer-text">Always consult with qualified veterinarians for professional advice.</p>
|
| 334 |
-
</div>
|
| 335 |
-
</div>
|
| 336 |
-
</div>
|
| 337 |
-
|
| 338 |
-
<div class="action-section">
|
| 339 |
-
<a href="https://www.akc.org/dog-breeds/{breed.lower().replace('_', '-')}/"
|
| 340 |
-
target="_blank"
|
| 341 |
-
class="akc-button">
|
| 342 |
-
<span class="icon">🌐</span>
|
| 343 |
-
Learn more about {breed.replace('_', ' ')} on AKC website
|
| 344 |
-
</a>
|
| 345 |
-
</div>
|
| 346 |
-
</div>
|
| 347 |
-
</div>
|
| 348 |
-
"""
|
| 349 |
-
|
| 350 |
-
html_content += "</div>"
|
| 351 |
-
return html_content
|
| 352 |
-
|
| 353 |
-
def get_breed_recommendations(user_prefs: UserPreferences, top_n: int = 10) -> List[Dict]:
|
| 354 |
-
"""基於使用者偏好推薦狗品種,確保正確的分數排序"""
|
| 355 |
-
print("Starting get_breed_recommendations")
|
| 356 |
-
recommendations = []
|
| 357 |
-
seen_breeds = set()
|
| 358 |
-
|
| 359 |
-
try:
|
| 360 |
-
# 獲取所有品種
|
| 361 |
-
conn = sqlite3.connect('animal_detector.db')
|
| 362 |
-
cursor = conn.cursor()
|
| 363 |
-
cursor.execute("SELECT Breed FROM AnimalCatalog")
|
| 364 |
-
all_breeds = cursor.fetchall()
|
| 365 |
-
conn.close()
|
| 366 |
-
|
| 367 |
-
# 收集所有品種的分數
|
| 368 |
-
for breed_tuple in all_breeds:
|
| 369 |
-
breed = breed_tuple[0]
|
| 370 |
-
base_breed = breed.split('(')[0].strip()
|
| 371 |
-
|
| 372 |
-
if base_breed in seen_breeds:
|
| 373 |
-
continue
|
| 374 |
-
seen_breeds.add(base_breed)
|
| 375 |
-
|
| 376 |
-
# 獲取品種資訊
|
| 377 |
-
breed_info = get_dog_description(breed)
|
| 378 |
-
if not isinstance(breed_info, dict):
|
| 379 |
-
continue
|
| 380 |
-
|
| 381 |
-
# 獲取噪音資訊
|
| 382 |
-
noise_info = breed_noise_info.get(breed, {
|
| 383 |
-
"noise_notes": "Noise information not available",
|
| 384 |
-
"noise_level": "Unknown",
|
| 385 |
-
"source": "N/A"
|
| 386 |
-
})
|
| 387 |
-
|
| 388 |
-
# 將噪音資訊整合到品種資訊中
|
| 389 |
-
breed_info['noise_info'] = noise_info
|
| 390 |
-
|
| 391 |
-
# 計算基礎相容性分數
|
| 392 |
-
compatibility_scores = calculate_compatibility_score(breed_info, user_prefs)
|
| 393 |
-
|
| 394 |
-
# 計算品種特定加分
|
| 395 |
-
breed_bonus = 0.0
|
| 396 |
-
|
| 397 |
-
# 壽命加分
|
| 398 |
-
try:
|
| 399 |
-
lifespan = breed_info.get('Lifespan', '10-12 years')
|
| 400 |
-
years = [int(x) for x in lifespan.split('-')[0].split()[0:1]]
|
| 401 |
-
longevity_bonus = min(0.02, (max(years) - 10) * 0.005)
|
| 402 |
-
breed_bonus += longevity_bonus
|
| 403 |
-
except:
|
| 404 |
-
pass
|
| 405 |
-
|
| 406 |
-
# 性格特徵加分
|
| 407 |
-
temperament = breed_info.get('Temperament', '').lower()
|
| 408 |
-
positive_traits = ['friendly', 'gentle', 'affectionate', 'intelligent']
|
| 409 |
-
negative_traits = ['aggressive', 'stubborn', 'dominant']
|
| 410 |
-
|
| 411 |
-
breed_bonus += sum(0.01 for trait in positive_traits if trait in temperament)
|
| 412 |
-
breed_bonus -= sum(0.01 for trait in negative_traits if trait in temperament)
|
| 413 |
-
|
| 414 |
-
# 與孩童相容性加分
|
| 415 |
-
if user_prefs.has_children:
|
| 416 |
-
if breed_info.get('Good with Children') == 'Yes':
|
| 417 |
-
breed_bonus += 0.02
|
| 418 |
-
elif breed_info.get('Good with Children') == 'No':
|
| 419 |
-
breed_bonus -= 0.03
|
| 420 |
-
|
| 421 |
-
# 噪音相關加分
|
| 422 |
-
if user_prefs.noise_tolerance == 'low':
|
| 423 |
-
if noise_info['noise_level'].lower() == 'high':
|
| 424 |
-
breed_bonus -= 0.03
|
| 425 |
-
elif noise_info['noise_level'].lower() == 'low':
|
| 426 |
-
breed_bonus += 0.02
|
| 427 |
-
elif user_prefs.noise_tolerance == 'high':
|
| 428 |
-
if noise_info['noise_level'].lower() == 'high':
|
| 429 |
-
breed_bonus += 0.01
|
| 430 |
-
|
| 431 |
-
# 計算最終分數
|
| 432 |
-
breed_bonus = round(breed_bonus, 4)
|
| 433 |
-
final_score = round(compatibility_scores['overall'] + breed_bonus, 4)
|
| 434 |
-
|
| 435 |
-
recommendations.append({
|
| 436 |
-
'breed': breed,
|
| 437 |
-
'base_score': round(compatibility_scores['overall'], 4),
|
| 438 |
-
'bonus_score': round(breed_bonus, 4),
|
| 439 |
-
'final_score': final_score,
|
| 440 |
-
'scores': compatibility_scores,
|
| 441 |
-
'info': breed_info,
|
| 442 |
-
'noise_info': noise_info # 添加噪音資訊到推薦結果
|
| 443 |
-
})
|
| 444 |
-
# 嚴格按照 final_score 排序
|
| 445 |
-
recommendations.sort(key=lambda x: (round(-x['final_score'], 4), x['breed'] )) # 負號使其降序排列,並確保4位小數
|
| 446 |
-
|
| 447 |
-
# 選擇前N名並確保正確排序
|
| 448 |
-
final_recommendations = []
|
| 449 |
-
last_score = None
|
| 450 |
-
rank = 1
|
| 451 |
-
|
| 452 |
-
for rec in recommendations:
|
| 453 |
-
if len(final_recommendations) >= top_n:
|
| 454 |
-
break
|
| 455 |
-
|
| 456 |
-
current_score = rec['final_score']
|
| 457 |
-
|
| 458 |
-
# 確保分數遞減
|
| 459 |
-
if last_score is not None and current_score > last_score:
|
| 460 |
-
continue
|
| 461 |
-
|
| 462 |
-
# 添加排名資訊
|
| 463 |
-
rec['rank'] = rank
|
| 464 |
-
final_recommendations.append(rec)
|
| 465 |
-
|
| 466 |
-
last_score = current_score
|
| 467 |
-
rank += 1
|
| 468 |
-
|
| 469 |
-
# 驗證最終排序
|
| 470 |
-
for i in range(len(final_recommendations)-1):
|
| 471 |
-
current = final_recommendations[i]
|
| 472 |
-
next_rec = final_recommendations[i+1]
|
| 473 |
-
|
| 474 |
-
if current['final_score'] < next_rec['final_score']:
|
| 475 |
-
print(f"Warning: Sorting error detected!")
|
| 476 |
-
print(f"#{i+1} {current['breed']}: {current['final_score']}")
|
| 477 |
-
print(f"#{i+2} {next_rec['breed']}: {next_rec['final_score']}")
|
| 478 |
-
|
| 479 |
-
# 交換位置
|
| 480 |
-
final_recommendations[i], final_recommendations[i+1] = \
|
| 481 |
-
final_recommendations[i+1], final_recommendations[i]
|
| 482 |
-
|
| 483 |
-
# 打印最終結果以供驗證
|
| 484 |
-
print("\nFinal Rankings:")
|
| 485 |
-
for rec in final_recommendations:
|
| 486 |
-
print(f"#{rec['rank']} {rec['breed']}")
|
| 487 |
-
print(f"Base Score: {rec['base_score']:.4f}")
|
| 488 |
-
print(f"Bonus: {rec['bonus_score']:.4f}")
|
| 489 |
-
print(f"Final Score: {rec['final_score']:.4f}\n")
|
| 490 |
-
|
| 491 |
-
return final_recommendations
|
| 492 |
-
|
| 493 |
-
except Exception as e:
|
| 494 |
-
print(f"Error in get_breed_recommendations: {str(e)}")
|
| 495 |
-
print(f"Traceback: {traceback.format_exc()}")
|
| 496 |
-
return []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|