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 """

No search history yet. Try making some breed recommendations!

""" html = "
" # 最新的顯示在前面 for entry in reversed(history_data): timestamp = entry.get('timestamp', 'Unknown time') search_type = entry.get('search_type', 'criteria') results = entry.get('results', []) # 標籤樣式 if search_type == "description": border_color = "#4299e1" tag_color = "#4299e1" tag_bg = "rgba(66, 153, 225, 0.1)" tag_text = "Description Search" icon = "🤖" else: border_color = "#48bb78" tag_color = "#48bb78" tag_bg = "rgba(72, 187, 120, 0.1)" tag_text = "Criteria Search" icon = "🔍" # header html += f"""
🕒 {timestamp} {icon} {tag_text}
""" # 參數/描述 if search_type == "criteria": prefs = entry.get('preferences', {}) html += f"""

Search Parameters:

  • Living Space: {prefs.get('living_space', 'N/A')}
  • Exercise Time: {prefs.get('exercise_time', 'N/A')} minutes
  • Grooming: {prefs.get('grooming_commitment', 'N/A')}
  • Size Preference: {prefs.get('size_preference', 'N/A')}
  • Experience: {prefs.get('experience_level', 'N/A')}
  • Children at Home: {"Yes" if prefs.get('has_children') else "No"}
  • Noise Tolerance: {prefs.get('noise_tolerance', 'N/A')}
""" elif search_type == "description": description = entry.get('user_description', '') html += f"""

User Description:

"{description}"
""" # 結果區 if results: html += """

Top 15 Breed Matches:

""" for i, result in enumerate(results[:15], 1): breed = result.get('breed', 'Unknown breed') # ★ 分數回退順序:final_score → overall_score → semantic_score score_val = ( result.get('final_score', None) if result.get('final_score', None) not in [None, ""] else result.get('overall_score', None) ) if score_val in [None, ""]: score_val = result.get('semantic_score', 0) try: score_pct = float(score_val) * 100.0 except Exception: score_pct = 0.0 html += f"""
#{i} {breed.replace('_', ' ')} {score_pct:.1f}%
""" html += """
""" html += "
" # 關閉 .history-entry html += "
" # 關閉 .history-container return html except Exception as e: print(f"Error formatting history: {str(e)}") print(traceback.format_exc()) return f"""
Error formatting history. Please try refreshing the page.
Error details: {str(e)}
""" 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: """參數原樣透傳給 history_manager""" return self.history_manager.save_history( user_preferences=user_preferences, results=results, search_type=search_type, description=description, user_description=description ) def create_history_component (): """只建立實例""" return SearchHistoryComponent() def create_history_tab ( history_component: SearchHistoryComponent ): """創建歷史紀錄的頁面 Args: history_component: """ with gr.TabItem( "Recommendation Search History" ): gr.HTML( """

Search History

View your previous breed recommendations and search preferences

""" ) with gr.Row(): with gr.Column(scale= 4 ): history_display = gr.HTML(value=history_component.format_history_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" ) 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" )