Spaces:
Running
on
Zero
Running
on
Zero
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" | |
) |