Spaces:
Running
on
Zero
Running
on
Zero
Delete recommendation_html_format.py
Browse files- recommendation_html_format.py +0 -487
recommendation_html_format.py
DELETED
@@ -1,487 +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 |
-
</h3>
|
248 |
-
<div class="noise-info">
|
249 |
-
<div class="noise-details">
|
250 |
-
<h4 class="section-header">Typical noise characteristics:</h4>
|
251 |
-
<div class="characteristics-list">
|
252 |
-
<div class="list-item">Moderate to high barker</div>
|
253 |
-
<div class="list-item">Alert watch dog</div>
|
254 |
-
<div class="list-item">Attention-seeking barks</div>
|
255 |
-
<div class="list-item">Social vocalizations</div>
|
256 |
-
</div>
|
257 |
-
|
258 |
-
<div class="noise-level-display">
|
259 |
-
<h4 class="section-header">Noise level:</h4>
|
260 |
-
<div class="level-indicator">
|
261 |
-
<span class="level-text">Moderate-High</span>
|
262 |
-
<div class="level-bars">
|
263 |
-
<span class="bar"></span>
|
264 |
-
<span class="bar"></span>
|
265 |
-
<span class="bar"></span>
|
266 |
-
</div>
|
267 |
-
</div>
|
268 |
-
</div>
|
269 |
-
|
270 |
-
<h4 class="section-header">Barking triggers:</h4>
|
271 |
-
<div class="triggers-list">
|
272 |
-
<div class="list-item">Separation anxiety</div>
|
273 |
-
<div class="list-item">Attention needs</div>
|
274 |
-
<div class="list-item">Strange noises</div>
|
275 |
-
<div class="list-item">Excitement</div>
|
276 |
-
</div>
|
277 |
-
</div>
|
278 |
-
<div class="noise-disclaimer">
|
279 |
-
<p class="disclaimer-text source-text">Source: Compiled from various breed behavior resources, 2024</p>
|
280 |
-
<p class="disclaimer-text">Individual dogs may vary in their vocalization patterns.</p>
|
281 |
-
<p class="disclaimer-text">Training can significantly influence barking behavior.</p>
|
282 |
-
<p class="disclaimer-text">Environmental factors may affect noise levels.</p>
|
283 |
-
</div>
|
284 |
-
</div>
|
285 |
-
</div>
|
286 |
-
|
287 |
-
<div class="health-section">
|
288 |
-
<h3 class="section-header">
|
289 |
-
<span class="icon">🏥</span> Health Insights
|
290 |
-
<span class="tooltip">
|
291 |
-
<span class="tooltip-icon">ⓘ</span>
|
292 |
-
<span class="tooltip-text">
|
293 |
-
Health information is compiled from multiple sources including veterinary resources, breed guides, and international canine health databases.
|
294 |
-
Each dog is unique and may vary from these general guidelines.
|
295 |
-
</span>
|
296 |
-
</span>
|
297 |
-
</h3>
|
298 |
-
<div class="health-info">
|
299 |
-
<div class="health-details">
|
300 |
-
<div class="health-block">
|
301 |
-
<h4 class="section-header">Common breed-specific health considerations:</h4>
|
302 |
-
<div class="health-grid">
|
303 |
-
<div class="health-item">Patellar luxation</div>
|
304 |
-
<div class="health-item">Progressive retinal atrophy</div>
|
305 |
-
<div class="health-item">Von Willebrand's disease</div>
|
306 |
-
<div class="health-item">Open fontanel</div>
|
307 |
-
</div>
|
308 |
-
</div>
|
309 |
-
|
310 |
-
<div class="health-block">
|
311 |
-
<h4 class="section-header">Recommended health screenings:</h4>
|
312 |
-
<div class="health-grid">
|
313 |
-
<div class="health-item screening">Patella evaluation</div>
|
314 |
-
<div class="health-item screening">Eye examination</div>
|
315 |
-
<div class="health-item screening">Blood clotting tests</div>
|
316 |
-
<div class="health-item screening">Skull development monitoring</div>
|
317 |
-
</div>
|
318 |
-
</div>
|
319 |
-
</div>
|
320 |
-
<div class="health-disclaimer">
|
321 |
-
<p class="disclaimer-text source-text">Source: Compiled from various veterinary and breed information resources, 2024</p>
|
322 |
-
<p class="disclaimer-text">This information is for reference only and based on breed tendencies.</p>
|
323 |
-
<p class="disclaimer-text">Each dog is unique and may not develop any or all of these conditions.</p>
|
324 |
-
<p class="disclaimer-text">Always consult with qualified veterinarians for professional advice.</p>
|
325 |
-
</div>
|
326 |
-
</div>
|
327 |
-
</div>
|
328 |
-
|
329 |
-
<div class="action-section">
|
330 |
-
<a href="https://www.akc.org/dog-breeds/{breed.lower().replace('_', '-')}/"
|
331 |
-
target="_blank"
|
332 |
-
class="akc-button">
|
333 |
-
<span class="icon">🌐</span>
|
334 |
-
Learn more about {breed.replace('_', ' ')} on AKC website
|
335 |
-
</a>
|
336 |
-
</div>
|
337 |
-
</div>
|
338 |
-
</div>
|
339 |
-
"""
|
340 |
-
|
341 |
-
html_content += "</div>"
|
342 |
-
return html_content
|
343 |
-
|
344 |
-
def get_breed_recommendations(user_prefs: UserPreferences, top_n: int = 10) -> List[Dict]:
|
345 |
-
"""基於使用者偏好推薦狗品種,確保正確的分數排序"""
|
346 |
-
print("Starting get_breed_recommendations")
|
347 |
-
recommendations = []
|
348 |
-
seen_breeds = set()
|
349 |
-
|
350 |
-
try:
|
351 |
-
# 獲取所有品種
|
352 |
-
conn = sqlite3.connect('animal_detector.db')
|
353 |
-
cursor = conn.cursor()
|
354 |
-
cursor.execute("SELECT Breed FROM AnimalCatalog")
|
355 |
-
all_breeds = cursor.fetchall()
|
356 |
-
conn.close()
|
357 |
-
|
358 |
-
# 收集所有品種的分數
|
359 |
-
for breed_tuple in all_breeds:
|
360 |
-
breed = breed_tuple[0]
|
361 |
-
base_breed = breed.split('(')[0].strip()
|
362 |
-
|
363 |
-
if base_breed in seen_breeds:
|
364 |
-
continue
|
365 |
-
seen_breeds.add(base_breed)
|
366 |
-
|
367 |
-
# 獲取品種資訊
|
368 |
-
breed_info = get_dog_description(breed)
|
369 |
-
if not isinstance(breed_info, dict):
|
370 |
-
continue
|
371 |
-
|
372 |
-
# 獲取噪音資訊
|
373 |
-
noise_info = breed_noise_info.get(breed, {
|
374 |
-
"noise_notes": "Noise information not available",
|
375 |
-
"noise_level": "Unknown",
|
376 |
-
"source": "N/A"
|
377 |
-
})
|
378 |
-
|
379 |
-
# 將噪音資訊整合到品種資訊中
|
380 |
-
breed_info['noise_info'] = noise_info
|
381 |
-
|
382 |
-
# 計算基礎相容性分數
|
383 |
-
compatibility_scores = calculate_compatibility_score(breed_info, user_prefs)
|
384 |
-
|
385 |
-
# 計算品種特定加分
|
386 |
-
breed_bonus = 0.0
|
387 |
-
|
388 |
-
# 壽命加分
|
389 |
-
try:
|
390 |
-
lifespan = breed_info.get('Lifespan', '10-12 years')
|
391 |
-
years = [int(x) for x in lifespan.split('-')[0].split()[0:1]]
|
392 |
-
longevity_bonus = min(0.02, (max(years) - 10) * 0.005)
|
393 |
-
breed_bonus += longevity_bonus
|
394 |
-
except:
|
395 |
-
pass
|
396 |
-
|
397 |
-
# 性格特徵加分
|
398 |
-
temperament = breed_info.get('Temperament', '').lower()
|
399 |
-
positive_traits = ['friendly', 'gentle', 'affectionate', 'intelligent']
|
400 |
-
negative_traits = ['aggressive', 'stubborn', 'dominant']
|
401 |
-
|
402 |
-
breed_bonus += sum(0.01 for trait in positive_traits if trait in temperament)
|
403 |
-
breed_bonus -= sum(0.01 for trait in negative_traits if trait in temperament)
|
404 |
-
|
405 |
-
# 與孩童相容性加分
|
406 |
-
if user_prefs.has_children:
|
407 |
-
if breed_info.get('Good with Children') == 'Yes':
|
408 |
-
breed_bonus += 0.02
|
409 |
-
elif breed_info.get('Good with Children') == 'No':
|
410 |
-
breed_bonus -= 0.03
|
411 |
-
|
412 |
-
# 噪音相關加分
|
413 |
-
if user_prefs.noise_tolerance == 'low':
|
414 |
-
if noise_info['noise_level'].lower() == 'high':
|
415 |
-
breed_bonus -= 0.03
|
416 |
-
elif noise_info['noise_level'].lower() == 'low':
|
417 |
-
breed_bonus += 0.02
|
418 |
-
elif user_prefs.noise_tolerance == 'high':
|
419 |
-
if noise_info['noise_level'].lower() == 'high':
|
420 |
-
breed_bonus += 0.01
|
421 |
-
|
422 |
-
# 計算最終分數
|
423 |
-
breed_bonus = round(breed_bonus, 4)
|
424 |
-
final_score = round(compatibility_scores['overall'] + breed_bonus, 4)
|
425 |
-
|
426 |
-
recommendations.append({
|
427 |
-
'breed': breed,
|
428 |
-
'base_score': round(compatibility_scores['overall'], 4),
|
429 |
-
'bonus_score': round(breed_bonus, 4),
|
430 |
-
'final_score': final_score,
|
431 |
-
'scores': compatibility_scores,
|
432 |
-
'info': breed_info,
|
433 |
-
'noise_info': noise_info # 添加噪音資訊到推薦結果
|
434 |
-
})
|
435 |
-
# 嚴格按照 final_score 排序
|
436 |
-
recommendations.sort(key=lambda x: (round(-x['final_score'], 4), x['breed'] )) # 負號使其降序排列,並確保4位小數
|
437 |
-
|
438 |
-
# 選擇前N名並確保正確排序
|
439 |
-
final_recommendations = []
|
440 |
-
last_score = None
|
441 |
-
rank = 1
|
442 |
-
|
443 |
-
for rec in recommendations:
|
444 |
-
if len(final_recommendations) >= top_n:
|
445 |
-
break
|
446 |
-
|
447 |
-
current_score = rec['final_score']
|
448 |
-
|
449 |
-
# 確保分數遞減
|
450 |
-
if last_score is not None and current_score > last_score:
|
451 |
-
continue
|
452 |
-
|
453 |
-
# 添加排名資訊
|
454 |
-
rec['rank'] = rank
|
455 |
-
final_recommendations.append(rec)
|
456 |
-
|
457 |
-
last_score = current_score
|
458 |
-
rank += 1
|
459 |
-
|
460 |
-
# 驗證最終排序
|
461 |
-
for i in range(len(final_recommendations)-1):
|
462 |
-
current = final_recommendations[i]
|
463 |
-
next_rec = final_recommendations[i+1]
|
464 |
-
|
465 |
-
if current['final_score'] < next_rec['final_score']:
|
466 |
-
print(f"Warning: Sorting error detected!")
|
467 |
-
print(f"#{i+1} {current['breed']}: {current['final_score']}")
|
468 |
-
print(f"#{i+2} {next_rec['breed']}: {next_rec['final_score']}")
|
469 |
-
|
470 |
-
# 交換位置
|
471 |
-
final_recommendations[i], final_recommendations[i+1] = \
|
472 |
-
final_recommendations[i+1], final_recommendations[i]
|
473 |
-
|
474 |
-
# 打印最終結果以供驗證
|
475 |
-
print("\nFinal Rankings:")
|
476 |
-
for rec in final_recommendations:
|
477 |
-
print(f"#{rec['rank']} {rec['breed']}")
|
478 |
-
print(f"Base Score: {rec['base_score']:.4f}")
|
479 |
-
print(f"Bonus: {rec['bonus_score']:.4f}")
|
480 |
-
print(f"Final Score: {rec['final_score']:.4f}\n")
|
481 |
-
|
482 |
-
return final_recommendations
|
483 |
-
|
484 |
-
except Exception as e:
|
485 |
-
print(f"Error in get_breed_recommendations: {str(e)}")
|
486 |
-
print(f"Traceback: {traceback.format_exc()}")
|
487 |
-
return []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|