import gradio as gr
from src.search.service import SearchService
from src.utils.constants import Constants
from src.utils.logging import get_logger
class UIComponents:
"""
Class to handle the Gradio UI components for the application.
"""
def __init__(self):
"""
Initialize the UI components.
"""
self.logger = get_logger()
self.logger.info("Initializing UI components")
try:
self.search_service = SearchService()
self.logger.info("UI components initialized successfully")
except Exception as e:
self.logger.error(f"Failed to initialize UI components: {str(e)}")
raise
def create_customer_tab(self):
"""
Create the customer search tab.
Returns:
gradio.Tab: The customer search tab.
"""
with gr.Tab(Constants.UI_CUSTOMER_TAB) as tab:
gr.Markdown(f"## {Constants.UI_CUSTOMER_DESCRIPTION}")
with gr.Row():
text_input = gr.Textbox(
label=Constants.UI_TEXT_INPUT_LABEL,
placeholder="Ex: Une grille décorative pour mon jardin avec des motifs floraux",
lines=3
)
with gr.Row():
image_input = gr.Image(
label=Constants.UI_IMAGE_INPUT_LABEL,
type="pil"
)
with gr.Row():
search_button = gr.Button(Constants.UI_SEARCH_BUTTON, variant="primary")
with gr.Row(visible=False) as loading_indicator:
gr.Markdown(f"### {Constants.UI_LOADING_TEXT}")
results_container = gr.HTML(visible=False)
def search(text, image):
self.logger.info("Customer UI search initiated")
self.logger.debug(f"Search parameters - Text: '{text}', Image provided: {image is not None}")
if not text and image is None:
self.logger.info("No search parameters provided, returning empty results")
return None, gr.update(visible=False), gr.update(visible=False)
try:
self.logger.info("Calling customer search service")
results = self.search_service.customer_search(text, image)
if not results:
self.logger.info("No results found for customer search")
return None, gr.update(visible=False), gr.HTML(
f"
{Constants.UI_NO_RESULTS}
", visible=True)
# Prepare gallery images
self.logger.debug(f"Preparing gallery with {len(results)} images")
gallery_images = []
for result in results:
gallery_images.append((result["thumbnail"], result["title"]))
# Prepare HTML for results
self.logger.debug("Creating HTML for search results")
html = self._create_results_html(results)
self.logger.info(f"Customer search completed successfully with {len(results)} results")
return gr.HTML(html, visible=True)
except Exception as e:
self.logger.error(f"Error during customer UI search: {str(e)}")
# Return a user-friendly error message
error_html = f"Une erreur s'est produite lors de la recherche. Veuillez réessayer.
"
return None, gr.update(visible=False), gr.HTML(error_html, visible=True)
search_button.click(
fn=search,
inputs=[text_input, image_input],
outputs=[results_container],
# js="""
# function(text, image) {
# // Disable search button and show loading indicator
# document.querySelector("button[variant='primary']").disabled = true;
# return [text, image];
# }
# """,
preprocess=True
).then(
# js="""
# function() {
# // Re-enable search button and hide loading indicator
# document.querySelector("button[variant='primary']").disabled = false;
# return [];
# }
# """
)
# Show/hide loading indicator
search_button.click(
fn=lambda: gr.update(visible=True),
inputs=None,
outputs=loading_indicator
).then(
fn=lambda: gr.update(visible=False),
inputs=None,
outputs=loading_indicator
)
return tab
def create_staff_tab(self):
"""
Create the staff search tab.
Returns:
gradio.Tab: The staff search tab.
"""
with gr.Tab(Constants.UI_STAFF_TAB) as tab:
gr.Markdown(f"## {Constants.UI_STAFF_DESCRIPTION}")
with gr.Row():
image_input = gr.Image(
label=Constants.UI_IMAGE_INPUT_LABEL,
type="pil"
)
with gr.Row():
search_button = gr.Button(Constants.UI_SEARCH_BUTTON, variant="primary")
with gr.Row(visible=False) as loading_indicator:
gr.Markdown(f"### {Constants.UI_LOADING_TEXT}")
result_container = gr.HTML(visible=False)
def search(image):
self.logger.info("Staff UI search initiated")
self.logger.debug(f"Search parameters - Image provided: {image is not None}")
if image is None:
self.logger.info("No image provided for staff search, returning empty results")
return gr.update(visible=False)
try:
self.logger.info("Calling staff search service")
result = self.search_service.staff_search(image)
if not result:
self.logger.info("No results found for staff search")
return gr.HTML(f"{Constants.UI_NO_RESULTS}
", visible=True)
# Prepare HTML for result
self.logger.debug("Creating HTML for search result")
html = self._create_results_html([result])
self.logger.info("Staff search completed successfully with a match")
return gr.HTML(html, visible=True)
except Exception as e:
self.logger.error(f"Error during staff UI search: {str(e)}")
# Return a user-friendly error message
error_html = f"Une erreur s'est produite lors de la recherche. Veuillez réessayer.
"
return gr.HTML(error_html, visible=True)
search_button.click(
fn=search,
inputs=[image_input],
outputs=[result_container],
# js="""
# function(image) {
# // Disable search button and show loading indicator
# document.querySelector("button[variant='primary']").disabled = true;
# return [image];
# }
# """,
preprocess=True
).then(
# js="""
# function() {
# // Re-enable search button and hide loading indicator
# document.querySelector("button[variant='primary']").disabled = false;
# return [];
# }
# """
)
# Show/hide loading indicator
search_button.click(
fn=lambda: gr.update(visible=True),
inputs=None,
outputs=loading_indicator
).then(
fn=lambda: gr.update(visible=False),
inputs=None,
outputs=loading_indicator
)
return tab
def _create_results_html(self, results):
"""
Create HTML for displaying search results.
Args:
results (list): List of search results.
Returns:
str: HTML string for displaying results.
"""
self.logger.debug(f"Creating HTML for {len(results)} results")
try:
html = ""
for i, result in enumerate(results):
self.logger.debug(f"Adding result {i + 1}: {result['title']}")
html += f"""
"""
html += "
"
self.logger.debug("HTML for results created successfully")
except Exception as e:
self.logger.error(f"Error creating HTML for results: {str(e)}")
# Create a simple fallback HTML in case of error
html = "Résultats disponibles mais impossible de les afficher correctement.
"
# Add CSS for styling
html += """
"""
return html
def create_interface(self):
"""
Create the complete Gradio interface.
Returns:
gradio.Blocks: The Gradio interface.
"""
with gr.Blocks(title=Constants.UI_TITLE, theme=gr.themes.Soft()) as interface:
gr.Markdown(f"# {Constants.UI_TITLE}")
with gr.Tabs():
self.create_customer_tab()
self.create_staff_tab()
# Add CSS for responsive design
gr.HTML("""
""")
return interface
def close(self):
"""
Close connections and resources.
"""
self.logger.info("Closing UI components resources")
try:
self.search_service.close()
self.logger.info("UI components resources closed successfully")
except Exception as e:
self.logger.error(f"Error closing UI components resources: {str(e)}")
# We don't re-raise the exception here to ensure cleanup continues even if there's an error