PawMatchAI / search_history.py
DawnC's picture
Update search_history.py
f036996
raw
history blame
13.7 kB
import gradio as gr
import traceback
from typing import Optional, Dict, List
from history_manager import UserHistoryManager
class SearchHistoryComponent :
def __init__ ( self ):
"""ๅˆๅง‹ๅŒ–ๆœๅฐ‹ๆญทๅฒ็ต„ไปถ"""
self.history_manager = UserHistoryManager()
def format_history_html ( self, history_data: Optional [ List [ Dict ]] = None ) -> str :
try :
if history_data is None :
history_data = self.history_manager.get_history()
if not history_data:
return """
<div style='text-align: center; padding: 40px 20px;'>
<p>No search history yet. Try making some breed recommendations!</p>
</div>
"""
html = "<div class='history-container'>"
# ๅฐๆญทๅฒ่จ˜้Œ„้€ฒ่กŒๅ่ฝ‰๏ผŒๆœ€ๆ–ฐ็š„้กฏ็คบๅœจๅ‰้ข
for entry in reversed (history_data):
timestamp = entry.get( 'timestamp' , 'Unknown time' )
search_type = entry.get( 'search_type' , 'criteria' )
results = entry.get( 'results' , [])
# ้กฏ็คบๆ™‚้–“ๆˆณ่จ˜ๅ’Œๆœๅฐ‹้กžๅž‹
html += f"""
<div class="history-entry">
<div class="history-header" style="border-left: 4px solid #4299e1; padding-left: 10px;">
<span class="timestamp">๐Ÿ•’ {timestamp} </span>
<span class="search-type" style="color: #4299e1; font-weight: bold; margin-left: 10px;">
Search History
</span>
</div>
"""
# ้กฏ็คบๆœๅฐ‹ๅƒๆ•ธ
if search_type == "criteria" :
prefs = entry.get( 'preferences' , {})
html += f"""
<div class="params-list" style="background: #f8fafc; padding: 16px; border-radius: 8px; margin-bottom: 16px;">
<h4 style="margin-bottom: 12px;">Search Parameters:</h4>
<ul style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
<li>Living Space: {prefs.get( 'living_space' , 'N/A' )} </li>
<li>Exercise Time: {prefs.get( 'exercise_time' , 'N/A' )} minutes</li>
<li>Grooming: {prefs.get( 'grooming_commitment' , 'N/A' )} </li>
<li>Size Preference: {prefs.get( 'size_preference' , 'N/A' )} </li>
<li>Experience: {prefs.get( 'experience_level' , 'N/A' )} </li>
<li>Children at Home: { "Yes" if prefs.get( 'has_children' ) else "No" } </li>
<li>Noise Tolerance: {prefs.get( 'noise_tolerance' , 'N/A' )} </li>
</ul>
</div>
"""
# ้—œ้ตไฟฎๆ”น๏ผš็ขบไฟ็ตๆžœ้ƒจๅˆ†ๅง‹็ต‚้กฏ็คบ
if results: # ๅชๆœ‰ๅœจๆœ‰็ตๆžœๆ™‚ๆ‰้กฏ็คบ็ตๆžœๅ€ๅŸŸ
html += """
<div class="results-list" style="margin-top: 16px;">
<h4 style="margin-bottom: 12px;">Top 15 Breed Matches:</h4>
<div class="breed-list">
"""
# ้กฏ็คบๆฏๅ€‹ๆŽจ่–ฆ็ตๆžœ
for i, result in enumerate (results[: 15 ], 1 ):
breed = result.get( 'breed' , 'Unknown breed' )
score = result.get( 'overall_score' , 0 ) # ๆ”น็”จoverall_score
if isinstance (score, ( int , float )): # ็ขบไฟๅˆ†ๆ•ธๆ˜ฏๆ•ธๅญ—
score = float (score) * 100 # ่ฝ‰ๆ›็‚บ็™พๅˆ†ๆฏ”
html += f"""
<div class="breed-item" style="margin-bottom: 8px;">
<div class="breed-info" style="display: flex; align-items: center; justify-content: space-between; padding: 8px; background: #f8fafc; border-radius: 6px;">
<span class="breed-rank" style="background: linear-gradient(135deg, #4299e1, #48bb78); color: white; padding: 4px 10px; border-radius: 6px; font-weight: 600; min-width: 40px; text-align: center;">#{i}</span>
<span class="breed-name" style="font-weight: 500; color: #2D3748; margin: 0 12px;">{breed.replace('_', ' ')}</span>
<span class="breed-score" style="background: #F0FFF4; color: #48BB78; padding: 4px 8px; border-radius: 4px; font-weight: 600;">{score:.1f}%</span>
</div>
</div>
"""
html += """
</div>
</div>
"""
html += "</div>" # ้—œ้–‰history-entry div
html += "</div>" # ้—œ้–‰history-container div
return html
except Exception as e:
print ( f"Error formatting history: { str (e)} " )
print (traceback.format_exc())
return f"""
<div style='text-align: center; padding: 20px; color: #dc2626;'>
Error formatting history. Please try refreshing the page.
<br>Error details: { str (e)}
</div>
"""
def clear_history ( self ) -> str :
"""ๆธ…้™คๆ‰€ๆœ‰ๆœๅฐ‹็ด€้Œ„"""
try :
success = self.history_manager.clear_all_history()
print ( f"Clear history result: {success} " )
return self.format_history_html()
except Exception as e:
print ( f"Error in clear_history: { str (e)} " )
print (traceback.format_exc())
return "Error clearing history"
def refresh_history ( self ) -> str :
"""ๅˆทๆ–ฐๆญทๅฒ่จ˜้Œ„้กฏ็คบ"""
try :
return self.format_history_html()
except Exception as e:
print ( f"Error in refresh_history: { str (e)} " )
return "Error refreshing history"
def save_search ( self, user_preferences: Optional [ dict ] = None ,
results: list = None ,
search_type: str = "criteria" ,
description: str = None ) -> bool :
"""
ๅ„ฒๅญ˜ๆœๅฐ‹็ตๆžœๅˆฐๆญทๅฒ่จ˜้Œ„
้€™ๅ€‹ๆ–นๆณ•่ฒ ่ฒฌ่™•็†ๆœๅฐ‹็ตๆžœ็š„ไฟๅญ˜๏ผŒไธฆ็ขบไฟๅชไฟๅญ˜ๅ‰15ๅ€‹ๆœ€็›ธ้—œ็š„ๆŽจ่–ฆ็ตๆžœใ€‚
ๅœจๅ„ฒๅญ˜ไน‹ๅ‰๏ผŒๆœƒ่™•็†็ตๆžœ่ณ‡ๆ–™็ขบไฟๆ ผๅผๆญฃ็ขบไธ”ๅŒ…ๅซๆ‰€้œ€็š„ๆ‰€ๆœ‰่ณ‡่จŠใ€‚
Args:
user_preferences: ไฝฟ็”จ่€…ๅๅฅฝ่จญๅฎš(ๅƒ…็”จๆ–ผcriteriaๆœๅฐ‹)
ๅŒ…ๅซๆ‰€ๆœ‰ๆœๅฐ‹ๆขไปถๅฆ‚ๅฑ…ไฝ็ฉบ้–“ใ€้‹ๅ‹•ๆ™‚้–“็ญ‰
results: ๆŽจ่–ฆ็ตๆžœๅˆ—่กจ
ๅŒ…ๅซๆ‰€ๆœ‰ๆŽจ่–ฆ็š„็‹—ๅ“็จฎๅŠๅ…ถ่ฉ•ๅˆ†
search_type: ๆœๅฐ‹้กžๅž‹("criteria" ๆˆ–"description")
็”จๆ–ผๆจ™่ญ˜ๆœๅฐ‹ๆ–นๅผ
description: ็”จๆˆถ่ผธๅ…ฅ็š„ๆ่ฟฐ(ๅƒ…็”จๆ–ผdescriptionๆœๅฐ‹)
็”จๆ–ผ่‡ช็„ถ่ชž่จ€ๆœๅฐ‹ๆ™‚็š„ๆ่ฟฐๆ–‡ๆœฌ
Returns:
bool: ่กจ็คบไฟๅญ˜ๆ˜ฏๅฆๆˆๅŠŸ
"""
# ้ฆ–ๅ…ˆ็ขบไฟ็ตๆžœไธ็‚บ็ฉบไธ”็‚บๅˆ—่กจ
if results and isinstance (results, list ):
# ๅชๅ–ๅ‰15ๅ€‹็ตๆžœ
processed_results = []
for result in results[: 15 ]: # ้™ๅˆถ็‚บๅ‰15ๅ€‹็ตๆžœ
# ็ขบไฟๆฏๅ€‹็ตๆžœ้ƒฝๅŒ…ๅซๅฟ…่ฆ็š„ไฟกๆฏ
if isinstance (result, dict ):
processed_result = {
'breed' : result.get( 'breed' , 'Unknown' ),
'overall_score' : result.get( 'overall_score' , result.get( 'final_score' , 0 )),
'rank' : result.get( 'rank' , 0 ),
'base_score' : result.get( 'base_score' , 0 ),
'bonus_score' : result.get( 'bonus_score' , 0 ),
'scores' : result.get( 'scores' , {})
}
processed_results.append(processed_result)
else :
# ๅฆ‚ๆžœๆฒ’ๆœ‰็ตๆžœ๏ผŒๅ‰ตๅปบ็ฉบๅˆ—่กจ
processed_results = []
# ่ชฟ็”จhistory_manager ็š„save_history ๆ–นๆณ•ไฟๅญ˜่™•็†้Ž็š„็ตๆžœ
return self.history_manager.save_history(
user_preferences=user_preferences,
results=processed_results, # ไฝฟ็”จ่™•็†้Ž็š„็ตๆžœ
search_type= 'criteria'
)
def create_history_component ():
"""ๅชๅปบ็ซ‹ๅฏฆไพ‹"""
return SearchHistoryComponent()
def create_history_tab ( history_component: SearchHistoryComponent ):
"""ๅ‰ตๅปบๆญทๅฒ็ด€้Œ„็š„้ ้ข
Args:
history_component:
"""
with gr.TabItem( "Recommendation Search History" ):
gr.HTML( """
<style>
.custom-btn {
padding: 10px 20px !important;
border-radius: 8px !important;
border: none !important;
font-weight: 500 !important;
transition: all 0.2s ease !important;
color: white !important;
font-size: 0.95em !important;
cursor: pointer !important;
width: 100% !important;
min-height: 42px !important;
}
/* Clear History ็š„ๆŒ‰้ˆ•*/
.clear-btn {
background: linear-gradient(135deg, #FF6B6B 0%, #FF9B8B 100%) !important;
box-shadow: 0 2px 4px rgba(255, 107, 107, 0.15) !important;
}
.clear-btn:hover {
background: linear-gradient(135deg, #FF5252, #FF8B7B) !important;
transform: translateY(-1px);
}
.clear-btn:active {
transform: translateY(1px) scale(0.98);
background: linear-gradient(135deg, #FF4242, #FF7B6B) !important;
}
/* Refresh ็š„ๆŒ‰้ˆ•*/
.refresh-btn {
background: linear-gradient(135deg, #4FB5E5 0%, #32CCBC 100%) !important;
box-shadow: 0 2px 4px rgba(79, 181, 229, 0.15) !important;
}
.refresh-btn:hover {
background: linear-gradient(135deg, #45A5D5, #2DBCAC) !important;
transform: translateY(-1px);
}
.refresh-btn:active {
transform: translateY(1px) scale(0.98);
background: linear-gradient(135deg, #3B95C5, #28AC9C) !important;
}
/* ๆ‡ธๆตฎ็š„ๆ•ˆๆžœ*/
.custom-btn:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1) !important;
}
/* ้ปžๆ“Š็š„ๆ•ˆๆžœ*/
.custom-btn:active {
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
}
</style>
<div style='text-align: center; padding: 20px;'>
<h3 style='
color: #2D3748;
margin-bottom: 10px;
font-size: 1.5em;
background: linear-gradient(90deg, #4299e1, #48bb78);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 600;
'>Search History</h3>
<div style='
text-align: center;
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;
'>
<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;
'>
View your previous breed recommendations and search preferences
</p>
</div>
</div>
""" )
with gr.Row():
with gr.Column(scale= 4 ):
history_display = gr.HTML()
with gr.Row(equal_height= True ):
with gr.Column(scale= 1 ):
clear_history_btn = gr.Button(
"๐Ÿ—‘๏ธ Clear History" ,
variant= "primary" ,
elem_classes= "custom-btn clear-btn"
)
with gr.Column(scale= 1 ):
refresh_btn = gr.Button(
"๐Ÿ”„ Refresh" ,
variant= "primary" ,
elem_classes= "custom-btn refresh-btn"
)
history_display.value = history_component.format_history_html()
clear_history_btn.click(
fn=history_component.clear_history,
outputs=[history_display],
api_name= "clear_history"
)
refresh_btn.click(
fn=history_component.refresh_history,
outputs=[history_display],
api_name= "refresh_history"
)