""" Gradio UI Builder for Homeopathic Analyzer """ import gradio as gr from config import APP_NAME, APP_DESCRIPTION, THEME_CONFIG from analyzer import analyzer from database import search_remedies, get_remedy_details class UIBuilder: def __init__(self): self.css = self._get_css() def _get_css(self): """Return custom CSS""" return """ .gradio-container { max-width: 1400px !important; margin: 0 auto !important; font-family: 'Inter', -apple-system, sans-serif !important; } .main-header { background: linear-gradient(135deg, #1e40af 0%, #1e3a8a 100%); padding: 32px; color: white; border-radius: 0 0 20px 20px; margin: -20px -20px 30px -20px; } .tab-nav { display: flex; gap: 2px; margin-bottom: 30px; background: #f3f4f6; padding: 4px; border-radius: 12px; } .tab-button { flex: 1; padding: 16px; border: none; background: transparent; font-weight: 500; color: #6b7280; border-radius: 8px; cursor: pointer; transition: all 0.2s ease; } .tab-button:hover { background: #e5e7eb; } .tab-button.active { background: white; color: #1e40af; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .analysis-card { background: white; border-radius: 12px; padding: 28px; margin-bottom: 24px; border: 1px solid #e5e7eb; box-shadow: 0 2px 8px rgba(0,0,0,0.05); } .primary-btn { background: linear-gradient(135deg, #1e40af 0%, #1e3a8a 100%); color: white; border: none; padding: 16px 32px; border-radius: 10px; font-weight: 600; font-size: 16px; width: 100%; margin-top: 20px; transition: all 0.3s ease; } .primary-btn:hover { transform: translateY(-2px); box-shadow: 0 8px 16px rgba(30, 64, 175, 0.2); } .results-container { margin-top: 30px; animation: fadeIn 0.5s ease; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .medicine-card { background: white; border-radius: 12px; padding: 20px; margin-bottom: 16px; border: 1px solid #e5e7eb; transition: all 0.2s ease; } .medicine-card:hover { border-color: #3b82f6; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.1); } .potency-badge { display: inline-block; padding: 6px 12px; border-radius: 20px; font-size: 13px; font-weight: 500; margin: 2px; } .potency-high { background: #d1fae5; color: #065f46; } .potency-medium { background: #fef3c7; color: #92400e; } .potency-low { background: #e5e7eb; color: #6b7280; } .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } .grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; } @media (max-width: 768px) { .grid-2, .grid-3 { grid-template-columns: 1fr; } } """ def create_header(self): """Create application header""" return gr.Markdown(f"""

🏥 {APP_NAME}

{APP_DESCRIPTION}

🌿
""") def create_analysis_tab(self): """Create symptom analysis tab""" with gr.Column(elem_id="analysis-tab") as tab: # Patient Information with gr.Group(elem_classes="analysis-card"): gr.Markdown("### 👤 Patient Information") with gr.Row(): age = gr.Slider(label="Age", minimum=0, maximum=120, value=35, step=1) gender = gr.Dropdown(label="Gender", choices=["Male", "Female", "Other"], value="Male") constitution = gr.Dropdown( label="Constitutional Type", choices=["Lean/Nervous", "Average", "Robust/Plethoric", "Not sure"], value="Average" ) # Symptom Assessment with gr.Group(elem_classes="analysis-card"): gr.Markdown("### 🔍 Symptom Assessment") chief_complaint = gr.Textbox( label="Chief Complaint", placeholder="Describe your main health concern...", lines=3 ) with gr.Row(): location = gr.Textbox( label="Location", placeholder="Where exactly are symptoms?", lines=2 ) sensation = gr.Textbox( label="Sensation", placeholder="What does it feel like?", lines=2 ) intensity = gr.Slider(label="Intensity (1-10)", minimum=1, maximum=10, value=5) # Modalities with gr.Group(elem_classes="analysis-card"): gr.Markdown("### 📈 Modalities") with gr.Row(): aggravations = gr.Textbox( label="Aggravating Factors", placeholder="What makes it worse?", lines=2 ) ameliorations = gr.Textbox( label="Ameliorating Factors", placeholder="What makes it better?", lines=2 ) timing = gr.Textbox( label="Timing & Periodicity", placeholder="When do symptoms occur?", lines=2 ) # Emotional State with gr.Group(elem_classes="analysis-card"): gr.Markdown("### 😊 Emotional & Mental State") emotional_state = gr.Textbox( label="Emotional State", placeholder="Describe emotions, fears, mental symptoms...", lines=2 ) generalities = gr.Textbox( label="General Symptoms", placeholder="Thermal preferences, food desires/aversions...", lines=2 ) # Analysis Button analyze_btn = gr.Button( "🔬 Start Enhanced Analysis", variant="primary", elem_classes="primary-btn" ) # Results Display results = gr.HTML( label="Analysis Results", elem_classes="results-container" ) # Analysis function def analyze_symptoms( age_val, gender_val, constitution_val, complaint_val, location_val, sensation_val, intensity_val, aggravations_val, ameliorations_val, timing_val, emotional_val, generalities_val ): patient_data = { "age": age_val, "gender": gender_val, "constitution": constitution_val, "chief_complaint": complaint_val, "location": location_val, "sensation": sensation_val, "intensity": intensity_val, "aggravations": aggravations_val, "ameliorations": ameliorations_val, "timing": timing_val, "emotional_state": emotional_val, "generalities": generalities_val } result = analyzer.analyze_case(patient_data) return self._generate_results_html(result) # Connect button analyze_btn.click( fn=analyze_symptoms, inputs=[ age, gender, constitution, chief_complaint, location, sensation, intensity, aggravations, ameliorations, timing, emotional_state, generalities ], outputs=results ) return tab def create_medicine_tab(self): """Create medicine database tab""" with gr.Column(elem_id="medicine-tab") as tab: gr.Markdown("### 💊 Homeopathic Medicine Database") gr.Markdown("Search and explore detailed information about homeopathic remedies") # Search interface with gr.Row(): search_input = gr.Textbox( label="Search Remedies", placeholder="Search by remedy name or indication (e.g., fever, headache, arnica)...", scale=4 ) search_btn = gr.Button("Search", variant="primary", scale=1) # Search results search_results = gr.HTML( label="Search Results", elem_classes="results-container" ) # Medicine details medicine_details = gr.HTML( label="Medicine Details", visible=False ) # Search function def perform_search(query): if not query or len(query.strip()) < 2: return "", gr.HTML(visible=False) results = search_remedies(query, max_results=10) if not results: return "
No remedies found. Try a different search term.
", gr.HTML(visible=False) html = "
" html += f"

Found {len(results)} remedies:

" for name, data, match_type in results: html += f"""

{name}

{data['description']}

""" for indication in data['indications'][:3]: html += f'{indication}' html += f"""
{data['potencies'].get('first_aid', '30C')}
""" html += "
" html += """ """ return html, gr.HTML(visible=False) # Details function def show_remedy_details(name): if not name: return "" data = get_remedy_details(name) if not data: return "
Remedy not found
" return self._generate_medicine_details_html(name, data) # Connect events search_input.submit( fn=perform_search, inputs=[search_input], outputs=[search_results, medicine_details] ) search_btn.click( fn=perform_search, inputs=[search_input], outputs=[search_results, medicine_details] ) return tab def _generate_results_html(self, analysis_result): """Generate HTML for analysis results""" if not analysis_result.get("success", False): return f"""
🏥

{analysis_result.get('message', 'Analysis Failed')}

Please provide more detailed symptoms.

""" matches = analysis_result["matches"] if not matches: return """
🏥

No Clear Matches Found

Try providing more specific symptom details.

""" top_match = matches[0] case_type = analysis_result.get("case_type", "acute") html = f"""

Enhanced Homeopathic Analysis

Case Type: {case_type.upper()} • Confidence: {top_match['score']:.1f}%

Top Match
{top_match['name']}
1

{top_match['name']}

{top_match['data']['description']}

💊 Recommendations

""" rec = top_match.get("recommendations", {}) if rec: html += f"""
Primary Potency
{rec.get('primary_potency', '30C')}
For {case_type} cases
Administration
{rec.get('frequency', '3 times daily')}
Duration: {rec.get('duration', '1-2 weeks')}
Dosage
{rec.get('dosage', '3-5 pellets sublingually')}
""" html += """

🔍 Key Match Factors

    """ for reason in top_match.get("match_reasons", []): html += f'
  • {reason}
  • ' html += """

Alternative Considerations

""" for i, match in enumerate(matches[1:4], 2): color = "#f59e0b" if match["score"] > 60 else "#6b7280" html += f"""
{i}
{match['name']}
{match['score']:.1f}%
{match['data']['description']}
""" for indication in match["data"]["indications"][:2]: html += f'{indication}' html += """
""" html += """
⚠️
Medical Disclaimer
This analysis is generated by AI for educational purposes only. It is not a substitute for professional medical advice, diagnosis, or treatment. Always consult a qualified homeopath or medical professional for health concerns.
""" return html def _generate_medicine_details_html(self, name, data): """Generate HTML for medicine details""" html = f"""

{name}

{data['description']}

🔍 Key Indications

    """ for indication in data["indications"][:6]: html += f"
  • {indication}
  • " html += """

📈 Modalities

Worse:
""" html += ', '.join(data["modalities"]["worse"]) html += """
Better:
""" html += ', '.join(data["modalities"]["better"]) html += """

💊 Potency Guide

""" for case_type, potencies in data["potencies"].items(): if case_type != "recommended": html += f"""
{case_type}:
""" if isinstance(potencies, list): html += ', '.join(potencies) else: html += potencies html += "
" html += f"""
Recommended:
{data['potencies'].get('recommended', '30C for acute cases')}
Disclaimer: This information is for educational purposes only. Always consult a qualified homeopathic practitioner for treatment recommendations.
""" return html