PawMatchAI / breed_recommendation.py
DawnC's picture
Update breed_recommendation.py
b12c2dc
raw
history blame
30.6 kB
import sqlite3
import gradio as gr
from dog_database import get_dog_description, dog_data
from breed_health_info import breed_health_info
from breed_noise_info import breed_noise_info
from scoring_calculation_system import UserPreferences, calculate_compatibility_score
from recommendation_html_format import format_recommendation_html, get_breed_recommendations
from search_history import create_history_tab, create_history_component
# def create_recommendation_tab(UserPreferences, get_breed_recommendations, format_recommendation_html, history_component):
# with gr.TabItem("Breed Recommendation"):
# with gr.Tabs():
# with gr.Tab("Find by Criteria"):
# gr.HTML("""
# <div style='
# text-align: center;
# position: relative;
# padding: 20px 0;
# margin: 15px 0;
# background: linear-gradient(to right, rgba(66, 153, 225, 0.1), rgba(72, 187, 120, 0.1));
# border-radius: 10px;
# '>
# <!-- BETA 標籤 -->
# <div style='
# position: absolute;
# top: 10px;
# right: 20px;
# background: linear-gradient(90deg, #4299e1, #48bb78);
# color: white;
# padding: 4px 12px;
# border-radius: 15px;
# font-size: 0.85em;
# font-weight: 600;
# letter-spacing: 1px;
# box-shadow: 0 2px 4px rgba(0,0,0,0.1);
# '>BETA</div>
# <!-- 主標題 -->
# <p style='
# font-size: 1.2em;
# margin: 0;
# padding: 0 20px;
# line-height: 1.5;
# background: linear-gradient(90deg, #4299e1, #48bb78);
# -webkit-background-clip: text;
# -webkit-text-fill-color: transparent;
# font-weight: 600;
# '>
# Tell us about your lifestyle, and we'll recommend the perfect dog breeds for you!
# </p>
# <!-- 提示訊息 -->
# <div style='
# margin-top: 15px;
# padding: 10px 20px;
# background: linear-gradient(to right, rgba(66, 153, 225, 0.15), rgba(72, 187, 120, 0.15));
# border-radius: 8px;
# font-size: 0.9em;
# color: #2D3748;
# display: flex;
# align-items: center;
# justify-content: center;
# gap: 8px;
# '>
# <span style="font-size: 1.2em;">🔬</span>
# <span style="
# letter-spacing: 0.3px;
# line-height: 1.4;
# "><strong>Beta Feature:</strong> Our matching algorithm is continuously improving. Results are for reference only.</span>
# </div>
# </div>
# """)
# with gr.Row():
# with gr.Column():
# living_space = gr.Radio(
# choices=["apartment", "house_small", "house_large"],
# label="What type of living space do you have?",
# info="Choose your current living situation",
# value="apartment"
# )
# yard_access = gr.Radio(
# choices=["no_yard", "shared_yard", "private_yard"],
# label="Yard Access Type",
# info="Available outdoor space",
# value="no_yard"
# )
# exercise_time = gr.Slider(
# minimum=0,
# maximum=180,
# value=60,
# label="Daily exercise time (minutes)",
# info="Consider walks, play time, and training"
# )
# exercise_type = gr.Radio(
# choices=["light_walks", "moderate_activity", "active_training"],
# label="Exercise Style",
# info="What kind of activities do you prefer?",
# value="moderate_activity"
# )
# grooming_commitment = gr.Radio(
# choices=["low", "medium", "high"],
# label="Grooming commitment level",
# info="Low: monthly, Medium: weekly, High: daily",
# value="medium"
# )
# with gr.Column():
# experience_level = gr.Radio(
# choices=["beginner", "intermediate", "advanced"],
# label="Dog ownership experience",
# info="Be honest - this helps find the right match",
# value="beginner"
# )
# time_availability = gr.Radio(
# choices=["limited", "moderate", "flexible"],
# label="Time Availability",
# info="Time available for dog care daily",
# value="moderate"
# )
# has_children = gr.Checkbox(
# label="Have children at home",
# info="Helps recommend child-friendly breeds"
# )
# children_age = gr.Radio(
# choices=["toddler", "school_age", "teenager"],
# label="Children's Age Group",
# info="Helps match with age-appropriate breeds",
# visible=False # 默認隱藏,只在has_children=True時顯示
# )
# noise_tolerance = gr.Radio(
# choices=["low", "medium", "high"],
# label="Noise tolerance level",
# info="Some breeds are more vocal than others",
# value="medium"
# )
# def update_children_age_visibility(has_children):
# return gr.update(visible=has_children)
# has_children.change(
# fn=update_children_age_visibility,
# inputs=has_children,
# outputs=children_age
# )
# get_recommendations_btn = gr.Button("Find My Perfect Match! 🔍", variant="primary")
# recommendation_output = gr.HTML(
# label="Breed Recommendations",
# visible=True, # 確保可見性
# elem_id="recommendation-output"
# )
# def on_find_match_click(*args):
# try:
# user_prefs = UserPreferences(
# living_space=args[0],
# yard_access=args[1],
# exercise_time=args[2],
# exercise_type=args[3],
# grooming_commitment=args[4],
# experience_level=args[5],
# time_availability=args[6],
# has_children=args[7],
# children_age=args[8] if args[7] else None,
# noise_tolerance=args[9],
# space_for_play=True if args[0] != "apartment" else False,
# other_pets=False,
# climate="moderate",
# health_sensitivity="medium",
# barking_acceptance=args[9]
# )
# recommendations = get_breed_recommendations(user_prefs, top_n=10)
# history_results = [{
# 'breed': rec['breed'],
# 'rank': rec['rank'],
# 'overall_score': rec['final_score'],
# 'base_score': rec['base_score'],
# 'bonus_score': rec['bonus_score'],
# 'scores': rec['scores']
# } for rec in recommendations]
# history_component.save_search(
# user_preferences={
# 'living_space': args[0],
# 'yard_access': args[1],
# 'exercise_time': args[2],
# 'exercise_type': args[3],
# 'grooming_commitment': args[4],
# 'experience_level': args[5],
# 'time_availability': args[6],
# 'has_children': args[7],
# 'children_age': args[8] if args[7] else None,
# 'noise_tolerance': args[9],
# 'search_type': 'Criteria'
# },
# results=history_results
# )
# return format_recommendation_html(recommendations, is_description_search=False)
# except Exception as e:
# print(f"Error in find match: {str(e)}")
# import traceback
# print(traceback.format_exc())
# return "Error getting recommendations"
# get_recommendations_btn.click(
# fn=on_find_match_click,
# inputs=[
# living_space,
# yard_access,
# exercise_time,
# exercise_type,
# grooming_commitment,
# experience_level,
# time_availability,
# has_children,
# children_age,
# noise_tolerance
# ],
# outputs=recommendation_output
# )
# return {
# 'living_space': living_space,
# 'exercise_time': exercise_time,
# 'grooming_commitment': grooming_commitment,
# 'experience_level': experience_level,
# 'has_children': has_children,
# 'noise_tolerance': noise_tolerance,
# 'get_recommendations_btn': get_recommendations_btn,
# 'recommendation_output': recommendation_output,
# }
def create_recommendation_tab(UserPreferences, get_breed_recommendations, format_recommendation_html, history_component):
"""
創建狗品種推薦頁面的主要函數
包含用戶輸入界面、推薦結果顯示和 loading 狀態
"""
with gr.TabItem("Breed Recommendation"):
with gr.Tabs():
with gr.Tab("Find by Criteria"):
# 頁面頂部的介紹性內容
gr.HTML("""
<div style='
text-align: center;
position: relative;
padding: 20px 0;
margin: 15px 0;
background: linear-gradient(to right, rgba(66, 153, 225, 0.1), rgba(72, 187, 120, 0.1));
border-radius: 10px;
'>
<!-- BETA 標籤 -->
<div style='
position: absolute;
top: 10px;
right: 20px;
background: linear-gradient(90deg, #4299e1, #48bb78);
color: white;
padding: 4px 12px;
border-radius: 15px;
font-size: 0.85em;
font-weight: 600;
letter-spacing: 1px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
'>BETA</div>
<!-- 主標題 -->
<p style='
font-size: 1.2em;
margin: 0;
padding: 0 20px;
line-height: 1.5;
background: linear-gradient(90deg, #4299e1, #48bb78);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 600;
'>
Tell us about your lifestyle, and we'll recommend the perfect dog breeds for you!
</p>
<!-- 提示訊息 -->
<div style='
margin-top: 15px;
padding: 10px 20px;
background: linear-gradient(to right, rgba(66, 153, 225, 0.15), rgba(72, 187, 120, 0.15));
border-radius: 8px;
font-size: 0.9em;
color: #2D3748;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
'>
<span style="font-size: 1.2em;">🔬</span>
<span style="
letter-spacing: 0.3px;
line-height: 1.4;
"><strong>Beta Feature:</strong> Our matching algorithm is continuously improving. Results are for reference only.</span>
</div>
</div>
""")
# 添加 Loading 狀態顯示
loading_html = gr.HTML("""
<div id="loading-status" style="
text-align: center;
margin: 30px 0;
opacity: 0;
transition: opacity 0.3s ease-in-out;
">
<div style="
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 15px;
padding: 25px;
background: linear-gradient(to right, rgba(66, 153, 225, 0.1), rgba(72, 187, 120, 0.1));
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
">
<!-- 可愛的狗狗圖示 -->
<div style="font-size: 40px;">🐕</div>
<!-- 載入中主要訊息 -->
<div style="
color: #2D3748;
font-size: 1.2em;
font-weight: 500;
margin-bottom: 5px;
">Sniffing out your perfect match...</div>
<!-- 有趣的載入訊息 -->
<div id="loading-message" style="
color: #4A5568;
font-size: 0.95em;
font-style: italic;
">Checking all the good boys and girls...</div>
<!-- 載入動畫 -->
<div style="
display: flex;
gap: 8px;
margin-top: 10px;
">
<div class="paw" style="
width: 12px;
height: 12px;
background: #4299e1;
border-radius: 50%;
animation: pawAnimation 1s infinite;
animation-delay: 0s;
"></div>
<div class="paw" style="
width: 12px;
height: 12px;
background: #4299e1;
border-radius: 50%;
animation: pawAnimation 1s infinite;
animation-delay: 0.2s;
"></div>
<div class="paw" style="
width: 12px;
height: 12px;
background: #4299e1;
border-radius: 50%;
animation: pawAnimation 1s infinite;
animation-delay: 0.4s;
"></div>
</div>
</div>
</div>
<style>
@keyframes pawAnimation {
0% { transform: translateY(0); }
50% { transform: translateY(-10px); }
100% { transform: translateY(0); }
}
#loading-status {
display: none;
}
#loading-status.visible {
display: block;
opacity: 1;
}
</style>
<script>
// 有趣的載入訊息列表
const messages = [
"Checking all the good boys and girls...",
"Fetching the perfect matches...",
"Calculating belly rub compatibility...",
"Analyzing tail wag frequencies...",
"Measuring treat enthusiasm levels...",
"Evaluating walkies requirements...",
"Consulting our canine experts...",
"Counting tennis balls...",
"Reviewing park visit preferences...",
"Assessing cuddle potential..."
];
// 定期更新載入訊息
function updateLoadingMessage() {
const messageElement = document.getElementById('loading-message');
if (messageElement) {
const randomMessage = messages[Math.floor(Math.random() * messages.length)];
messageElement.textContent = randomMessage;
}
}
setInterval(updateLoadingMessage, 2000);
</script>
""", visible=False)
# 用戶輸入區域
with gr.Row():
with gr.Column():
living_space = gr.Radio(
choices=["apartment", "house_small", "house_large"],
label="What type of living space do you have?",
info="Choose your current living situation",
value="apartment"
)
yard_access = gr.Radio(
choices=["no_yard", "shared_yard", "private_yard"],
label="Yard Access Type",
info="Available outdoor space",
value="no_yard"
)
exercise_time = gr.Slider(
minimum=0,
maximum=180,
value=60,
label="Daily exercise time (minutes)",
info="Consider walks, play time, and training"
)
exercise_type = gr.Radio(
choices=["light_walks", "moderate_activity", "active_training"],
label="Exercise Style",
info="What kind of activities do you prefer?",
value="moderate_activity"
)
grooming_commitment = gr.Radio(
choices=["low", "medium", "high"],
label="Grooming commitment level",
info="Low: monthly, Medium: weekly, High: daily",
value="medium"
)
with gr.Column():
experience_level = gr.Radio(
choices=["beginner", "intermediate", "advanced"],
label="Dog ownership experience",
info="Be honest - this helps find the right match",
value="beginner"
)
time_availability = gr.Radio(
choices=["limited", "moderate", "flexible"],
label="Time Availability",
info="Time available for dog care daily",
value="moderate"
)
has_children = gr.Checkbox(
label="Have children at home",
info="Helps recommend child-friendly breeds"
)
children_age = gr.Radio(
choices=["toddler", "school_age", "teenager"],
label="Children's Age Group",
info="Helps match with age-appropriate breeds",
visible=False
)
noise_tolerance = gr.Radio(
choices=["low", "medium", "high"],
label="Noise tolerance level",
info="Some breeds are more vocal than others",
value="medium"
)
# 控制 children_age 顯示邏輯
def update_children_age_visibility(has_children):
return gr.update(visible=has_children)
has_children.change(
fn=update_children_age_visibility,
inputs=has_children,
outputs=children_age
)
# 推薦按鈕
get_recommendations_btn = gr.Button(
"Find My Perfect Match! 🔍",
variant="primary"
)
# 推薦結果顯示區域
recommendation_output = gr.HTML(
label="Breed Recommendations",
visible=True,
elem_id="recommendation-output"
)
def on_find_match_click(*args):
try:
# 顯示中間狀態
intermediate_message = gr.HTML("""
<div style="
text-align: center;
padding: 20px;
background: linear-gradient(to right, rgba(66, 153, 225, 0.1), rgba(72, 187, 120, 0.1));
border-radius: 10px;
margin: 20px 0;
">
<div style="font-size: 40px; margin-bottom: 10px;">🐕</div>
<p style="
color: #2D3748;
font-size: 1.2em;
margin: 0;
">
Finding your perfect match...
<br>
<span style="font-size: 0.9em; color: #4A5568;">
This may take a few moments
</span>
</p>
</div>
""")
# 創建用戶偏好對象
user_prefs = UserPreferences(
living_space=args[0],
yard_access=args[1],
exercise_time=args[2],
exercise_type=args[3],
grooming_commitment=args[4],
experience_level=args[5],
time_availability=args[6],
has_children=args[7],
children_age=args[8] if args[7] else None,
noise_tolerance=args[9],
space_for_play=True if args[0] != "apartment" else False,
other_pets=False,
climate="moderate",
health_sensitivity="medium",
barking_acceptance=args[9]
)
# 獲取推薦結果
recommendations = get_breed_recommendations(user_prefs, top_n=10)
# 儲存搜尋歷史
history_results = [{
'breed': rec['breed'],
'rank': rec['rank'],
'overall_score': rec['final_score'],
'base_score': rec['base_score'],
'bonus_score': rec['bonus_score'],
'scores': rec['scores']
} for rec in recommendations]
history_component.save_search(
user_preferences={
'living_space': args[0],
'yard_access': args[1],
'exercise_time': args[2],
'exercise_type': args[3],
'grooming_commitment': args[4],
'experience_level': args[5],
'time_availability': args[6],
'has_children': args[7],
'children_age': args[8] if args[7] else None,
'noise_tolerance': args[9],
'search_type': 'Criteria'
},
results=history_results
)
# 返回最終結果
return format_recommendation_html(recommendations, is_description_search=False)
except Exception as e:
print(f"Error in find match: {str(e)}")
import traceback
print(traceback.format_exc())
return gr.HTML("""
<div style="
text-align: center;
padding: 20px;
background: rgba(255, 0, 0, 0.1);
border-radius: 10px;
margin: 20px 0;
">
<p style="
color: #d32f2f;
font-size: 1.1em;
margin: 0;
">
🐾 Oops! Something went wrong while finding your perfect match.
<br>Please try again!
</p>
</div>
""")
# 設置按鈕點擊事件
get_recommendations_btn.click(
fn=on_find_match_click,
inputs=[
living_space,
yard_access,
exercise_time,
exercise_type,
grooming_commitment,
experience_level,
time_availability,
has_children,
children_age,
noise_tolerance
],
outputs=recommendation_output
)
# 返回頁面組件
return {
'living_space': living_space,
'exercise_time': exercise_time,
'grooming_commitment': grooming_commitment,
'experience_level': experience_level,
'has_children': has_children,
'noise_tolerance': noise_tolerance,
'get_recommendations_btn': get_recommendations_btn,
'recommendation_output': recommendation_output,
}