yekkala commited on
Commit
77ac32c
ยท
verified ยท
1 Parent(s): b91fc86

implemented deep analysis

Browse files

Implemented deep analysis
added a medicine search

Files changed (1) hide show
  1. app.py +1286 -676
app.py CHANGED
@@ -1,679 +1,968 @@
1
  """
2
- Homeopathic Symptom Analyzer Pro
3
- Professional Medical UI with Multi-Step Assessment
4
- Optimized for Hugging Face Spaces
5
  """
6
  import gradio as gr
7
  import re
8
  import json
9
  from datetime import datetime
 
10
 
11
- # ==================== MEDICAL DATABASE ====================
12
- REMEDIES = {
13
  "Arnica Montana": {
14
  "description": "First remedy for trauma, injuries, and shock",
15
- "indications": ["Physical trauma", "Bruising", "Muscle soreness", "Post-surgical"],
16
  "modalities": {
17
- "worse": ["Touch", "Motion", "Cold damp"],
18
- "better": ["Lying down", "Cold applications"]
19
  },
20
- "mental": "Apathetic, denies illness, wants solitude",
21
- "physical": "Sore bruised feeling, bed feels hard",
22
- "constitutional": "Robust, sanguine",
23
- "potency": "6C, 30C, 200C",
24
- "confidence": 0
 
 
 
 
 
 
 
 
 
 
 
 
25
  },
26
  "Belladonna": {
27
  "description": "Sudden violent onset with intense symptoms",
28
- "indications": ["Sudden fever", "Throbbing headache", "Inflammation", "Septic states"],
29
  "modalities": {
30
- "worse": ["Touch", "Noise", "Light", "3 PM"],
31
- "better": ["Semi-erect", "Warm room"]
 
 
 
 
 
 
 
 
 
32
  },
33
- "mental": "Restless, anxious, delirious, sees monsters",
34
- "physical": "Bright red face, hot head, cold extremities",
35
- "constitutional": "Plethoric, robust individuals",
36
- "potency": "30C, 200C",
37
- "confidence": 0
 
 
 
38
  },
39
  "Bryonia Alba": {
40
  "description": "Irritable, thirsty, worse from slightest motion",
41
- "indications": ["Dry mucous membranes", "Joint pains", "Constipation", "Pleurisy"],
42
  "modalities": {
43
- "worse": ["Motion", "Heat", "Morning"],
44
- "better": ["Pressure", "Rest", "Cold"]
45
  },
46
- "mental": "Irritable, anxious about business, wants to be alone",
47
- "physical": "Great thirst for cold water, stitching pains",
48
- "constitutional": "Irritable, nervous types",
49
- "potency": "6C, 30C, 200C",
50
- "confidence": 0
 
 
 
 
 
 
 
 
 
 
 
 
51
  },
52
  "Nux Vomica": {
53
- "description": "Irritable, impatient, digestive complaints",
54
- "indications": ["Digestive disorders", "Overindulgence", "Insomnia", "Irritability"],
55
  "modalities": {
56
- "worse": ["Morning", "After eating", "Cold", "Mental exertion"],
57
- "better": ["Evening", "Rest", "Damp weather"]
 
 
 
 
 
 
 
 
 
58
  },
59
- "mental": "Irritable, fault-finding, ambitious",
60
- "physical": "Constipation with ineffectual urging, sensitive to stimuli",
61
- "constitutional": "Thin, nervous, active persons",
62
- "potency": "6C, 30C",
63
- "confidence": 0
 
 
 
64
  },
65
  "Pulsatilla": {
66
- "description": "Mild, yielding, changeable symptoms",
67
- "indications": ["Changeable symptoms", "Digestive issues", "Colds", "Venous congestion"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  "modalities": {
69
- "worse": ["Heat", "Rich food", "Evening"],
70
- "better": ["Open air", "Cold", "Gentle motion"]
 
 
 
 
 
 
 
 
 
71
  },
72
- "mental": "Weepy, changeable mood, seeks consolation",
73
- "physical": "Thirstless, bland yellow discharges",
74
- "constitutional": "Mild, gentle, emotional types",
75
- "potency": "6C, 30C",
76
- "confidence": 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  }
78
  }
79
 
80
- # ==================== ANALYSIS ENGINE ====================
81
- class HomeopathicAnalyzer:
82
  def __init__(self):
83
- self.patient_data = {}
84
- self.analysis_results = {}
 
 
 
 
 
 
 
85
 
86
- def assess_patient(self, data):
87
- """Comprehensive patient assessment"""
88
- self.patient_data = data
 
 
 
 
 
 
89
 
90
  # Calculate remedy matches
91
- matches = self.calculate_remedy_matches(data)
 
 
 
 
 
 
 
 
 
92
 
93
- # Generate analysis report
94
- report = self.generate_report(matches, data)
95
 
96
- return matches, report
 
 
 
 
 
97
 
98
- def calculate_remedy_matches(self, data):
99
- """Calculate remedy matches with confidence scores"""
100
  matches = []
101
- symptoms = f"{data.get('chief_complaint', '')} {data.get('location', '')} {data.get('sensation', '')}"
102
 
103
- for name, remedy in REMEDIES.items():
104
  score = 0
 
 
 
 
 
 
105
 
106
- # Symptom matching (40%)
107
- symptom_match = self.match_symptoms(symptoms, remedy)
108
- score += symptom_match * 0.4
 
 
109
 
110
- # Modality matching (30%)
111
- modality_match = self.match_modalities(data, remedy)
112
- score += modality_match * 0.3
 
 
113
 
114
- # Emotional matching (20%)
115
- emotional_match = self.match_emotional(data, remedy)
116
- score += emotional_match * 0.2
 
 
117
 
118
- # Time matching (10%)
119
- time_match = self.match_timing(data, remedy)
120
- score += time_match * 0.1
 
 
121
 
122
- if score > 0.2: # 20% threshold
123
- remedy['confidence'] = min(99, score * 100)
 
 
 
124
  matches.append({
125
- 'name': name,
126
- 'data': remedy.copy(),
127
- 'score': remedy['confidence']
 
 
 
 
 
 
128
  })
129
 
130
  # Sort by confidence
131
  matches.sort(key=lambda x: x['score'], reverse=True)
132
- return matches[:5]
133
 
134
- def match_symptoms(self, symptoms, remedy):
135
- """Match patient symptoms with remedy indications"""
136
- symptom_words = set(re.findall(r'\b\w{4,}\b', symptoms.lower()))
137
- remedy_text = f"{remedy['description']} {' '.join(remedy['indications'])} {remedy['physical']}".lower()
138
- remedy_words = set(re.findall(r'\b\w{4,}\b', remedy_text))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
- if not symptom_words:
141
- return 0
 
142
 
143
- common = symptom_words.intersection(remedy_words)
144
- return len(common) / len(symptom_words)
145
 
146
- def match_modalities(self, data, remedy):
147
- """Match patient modalities with remedy modalities"""
148
  score = 0
 
149
 
150
  # Check aggravations
151
- patient_worse = data.get('aggravations', '').lower()
152
- remedy_worse = ' '.join(remedy['modalities']['worse']).lower()
153
 
154
- if patient_worse and any(word in patient_worse for word in remedy_worse.split()):
155
- score += 0.5
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  # Check ameliorations
158
- patient_better = data.get('ameliorations', '').lower()
159
- remedy_better = ' '.join(remedy['modalities']['better']).lower()
160
 
161
- if patient_better and any(word in patient_better for word in remedy_better.split()):
162
- score += 0.5
 
 
 
 
 
 
 
 
 
 
 
163
 
164
- return score
 
 
 
 
 
 
 
 
 
165
 
166
- def match_emotional(self, data, remedy):
167
  """Match emotional state"""
168
- emotional = data.get('emotional_state', '').lower()
169
- remedy_emotional = remedy['mental'].lower()
170
 
 
171
  if not emotional:
172
- return 0
173
 
174
- emotional_words = set(re.findall(r'\b\w{4,}\b', emotional))
175
- remedy_words = set(re.findall(r'\b\w{4,}\b', remedy_emotional))
176
 
177
- if not emotional_words:
178
- return 0
 
 
 
179
 
180
- common = emotional_words.intersection(remedy_words)
181
- return len(common) / len(emotional_words)
182
-
183
- def match_timing(self, data, remedy):
184
- """Match timing patterns"""
185
- timing = data.get('timing', '').lower()
 
186
 
187
- # Check for common time patterns
188
- time_patterns = ['morning', 'evening', 'night', 'afternoon', '3 pm', 'midnight']
 
 
 
 
 
189
 
190
- for pattern in time_patterns:
191
- if pattern in timing:
192
- return 0.5
193
 
194
- return 0
 
 
 
 
195
 
196
- def generate_report(self, matches, data):
197
- """Generate professional medical report"""
198
- report = {
199
- 'patient_info': {
200
- 'chief_complaint': data.get('chief_complaint', 'Not specified'),
201
- 'assessment_date': datetime.now().strftime('%Y-%m-%d %H:%M'),
202
- 'symptom_count': len(re.findall(r'\b\w{3,}\b', data.get('chief_complaint', '')))
203
- },
204
- 'top_remedies': matches[:3],
205
- 'analysis_notes': self.generate_notes(matches, data),
206
- 'recommendations': self.generate_recommendations(matches)
 
 
 
 
207
  }
208
- return report
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
- def generate_notes(self, matches, data):
211
- """Generate analysis notes"""
212
- notes = []
 
213
 
214
- if matches:
215
- top_match = matches[0]
216
- notes.append(f"**Primary Match:** {top_match['name']} ({top_match['score']:.1f}% confidence)")
217
- notes.append(f"**Key Indications:** {', '.join(top_match['data']['indications'][:3])}")
218
- notes.append(f"**Constitutional Fit:** {top_match['data']['constitutional']}")
219
 
220
- # Add modality analysis
221
- if data.get('aggravations'):
222
- notes.append(f"**Aggravating Factors:** {data['aggravations']}")
223
- if data.get('ameliorations'):
224
- notes.append(f"**Relieving Factors:** {data['ameliorations']}")
225
 
226
- return notes
 
 
 
 
 
227
 
228
- def generate_recommendations(self, matches):
229
- """Generate clinical recommendations"""
230
- recommendations = []
 
231
 
232
- if matches:
233
- top_match = matches[0]
234
- recommendations.append(f"**Consider:** {top_match['name']} {top_match['data']['potency']}")
235
- recommendations.append("**Administration:** 3-5 pellets sublingually, away from meals")
236
- recommendations.append("**Follow-up:** Reassess in 48 hours")
237
- recommendations.append("**Contraindications:** Monitor for aggravation")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
- recommendations.append("**General Advice:** Maintain hydration, rest as needed")
240
- recommendations.append("**Referral:** Consult qualified homeopath for chronic conditions")
241
 
242
  return recommendations
243
-
244
- # Initialize analyzer
245
- analyzer = HomeopathicAnalyzer()
246
-
247
- # ==================== PROFESSIONAL UI COMPONENTS ====================
248
- def create_header():
249
- """Create professional header"""
250
- return gr.Markdown("""
251
- <div style="background: linear-gradient(135deg, #1e40af 0%, #1e3a8a 100%);
252
- padding: 32px; border-radius: 0 0 16px 16px; margin: -20px -20px 30px -20px; color: white;">
253
- <div style="display: flex; align-items: center; gap: 20px;">
254
- <div style="font-size: 40px;">๐ŸŒฟ</div>
255
- <div>
256
- <h1 style="margin: 0; font-size: 28px; font-weight: 600;">Homeopathic Symptom Analyzer Pro</h1>
257
- <p style="margin: 8px 0 0 0; opacity: 0.9; font-size: 16px;">
258
- Professional AI-powered homeopathic assessment and remedy matching
259
- </p>
260
- </div>
261
- </div>
262
- </div>
263
- """)
264
-
265
- def create_patient_info_section():
266
- """Create patient information section"""
267
- with gr.Group():
268
- gr.Markdown("### ๐Ÿ‘ค Patient Information")
269
 
270
- with gr.Row():
271
- with gr.Column(scale=1):
272
- age = gr.Slider(label="Age", minimum=0, maximum=120, value=35, step=1)
273
- with gr.Column(scale=1):
274
- gender = gr.Dropdown(label="Gender", choices=["Male", "Female", "Other", "Prefer not to say"], value="Male")
275
- with gr.Column(scale=1):
276
- constitution = gr.Dropdown(
277
- label="Body Type",
278
- choices=["Lean/Nervous", "Average", "Robust/Plethoric", "Not sure"],
279
- value="Average"
280
- )
281
 
282
- return age, gender, constitution
283
-
284
- def create_symptom_assessment_section():
285
- """Create symptom assessment section"""
286
- with gr.Group():
287
- gr.Markdown("### ๐Ÿฉบ Symptom Assessment")
288
-
289
- chief_complaint = gr.Textbox(
290
- label="Chief Complaint",
291
- placeholder="Describe the main health concern in detail...",
292
- lines=3,
293
- info="Be specific about onset, location, and characteristics"
294
- )
295
 
296
- with gr.Row():
297
- location = gr.Textbox(
298
- label="Location",
299
- placeholder="Where exactly are symptoms located?",
300
- lines=2,
301
- scale=1
302
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
 
304
- sensation = gr.Dropdown(
305
- label="Sensation Type",
306
- choices=["Burning", "Throbbing", "Stabbing", "Aching", "Pressing", "Numbness", "Other"],
307
- value="Throbbing",
308
- multiselect=True,
309
- scale=1
310
- )
 
 
 
 
 
 
 
 
 
 
 
 
311
 
312
- intensity = gr.Slider(
313
- label="Symptom Intensity (1-10)",
314
- minimum=1,
315
- maximum=10,
316
- value=5,
317
- step=1
318
- )
 
 
 
 
319
 
320
- return chief_complaint, location, sensation, intensity
321
 
322
- def create_modality_section():
323
- """Create modality assessment section"""
324
- with gr.Group():
325
- gr.Markdown("### โš–๏ธ Modality Assessment")
 
 
 
 
 
 
326
 
 
327
  with gr.Row():
328
- aggravations = gr.Textbox(
329
- label="What makes it worse?",
330
- placeholder="e.g., motion, cold, noise, after eating...",
331
- lines=2,
332
- scale=1
333
- )
334
-
335
- ameliorations = gr.Textbox(
336
- label="What makes it better?",
337
- placeholder="e.g., rest, pressure, warmth, open air...",
338
- lines=2,
339
- scale=1
340
  )
 
341
 
342
- timing = gr.Textbox(
343
- label="Timing & Periodicity",
344
- placeholder="When do symptoms occur? Any pattern?",
345
- lines=2
 
 
346
  )
347
 
348
- return aggravations, ameliorations, timing
349
-
350
- def create_emotional_section():
351
- """Create emotional/mental assessment section"""
352
- with gr.Group():
353
- gr.Markdown("### ๐Ÿ’ญ Emotional & Mental State")
354
-
355
- emotional_state = gr.Textbox(
356
- label="Current Emotional State",
357
- placeholder="Describe emotions, fears, anxieties, mental symptoms...",
358
- lines=3
359
  )
360
 
361
- with gr.Row():
362
- sleep = gr.Dropdown(
363
- label="Sleep Pattern",
364
- choices=["Normal", "Insomnia", "Excessive sleep", "Restless", "Nightmares"],
365
- value="Normal",
366
- scale=1
367
- )
368
 
369
- appetite = gr.Dropdown(
370
- label="Appetite",
371
- choices=["Normal", "Increased", "Decreased", "Craving specific foods"],
372
- value="Normal",
373
- scale=1
374
- )
 
 
 
 
 
 
 
 
 
375
 
376
- generalities = gr.Textbox(
377
- label="General Symptoms & Preferences",
378
- placeholder="Thermal preferences, thirst, food desires/aversions...",
379
- lines=2
380
- )
 
 
381
 
382
- return emotional_state, sleep, appetite, generalities
383
-
384
- def create_search_options():
385
- """Create search configuration section"""
386
- with gr.Group():
387
- gr.Markdown("### ๐Ÿ” Analysis Settings")
388
 
389
- with gr.Row():
390
- search_depth = gr.Radio(
391
- label="Analysis Depth",
392
- choices=["Quick Assessment", "Standard Analysis", "Comprehensive Evaluation"],
393
- value="Standard Analysis"
394
- )
395
-
396
- include_database = gr.CheckboxGroup(
397
- label="Knowledge Sources",
398
- choices=["Internal Database", "Online Resources", "Clinical Guidelines"],
399
- value=["Internal Database", "Clinical Guidelines"]
400
- )
401
 
402
- return search_depth, include_database
403
-
404
- def create_analysis_button():
405
- """Create primary analysis button"""
406
- return gr.Button(
407
- "๐Ÿš€ Start Professional Analysis",
408
- variant="primary",
409
- size="lg",
410
- elem_classes="analysis-btn"
411
- )
412
-
413
- def create_results_display():
414
- """Create results display section"""
415
- with gr.Group():
416
- gr.Markdown("### ๐Ÿ“Š Analysis Results")
417
-
418
- results = gr.HTML(
419
- label="",
420
- elem_id="results-display"
421
  )
422
 
423
- return results
424
 
425
- def generate_results_html(matches, report, patient_data):
426
- """Generate professional results HTML"""
427
-
428
- if not matches:
429
- return """
430
- <div style="text-align: center; padding: 40px; background: #f8f9fa; border-radius: 12px;">
431
- <div style="font-size: 48px; margin-bottom: 20px;">๐Ÿ”</div>
432
- <h3 style="color: #6b7280; margin-bottom: 10px;">No Clear Matches Found</h3>
433
- <p style="color: #9ca3af;">Try providing more specific symptom details and modalities.</p>
434
- </div>
435
- """
436
 
437
- # Top match
438
- top_match = matches[0]
439
 
440
  html = f"""
441
  <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
442
 
443
- <!-- Report Header -->
444
  <div style="background: linear-gradient(135deg, #1e40af 0%, #1e3a8a 100%);
445
  padding: 24px; border-radius: 12px; margin-bottom: 24px; color: white;">
446
- <div style="display: flex; justify-content: space-between; align-items: center;">
447
- <div>
448
- <h2 style="margin: 0; font-size: 22px; font-weight: 600;">Homeopathic Assessment Report</h2>
449
- <p style="margin: 8px 0 0 0; opacity: 0.9; font-size: 14px;">
450
- Generated: {datetime.now().strftime('%B %d, %Y at %H:%M')}
451
- </p>
452
- </div>
453
- <div style="background: rgba(255,255,255,0.1); padding: 8px 16px; border-radius: 20px;">
454
- <div style="font-size: 12px; opacity: 0.8;">Confidence</div>
455
- <div style="font-size: 20px; font-weight: 700;">{top_match['score']:.1f}%</div>
456
- </div>
457
- </div>
458
  </div>
459
 
460
- <!-- Top Recommendation -->
461
- <div style="background: white; border-radius: 12px; padding: 24px; margin-bottom: 20px;
462
- box-shadow: 0 2px 8px rgba(0,0,0,0.05); border: 1px solid #e5e7eb;">
463
-
464
- <div style="display: flex; align-items: center; margin-bottom: 20px;">
465
- <div style="width: 60px; height: 60px; background: linear-gradient(135deg, #10b981, #059669);
466
- border-radius: 12px; display: flex; align-items: center; justify-content: center;
467
- margin-right: 20px; color: white; font-size: 24px;">
468
- ๐Ÿ†
469
- </div>
470
- <div>
471
- <h3 style="margin: 0; color: #1f2937; font-size: 20px; font-weight: 600;">Primary Recommendation</h3>
472
- <p style="margin: 4px 0 0 0; color: #6b7280; font-size: 16px;">{top_match['name']}</p>
473
- </div>
474
- </div>
475
 
476
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px;">
477
-
478
- <!-- Key Information -->
479
- <div>
480
- <div style="font-size: 14px; font-weight: 600; color: #4b5563; margin-bottom: 8px;">
481
- ๐Ÿ“‹ Key Information
482
- </div>
483
- <div style="background: #f9fafb; padding: 16px; border-radius: 8px;">
484
- <div style="margin-bottom: 8px;">
485
- <div style="font-size: 13px; color: #9ca3af;">Description</div>
486
- <div style="color: #374151; font-size: 14px;">{top_match['data']['description']}</div>
487
- </div>
488
- <div style="margin-bottom: 8px;">
489
- <div style="font-size: 13px; color: #9ca3af;">Constitutional Type</div>
490
- <div style="color: #374151; font-size: 14px;">{top_match['data']['constitutional']}</div>
491
- </div>
492
- <div>
493
- <div style="font-size: 13px; color: #9ca3af;">Suggested Potency</div>
494
- <div style="color: #374151; font-size: 14px; font-weight: 500;">{top_match['data']['potency']}</div>
495
- </div>
496
- </div>
497
- </div>
498
-
499
- <!-- Indications -->
500
- <div>
501
- <div style="font-size: 14px; font-weight: 600; color: #4b5563; margin-bottom: 8px;">
502
- ๐ŸŽฏ Key Indications
503
- </div>
504
- <div style="background: #f9fafb; padding: 16px; border-radius: 8px;">
505
  """
506
 
507
- for indication in top_match['data']['indications'][:4]:
508
- html += f"""
509
- <div style="display: flex; align-items: center; margin-bottom: 6px;">
510
- <div style="width: 6px; height: 6px; background: #10b981; border-radius: 50%; margin-right: 10px;"></div>
511
- <div style="color: #374151; font-size: 14px;">{indication}</div>
512
- </div>
513
- """
514
 
515
  html += """
516
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
  </div>
518
-
519
- <!-- Modalities -->
520
  <div>
521
- <div style="font-size: 14px; font-weight: 600; color: #4b5563; margin-bottom: 8px;">
522
- โš–๏ธ Modalities
523
- </div>
524
- <div style="background: #f9fafb; padding: 16px; border-radius: 8px;">
525
- <div style="margin-bottom: 12px;">
526
- <div style="font-size: 13px; color: #ef4444; margin-bottom: 4px;">Worse:</div>
527
- <div style="color: #374151; font-size: 14px;">"""
528
 
529
- html += ', '.join(top_match['data']['modalities']['worse'][:3])
530
 
531
- html += """
532
- </div>
533
- </div>
534
- <div>
535
- <div style="font-size: 13px; color: #10b981; margin-bottom: 4px;">Better:</div>
536
- <div style="color: #374151; font-size: 14px;">"""
 
 
 
 
 
537
 
538
- html += ', '.join(top_match['data']['modalities']['better'][:3])
 
 
 
 
 
 
 
 
 
 
 
 
539
 
540
- html += """
541
- </div>
542
- </div>
543
- </div>
 
544
  </div>
545
-
546
  </div>
 
 
 
 
547
 
548
- <!-- Mental State -->
549
- <div style="margin-top: 20px;">
550
- <div style="font-size: 14px; font-weight: 600; color: #4b5563; margin-bottom: 8px;">
551
- ๐Ÿ’ญ Mental/Emotional Profile
552
- </div>
553
- <div style="background: #f9fafb; padding: 16px; border-radius: 8px;">
554
- <div style="color: #374151; font-size: 14px; line-height: 1.5;">{mental}</div>
 
 
 
 
 
 
 
 
 
 
 
 
555
  </div>
556
  </div>
557
 
558
  </div>
559
- """.format(mental=top_match['data']['mental'])
560
-
561
- # Additional Remedies
562
- if len(matches) > 1:
563
- html += """
564
- <div style="margin-bottom: 20px;">
565
- <h3 style="color: #1f2937; font-size: 18px; margin-bottom: 16px;">Alternative Considerations</h3>
566
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px;">
567
- """
568
 
569
- for i, match in enumerate(matches[1:4], 2):
570
- color = "#f59e0b" if match['score'] > 60 else "#6b7280"
571
 
572
- html += f"""
573
- <div style="background: white; border-radius: 10px; padding: 20px;
574
- box-shadow: 0 1px 3px rgba(0,0,0,0.05); border: 1px solid #e5e7eb;">
575
- <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
576
- <div style="font-weight: 600; color: #1f2937;">#{i}: {match['name']}</div>
577
- <div style="background: {color}10; color: {color}; padding: 4px 12px;
578
- border-radius: 20px; font-size: 13px; font-weight: 500;">
579
- {match['score']:.1f}%
580
- </div>
581
  </div>
582
- <div style="color: #6b7280; font-size: 14px; margin-bottom: 8px;">{match['data']['description']}</div>
583
- <div style="font-size: 13px; color: #9ca3af;">Key: {', '.join(match['data']['indications'][:2])}</div>
584
  </div>
585
- """
586
-
587
- html += "</div></div>"
588
-
589
- # Clinical Recommendations
590
- html += """
591
- <div style="background: #f0f9ff; border-radius: 12px; padding: 24px; margin-bottom: 20px; border: 1px solid #bae6fd;">
592
- <h3 style="color: #0369a1; font-size: 18px; margin-bottom: 16px; display: flex; align-items: center;">
593
- <span style="margin-right: 8px;">๐Ÿ’Š</span> Clinical Recommendations
594
- </h3>
595
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px;">
596
- """
597
-
598
- recommendations = [
599
- ("Dosage", "3-5 pellets sublingually, away from meals"),
600
- ("Frequency", "Once daily or as needed for acute symptoms"),
601
- ("Observation", "Monitor response for 48 hours"),
602
- ("Follow-up", "Consult practitioner if no improvement"),
603
- ("Contraindications", "Discontinue if symptoms worsen")
604
- ]
605
-
606
- for title, detail in recommendations:
607
- html += f"""
608
- <div style="background: white; padding: 16px; border-radius: 8px; border-left: 4px solid #0ea5e9;">
609
- <div style="font-size: 14px; font-weight: 600; color: #0c4a6e; margin-bottom: 4px;">{title}</div>
610
- <div style="color: #475569; font-size: 14px;">{detail}</div>
611
- </div>
612
- """
613
-
614
- html += """
615
- </div>
616
- </div>
617
-
618
- <!-- Disclaimer -->
619
- <div style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 20px;">
620
- <div style="display: flex; align-items: flex-start;">
621
- <div style="color: #dc2626; font-size: 20px; margin-right: 12px;">โš ๏ธ</div>
622
- <div>
623
- <div style="font-weight: 600; color: #991b1b; margin-bottom: 4px;">Medical Disclaimer</div>
624
- <div style="color: #7f1d1d; font-size: 14px; line-height: 1.5;">
625
- This analysis is for educational purposes only and not a substitute for professional medical advice.
626
- Always consult a qualified homeopath or medical professional for diagnosis and treatment.
627
  </div>
628
  </div>
 
629
  </div>
630
- </div>
631
-
632
  </div>
633
  """
634
 
635
  return html
636
 
637
- # ==================== MAIN APPLICATION ====================
638
  with gr.Blocks(
639
- title="Homeopathic Symptom Analyzer Pro | Medical Assessment",
640
  theme=gr.themes.Soft(
641
  primary_hue="blue",
642
  secondary_hue="slate",
643
  font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"]
644
  ),
645
  css="""
646
- /* Main Layout */
647
  .gradio-container {
648
- max-width: 1200px !important;
649
  margin: 0 auto !important;
650
- padding: 0 !important;
651
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
652
  }
653
 
654
- .main-content {
655
- padding: 0 24px 24px 24px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
  }
657
 
658
  /* Cards */
659
- .section-card {
660
  background: white !important;
661
  border-radius: 12px !important;
662
- padding: 24px !important;
663
- margin-bottom: 20px !important;
664
  border: 1px solid #e5e7eb !important;
665
- box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important;
666
- }
667
-
668
- /* Typography */
669
- h1, h2, h3, h4 {
670
- font-weight: 600 !important;
671
- letter-spacing: -0.025em !important;
672
  }
673
 
674
  /* Form Elements */
675
  .form-section {
676
- margin-bottom: 32px !important;
677
  }
678
 
679
  .form-label {
@@ -699,7 +988,7 @@ with gr.Blocks(
699
  }
700
 
701
  /* Buttons */
702
- .analysis-btn {
703
  background: linear-gradient(135deg, #1e40af 0%, #1e3a8a 100%) !important;
704
  color: white !important;
705
  border: none !important;
@@ -712,26 +1001,23 @@ with gr.Blocks(
712
  transition: all 0.3s ease !important;
713
  }
714
 
715
- .analysis-btn:hover {
716
  transform: translateY(-2px) !important;
717
  box-shadow: 0 8px 16px rgba(30, 64, 175, 0.2) !important;
718
  }
719
 
720
- /* Checkboxes & Radio */
721
- .checkbox-group, .radio-group {
722
- display: flex !important;
723
- gap: 16px !important;
724
- margin-top: 12px !important;
725
- }
726
-
727
- /* Sliders */
728
- .slider-container {
729
- padding: 8px 0 !important;
730
  }
731
 
732
- /* Results Area */
733
- #results-display {
734
- margin-top: 24px !important;
735
  animation: fadeIn 0.5s ease !important;
736
  }
737
 
@@ -740,182 +1026,506 @@ with gr.Blocks(
740
  to { opacity: 1; transform: translateY(0); }
741
  }
742
 
743
- /* Grid Layout */
744
- .grid-2 {
745
- display: grid !important;
746
- grid-template-columns: 1fr 1fr !important;
747
- gap: 20px !important;
748
- }
749
-
750
- @media (max-width: 768px) {
751
- .grid-2 {
752
- grid-template-columns: 1fr !important;
753
- }
754
-
755
- .main-content {
756
- padding: 0 16px 16px 16px !important;
757
- }
758
- }
759
-
760
- /* Status Indicators */
761
- .status-indicator {
762
- display: inline-flex !important;
763
- align-items: center !important;
764
- padding: 4px 12px !important;
765
  border-radius: 20px !important;
766
- font-size: 12px !important;
767
  font-weight: 500 !important;
 
768
  }
769
 
770
- .status-high {
771
  background: #d1fae5 !important;
772
  color: #065f46 !important;
773
  }
774
 
775
- .status-medium {
776
  background: #fef3c7 !important;
777
  color: #92400e !important;
778
  }
779
 
780
- .status-low {
781
- background: #f3f4f6 !important;
782
  color: #6b7280 !important;
783
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
784
  """
785
  ) as app:
786
 
787
  # Header
788
- create_header()
789
-
790
- with gr.Column(elem_classes="main-content"):
791
-
792
- # Patient Information
793
- with gr.Group(elem_classes="section-card"):
794
- age, gender, constitution = create_patient_info_section()
795
-
796
- # Symptom Assessment
797
- with gr.Group(elem_classes="section-card"):
798
- chief_complaint, location, sensation, intensity = create_symptom_assessment_section()
799
-
800
- # Modality Assessment
801
- with gr.Group(elem_classes="section-card"):
802
- aggravations, ameliorations, timing = create_modality_section()
803
-
804
- # Emotional Assessment
805
- with gr.Group(elem_classes="section-card"):
806
- emotional_state, sleep, appetite, generalities = create_emotional_section()
807
-
808
- # Search Options
809
- with gr.Group(elem_classes="section-card"):
810
- search_depth, include_database = create_search_options()
811
-
812
- # Analysis Button
813
- analyze_btn = create_analysis_button()
814
-
815
- # Results Display
816
- results_display = create_results_display()
817
-
818
- # Analysis Function
819
- def perform_analysis(
820
- age_val, gender_val, constitution_val,
821
- complaint_val, location_val, sensation_val, intensity_val,
822
- aggravations_val, ameliorations_val, timing_val,
823
- emotional_val, sleep_val, appetite_val, generalities_val,
824
- search_depth_val, include_database_val
825
- ):
826
- """Perform complete analysis"""
827
-
828
- # Prepare patient data
829
- patient_data = {
830
- 'age': age_val,
831
- 'gender': gender_val,
832
- 'constitution': constitution_val,
833
- 'chief_complaint': complaint_val,
834
- 'location': location_val,
835
- 'sensation': ', '.join(sensation_val) if isinstance(sensation_val, list) else sensation_val,
836
- 'intensity': intensity_val,
837
- 'aggravations': aggravations_val,
838
- 'ameliorations': ameliorations_val,
839
- 'timing': timing_val,
840
- 'emotional_state': emotional_val,
841
- 'sleep': sleep_val,
842
- 'appetite': appetite_val,
843
- 'generalities': generalities_val
844
- }
845
-
846
- # Perform analysis
847
- matches, report = analyzer.assess_patient(patient_data)
848
-
849
- # Generate results HTML
850
- results_html = generate_results_html(matches, report, patient_data)
851
-
852
- return results_html
853
-
854
- # Connect button
855
- analyze_btn.click(
856
- fn=perform_analysis,
857
- inputs=[
858
- age, gender, constitution,
859
- chief_complaint, location, sensation, intensity,
860
- aggravations, ameliorations, timing,
861
- emotional_state, sleep, appetite, generalities,
862
- search_depth, include_database
863
- ],
864
- outputs=results_display
865
- )
866
 
867
- # Example Cases
868
- with gr.Accordion("๐Ÿ“‹ Example Cases (Click to Load)", open=False):
869
- gr.Examples(
870
- examples=[
871
- [
872
- 42, "Male", "Robust/Plethoric",
873
- "Sudden severe headache with throbbing pain on right side. Face is red and hot to touch.",
874
- "Right temple and forehead",
875
- ["Throbbing", "Burning"],
876
- 8,
877
- "Noise, light, touch, 3 PM",
878
- "Pressure, dark room, sitting still",
879
- "Started suddenly at 3 PM, worse in afternoon",
880
- "Very anxious, restless, fear of serious illness",
881
- "Restless", "Normal",
882
- "Very thirsty for cold water, sensitive to all stimuli",
883
- "Comprehensive Evaluation",
884
- ["Internal Database", "Clinical Guidelines"]
885
- ],
886
- [
887
- 28, "Female", "Lean/Nervous",
888
- "Sore muscles and bruises after falling yesterday. Everything hurts to touch.",
889
- "Entire body, especially limbs",
890
- ["Sore", "Bruised", "Aching"],
891
- 6,
892
- "Motion, touch, attempting to move",
893
- "Lying completely still, cold applications",
894
- "Started immediately after accident yesterday, constant",
895
- "Wants to be left alone, says 'I'm fine' but clearly in pain",
896
- "Normal", "Decreased",
897
- "Bed feels too hard, can't find comfortable position",
898
- "Standard Analysis",
899
- ["Internal Database", "Clinical Guidelines"]
900
- ]
901
- ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
902
  inputs=[
903
  age, gender, constitution,
904
  chief_complaint, location, sensation, intensity,
905
  aggravations, ameliorations, timing,
906
- emotional_state, sleep, appetite, generalities,
907
- search_depth, include_database
908
  ],
909
- outputs=results_display,
910
- fn=perform_analysis,
911
- cache_examples=False
912
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
913
 
914
- # Launch application
915
- if __name__ == "__main__":
916
- app.launch(
917
- server_name="0.0.0.0",
918
- server_port=7860,
919
- share=False,
920
- debug=False
921
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ Homeopathic Analyzer Pro - Enhanced Analysis Engine
3
+ Professional UI with Medicine Database & Potency Recommendations
 
4
  """
5
  import gradio as gr
6
  import re
7
  import json
8
  from datetime import datetime
9
+ from typing import Dict, List, Tuple, Optional
10
 
11
+ # ==================== ENHANCED DATABASE WITH POTENCIES ====================
12
+ REMEDY_DATABASE = {
13
  "Arnica Montana": {
14
  "description": "First remedy for trauma, injuries, and shock",
15
+ "indications": ["Physical trauma", "Bruising", "Muscle soreness", "Post-surgical", "Shock"],
16
  "modalities": {
17
+ "worse": ["Touch", "Motion", "Cold damp", "Rest"],
18
+ "better": ["Lying down", "Cold applications", "Warmth"]
19
  },
20
+ "mental": "Apathetic, denies illness, wants solitude, says 'I'm fine'",
21
+ "physical": "Sore bruised feeling, bed feels hard, black and blue spots",
22
+ "constitutional": "Robust, sanguine temperament, active individuals",
23
+ "potencies": {
24
+ "acute": ["6C", "30C", "200C"],
25
+ "chronic": ["200C", "1M", "10M"],
26
+ "first_aid": "30C",
27
+ "recommended": "30C for acute, 200C for chronic"
28
+ },
29
+ "dosage": "3-5 pellets every 2-4 hours for acute, once daily for chronic",
30
+ "duration": "48 hours for acute, 4-6 weeks for chronic",
31
+ "aggravation": "Possible initial aggravation",
32
+ "antidotes": ["Camphor", "Coffee"],
33
+ "complementary": ["Aconite", "Hypericum", "Ruta"],
34
+ "miasm": "Traumatic",
35
+ "confidence": 0,
36
+ "match_reasons": []
37
  },
38
  "Belladonna": {
39
  "description": "Sudden violent onset with intense symptoms",
40
+ "indications": ["Sudden high fever", "Throbbing headache", "Acute inflammation", "Septic states", "Sunstroke"],
41
  "modalities": {
42
+ "worse": ["Touch", "Noise", "Light", "3 PM", "Afternoon"],
43
+ "better": ["Semi-erect", "Warm room", "Rest"]
44
+ },
45
+ "mental": "Restless, anxious, delirious, sees monsters, fearful",
46
+ "physical": "Bright red face, hot head, cold extremities, dilated pupils",
47
+ "constitutional": "Plethoric, robust, red-faced individuals",
48
+ "potencies": {
49
+ "acute": ["30C", "200C"],
50
+ "chronic": ["1M", "10M"],
51
+ "first_aid": "30C",
52
+ "recommended": "30C every 1-2 hours in acute fever"
53
  },
54
+ "dosage": "Every 1-2 hours in acute, 3 times daily in subacute",
55
+ "duration": "24-48 hours in acute conditions",
56
+ "aggravation": "Rare",
57
+ "antidotes": ["Camphor", "Coffea"],
58
+ "complementary": ["Aconite", "Hepar Sulph", "Mercurius"],
59
+ "miasm": "Psoric",
60
+ "confidence": 0,
61
+ "match_reasons": []
62
  },
63
  "Bryonia Alba": {
64
  "description": "Irritable, thirsty, worse from slightest motion",
65
+ "indications": ["Dry mucous membranes", "Joint pains", "Constipation", "Pleurisy", "Influenza"],
66
  "modalities": {
67
+ "worse": ["Motion", "Heat", "Morning", "Eating"],
68
+ "better": ["Pressure", "Rest", "Cold", "Lying on painful side"]
69
  },
70
+ "mental": "Irritable, anxious about business, wants to be alone, fear of poverty",
71
+ "physical": "Great thirst for cold water, stitching pains, dry cough",
72
+ "constitutional": "Irritable, nervous, anxious about business",
73
+ "potencies": {
74
+ "acute": ["6C", "30C"],
75
+ "chronic": ["200C", "1M"],
76
+ "first_aid": "30C",
77
+ "recommended": "30C every 4 hours"
78
+ },
79
+ "dosage": "Every 4 hours for acute, once daily for chronic",
80
+ "duration": "3-5 days for acute",
81
+ "aggravation": "Possible if overused",
82
+ "antidotes": ["Nux Vomica", "Chamomilla"],
83
+ "complementary": ["Rhus Tox", "Alumina", "Nux Vomica"],
84
+ "miasm": "Psoric-Sycotic",
85
+ "confidence": 0,
86
+ "match_reasons": []
87
  },
88
  "Nux Vomica": {
89
+ "description": "Irritable, impatient, digestive complaints from overindulgence",
90
+ "indications": ["Digestive disorders", "Overindulgence", "Insomnia", "Irritability", "Hangover"],
91
  "modalities": {
92
+ "worse": ["Morning", "After eating", "Cold", "Mental exertion", "Spices"],
93
+ "better": ["Evening", "Rest", "Damp weather", "Warm wraps"]
94
+ },
95
+ "mental": "Irritable, fault-finding, ambitious, workaholic, sensitive to criticism",
96
+ "physical": "Constipation with ineffectual urging, nausea, chilliness",
97
+ "constitutional": "Thin, nervous, active, ambitious persons",
98
+ "potencies": {
99
+ "acute": ["6C", "30C"],
100
+ "chronic": ["200C", "1M"],
101
+ "first_aid": "30C",
102
+ "recommended": "6C for digestive issues, 30C for acute"
103
  },
104
+ "dosage": "3 times daily for acute, once daily for chronic",
105
+ "duration": "1-2 weeks for acute digestive issues",
106
+ "aggravation": "Possible with high potencies",
107
+ "antidotes": ["Camphor", "Coffee"],
108
+ "complementary": ["Sulphur", "Pulsatilla", "Lycopodium"],
109
+ "miasm": "Sycotic",
110
+ "confidence": 0,
111
+ "match_reasons": []
112
  },
113
  "Pulsatilla": {
114
+ "description": "Mild, yielding, changeable symptoms, weepy disposition",
115
+ "indications": ["Changeable symptoms", "Digestive issues", "Colds", "Venous congestion", "Teething"],
116
+ "modalities": {
117
+ "worse": ["Heat", "Rich food", "Evening", "Lying on left side"],
118
+ "better": ["Open air", "Cold", "Gentle motion", "Consolation"]
119
+ },
120
+ "mental": "Weepy, changeable mood, seeks consolation, affectionate, clingy",
121
+ "physical": "Thirstless, bland yellow discharges, varicose veins",
122
+ "constitutional": "Mild, gentle, emotional, fair-haired persons",
123
+ "potencies": {
124
+ "acute": ["6C", "30C"],
125
+ "chronic": ["200C", "1M"],
126
+ "first_aid": "30C",
127
+ "recommended": "30C for emotional issues, 6C for physical"
128
+ },
129
+ "dosage": "Every 4 hours for acute, once daily for chronic",
130
+ "duration": "1-2 weeks for acute",
131
+ "aggravation": "Rare",
132
+ "antidotes": ["Coffee", "Chamomilla"],
133
+ "complementary": ["Sepia", "Silicea", "Sulphur"],
134
+ "miasm": "Sycotic",
135
+ "confidence": 0,
136
+ "match_reasons": []
137
+ },
138
+ "Aconitum Napellus": {
139
+ "description": "Sudden onset after fright, exposure to cold, intense anxiety",
140
+ "indications": ["Sudden fever", "Panic attacks", "Croup", "Shock", "Exposure"],
141
  "modalities": {
142
+ "worse": ["Evening", "Night", "Cold dry wind", "Noise"],
143
+ "better": ["Open air", "Warm sweat", "Rest"]
144
+ },
145
+ "mental": "Intense fear, anxiety, restlessness, predicts time of death",
146
+ "physical": "Dry hot skin, high fever, red face, intense thirst",
147
+ "constitutional": "Robust persons suddenly taken ill",
148
+ "potencies": {
149
+ "acute": ["30C", "200C"],
150
+ "chronic": ["1M"],
151
+ "first_aid": "30C",
152
+ "recommended": "30C every 30 minutes in acute panic"
153
  },
154
+ "dosage": "Every 30 minutes to 2 hours in acute",
155
+ "duration": "24 hours maximum",
156
+ "aggravation": "Possible with repeated doses",
157
+ "antidotes": ["Coffea", "Camphor"],
158
+ "complementary": ["Arnica", "Belladonna", "Gelsemium"],
159
+ "miasm": "Acute",
160
+ "confidence": 0,
161
+ "match_reasons": []
162
+ },
163
+ "Chamomilla": {
164
+ "description": "Extreme irritability, oversensitivity to pain, especially in children",
165
+ "indications": ["Teething", "Colic", "Irritability", "Oversensitivity", "Childbirth"],
166
+ "modalities": {
167
+ "worse": ["Anger", "Night", "Heat", "Being looked at"],
168
+ "better": ["Being carried", "Cold", "Warm wet weather"]
169
+ },
170
+ "mental": "Extremely irritable, angry at pain, demands things then refuses, only calm when carried",
171
+ "physical": "One cheek red and hot, other pale, oversensitive to pain",
172
+ "constitutional": "Irritable, oversensitive persons, especially children",
173
+ "potencies": {
174
+ "acute": ["6C", "30C"],
175
+ "chronic": ["200C"],
176
+ "first_aid": "30C",
177
+ "recommended": "6C for teething, 30C for acute pain"
178
+ },
179
+ "dosage": "Every 15-30 minutes for acute pain",
180
+ "duration": "Until calm",
181
+ "aggravation": "Rare",
182
+ "antidotes": ["Coffea", "Camphor"],
183
+ "complementary": ["Belladonna", "Pulsatilla", "Nux Vomica"],
184
+ "miasm": "Psoric",
185
+ "confidence": 0,
186
+ "match_reasons": []
187
  }
188
  }
189
 
190
+ # ==================== ENHANCED ANALYSIS ENGINE ====================
191
+ class EnhancedHomeopathicAnalyzer:
192
  def __init__(self):
193
+ self.symptom_weights = {
194
+ "location": 15,
195
+ "sensation": 20,
196
+ "modality_worse": 25,
197
+ "modality_better": 25,
198
+ "emotional": 30,
199
+ "timing": 10,
200
+ "generalities": 15
201
+ }
202
 
203
+ self.potency_rules = {
204
+ "acute": {"intensity": "high", "duration": "short", "potency": "30C-200C"},
205
+ "subacute": {"intensity": "medium", "duration": "medium", "potency": "30C"},
206
+ "chronic": {"intensity": "low", "duration": "long", "potency": "200C-1M"},
207
+ "constitutional": {"intensity": "variable", "duration": "long", "potency": "1M-10M"}
208
+ }
209
+
210
+ def analyze_case(self, patient_data: Dict) -> Dict:
211
+ """Comprehensive case analysis with potency recommendations"""
212
 
213
  # Calculate remedy matches
214
+ matches = self.calculate_remedy_matches(patient_data)
215
+
216
+ # Determine case type
217
+ case_type = self.determine_case_type(patient_data)
218
+
219
+ # Generate potency recommendations
220
+ for match in matches:
221
+ match['potency_recommendation'] = self.recommend_potency(match, case_type, patient_data)
222
+ match['administration'] = self.generate_administration_plan(match, case_type)
223
+ match['differentiation'] = self.differentiate_remedies(matches, match['name'])
224
 
225
+ # Generate comprehensive report
226
+ report = self.generate_comprehensive_report(matches, patient_data, case_type)
227
 
228
+ return {
229
+ "matches": matches,
230
+ "report": report,
231
+ "case_type": case_type,
232
+ "confidence_level": self.calculate_confidence_level(matches)
233
+ }
234
 
235
+ def calculate_remedy_matches(self, patient_data: Dict) -> List[Dict]:
236
+ """Calculate remedy matches with detailed scoring"""
237
  matches = []
 
238
 
239
+ for remedy_name, remedy_data in REMEDY_DATABASE.items():
240
  score = 0
241
+ match_reasons = []
242
+
243
+ # Reset confidence and reasons
244
+ remedy_data = remedy_data.copy()
245
+ remedy_data['confidence'] = 0
246
+ remedy_data['match_reasons'] = []
247
 
248
+ # 1. Chief Complaint matching (30%)
249
+ chief_match = self.match_chief_complaint(patient_data, remedy_data)
250
+ score += chief_match['score']
251
+ if chief_match['score'] > 5:
252
+ match_reasons.extend(chief_match['reasons'])
253
 
254
+ # 2. Modality matching (40%)
255
+ modality_match = self.match_modalities(patient_data, remedy_data)
256
+ score += modality_match['score']
257
+ if modality_match['score'] > 5:
258
+ match_reasons.extend(modality_match['reasons'])
259
 
260
+ # 3. Emotional matching (20%)
261
+ emotional_match = self.match_emotional(patient_data, remedy_data)
262
+ score += emotional_match['score']
263
+ if emotional_match['score'] > 5:
264
+ match_reasons.extend(emotional_match['reasons'])
265
 
266
+ # 4. General symptoms matching (10%)
267
+ general_match = self.match_general_symptoms(patient_data, remedy_data)
268
+ score += general_match['score']
269
+ if general_match['score'] > 2:
270
+ match_reasons.extend(general_match['reasons'])
271
 
272
+ if score > 20: # Minimum threshold
273
+ confidence = min(99, score)
274
+ remedy_data['confidence'] = confidence
275
+ remedy_data['match_reasons'] = match_reasons[:5] # Top 5 reasons
276
+
277
  matches.append({
278
+ 'name': remedy_name,
279
+ 'data': remedy_data,
280
+ 'score': confidence,
281
+ 'detailed_scores': {
282
+ 'chief_complaint': chief_match['score'],
283
+ 'modalities': modality_match['score'],
284
+ 'emotional': emotional_match['score'],
285
+ 'general': general_match['score']
286
+ }
287
  })
288
 
289
  # Sort by confidence
290
  matches.sort(key=lambda x: x['score'], reverse=True)
291
+ return matches[:6]
292
 
293
+ def match_chief_complaint(self, patient_data: Dict, remedy_data: Dict) -> Dict:
294
+ """Match chief complaint with remedy indications"""
295
+ score = 0
296
+ reasons = []
297
+
298
+ complaint = patient_data.get('chief_complaint', '').lower()
299
+ if not complaint:
300
+ return {'score': 0, 'reasons': reasons}
301
+
302
+ # Check remedy indications
303
+ for indication in remedy_data['indications']:
304
+ indication_lower = indication.lower()
305
+
306
+ # Exact phrase match
307
+ if indication_lower in complaint:
308
+ score += 15
309
+ reasons.append(f"Chief complaint matches indication: {indication}")
310
+
311
+ # Word matching
312
+ indication_words = set(re.findall(r'\b\w{4,}\b', indication_lower))
313
+ complaint_words = set(re.findall(r'\b\w{4,}\b', complaint))
314
+
315
+ common_words = indication_words.intersection(complaint_words)
316
+ if common_words:
317
+ score += len(common_words) * 2
318
+ reasons.append(f"Keywords match: {', '.join(list(common_words)[:3])}")
319
+
320
+ # Check physical symptoms
321
+ physical = remedy_data['physical'].lower()
322
+ physical_words = set(re.findall(r'\b\w{4,}\b', physical))
323
+ common_physical = physical_words.intersection(complaint_words)
324
 
325
+ if common_physical:
326
+ score += len(common_physical) * 3
327
+ reasons.append(f"Physical symptoms match: {', '.join(list(common_physical)[:2])}")
328
 
329
+ return {'score': min(30, score), 'reasons': reasons}
 
330
 
331
+ def match_modalities(self, patient_data: Dict, remedy_data: Dict) -> Dict:
332
+ """Match modalities with detailed scoring"""
333
  score = 0
334
+ reasons = []
335
 
336
  # Check aggravations
337
+ patient_worse = patient_data.get('aggravations', '').lower()
338
+ remedy_worse = [w.lower() for w in remedy_data['modalities']['worse']]
339
 
340
+ if patient_worse:
341
+ for modality in remedy_worse:
342
+ if modality in patient_worse:
343
+ score += 10
344
+ reasons.append(f"Worse from: {modality}")
345
+ else:
346
+ # Check partial matches
347
+ modality_words = modality.split()
348
+ for word in modality_words:
349
+ if len(word) > 3 and word in patient_worse:
350
+ score += 5
351
+ reasons.append(f"Partially worse from: {word}")
352
+ break
353
 
354
  # Check ameliorations
355
+ patient_better = patient_data.get('ameliorations', '').lower()
356
+ remedy_better = [b.lower() for b in remedy_data['modalities']['better']]
357
 
358
+ if patient_better:
359
+ for modality in remedy_better:
360
+ if modality in patient_better:
361
+ score += 10
362
+ reasons.append(f"Better from: {modality}")
363
+ else:
364
+ # Check partial matches
365
+ modality_words = modality.split()
366
+ for word in modality_words:
367
+ if len(word) > 3 and word in patient_better:
368
+ score += 5
369
+ reasons.append(f"Partially better from: {word}")
370
+ break
371
 
372
+ # Timing matches
373
+ timing = patient_data.get('timing', '').lower()
374
+ if timing:
375
+ time_keywords = ['morning', 'evening', 'night', 'afternoon', '3 pm', 'midnight']
376
+ for time_word in time_keywords:
377
+ if time_word in timing and any(time_word in w for w in remedy_worse + remedy_better):
378
+ score += 5
379
+ reasons.append(f"Timing pattern matches: {time_word}")
380
+
381
+ return {'score': min(40, score), 'reasons': reasons}
382
 
383
+ def match_emotional(self, patient_data: Dict, remedy_data: Dict) -> Dict:
384
  """Match emotional state"""
385
+ score = 0
386
+ reasons = []
387
 
388
+ emotional = patient_data.get('emotional_state', '').lower()
389
  if not emotional:
390
+ return {'score': 0, 'reasons': reasons}
391
 
392
+ remedy_mental = remedy_data['mental'].lower()
 
393
 
394
+ # Extract emotional keywords
395
+ emotional_keywords = [
396
+ 'anxious', 'fearful', 'irritable', 'restless', 'weepy', 'apathetic',
397
+ 'angry', 'sad', 'depressed', 'panicked', 'frightened', 'nervous'
398
+ ]
399
 
400
+ patient_emotions = []
401
+ for keyword in emotional_keywords:
402
+ if keyword in emotional:
403
+ patient_emotions.append(keyword)
404
+
405
+ # Check remedy mental state
406
+ remedy_words = set(re.findall(r'\b\w{4,}\b', remedy_mental))
407
 
408
+ for emotion in patient_emotions:
409
+ if emotion in remedy_mental:
410
+ score += 8
411
+ reasons.append(f"Emotional state matches: {emotion}")
412
+ elif any(emotion in word for word in remedy_words):
413
+ score += 4
414
+ reasons.append(f"Emotional aspect matches: {emotion}")
415
 
416
+ # Constitution matching
417
+ constitution = patient_data.get('constitution', '').lower()
418
+ remedy_constitution = remedy_data['constitutional'].lower()
419
 
420
+ if constitution and any(word in remedy_constitution for word in constitution.split()):
421
+ score += 6
422
+ reasons.append("Constitutional type matches")
423
+
424
+ return {'score': min(20, score), 'reasons': reasons}
425
 
426
+ def match_general_symptoms(self, patient_data: Dict, remedy_data: Dict) -> Dict:
427
+ """Match general symptoms"""
428
+ score = 0
429
+ reasons = []
430
+
431
+ generalities = patient_data.get('generalities', '').lower()
432
+ if not generalities:
433
+ return {'score': 0, 'reasons': reasons}
434
+
435
+ # Check thermal preferences
436
+ thermal_keywords = {
437
+ 'worse cold': ['chilly', 'cold', 'worse cold', 'sensitive cold'],
438
+ 'better cold': ['better cold', 'likes cold', 'prefers cold'],
439
+ 'worse heat': ['worse heat', 'heat aggravates', 'hot'],
440
+ 'better heat': ['better heat', 'likes warmth', 'prefers warm']
441
  }
442
+
443
+ for thermal_type, keywords in thermal_keywords.items():
444
+ if any(keyword in generalities for keyword in keywords):
445
+ # Check if remedy has similar thermal
446
+ remedy_text = f"{remedy_data['physical']} {remedy_data['modalities']['worse']} {remedy_data['modalities']['better']}".lower()
447
+ if any(keyword in remedy_text for keyword in keywords):
448
+ score += 3
449
+ reasons.append(f"Thermal preference matches: {thermal_type}")
450
+
451
+ # Check thirst
452
+ if 'thirst' in generalities or 'thirsty' in generalities:
453
+ remedy_text = remedy_data['physical'].lower()
454
+ if 'thirst' in remedy_text or 'thirsty' in remedy_text:
455
+ score += 3
456
+ reasons.append("Thirst pattern matches")
457
+
458
+ return {'score': min(10, score), 'reasons': reasons}
459
 
460
+ def determine_case_type(self, patient_data: Dict) -> str:
461
+ """Determine if case is acute, subacute, or chronic"""
462
+ timing = patient_data.get('timing', '').lower()
463
+ intensity = patient_data.get('intensity', 5)
464
 
465
+ acute_keywords = ['sudden', 'acute', 'just started', 'today', 'yesterday']
466
+ chronic_keywords = ['chronic', 'long', 'months', 'years', 'always']
 
 
 
467
 
468
+ is_acute = any(keyword in timing for keyword in acute_keywords)
469
+ is_chronic = any(keyword in timing for keyword in chronic_keywords)
 
 
 
470
 
471
+ if is_acute and intensity >= 7:
472
+ return "acute"
473
+ elif is_chronic or (not is_acute and not is_chronic):
474
+ return "chronic"
475
+ else:
476
+ return "subacute"
477
 
478
+ def recommend_potency(self, match: Dict, case_type: str, patient_data: Dict) -> Dict:
479
+ """Recommend specific potency based on case analysis"""
480
+ remedy_data = match['data']
481
+ confidence = match['score']
482
 
483
+ recommendations = {
484
+ "primary_potency": "",
485
+ "alternative_potencies": [],
486
+ "frequency": "",
487
+ "duration": "",
488
+ "considerations": []
489
+ }
490
+
491
+ # Determine primary potency based on case type and confidence
492
+ if case_type == "acute":
493
+ if confidence > 80:
494
+ recommendations["primary_potency"] = "200C"
495
+ elif confidence > 60:
496
+ recommendations["primary_potency"] = "30C"
497
+ else:
498
+ recommendations["primary_potency"] = "6C"
499
+ recommendations["frequency"] = "Every 2-4 hours"
500
+ recommendations["duration"] = "24-48 hours"
501
+
502
+ elif case_type == "chronic":
503
+ if confidence > 80:
504
+ recommendations["primary_potency"] = "1M"
505
+ elif confidence > 60:
506
+ recommendations["primary_potency"] = "200C"
507
+ else:
508
+ recommendations["primary_potency"] = "30C"
509
+ recommendations["frequency"] = "Once daily or weekly"
510
+ recommendations["duration"] = "4-6 weeks"
511
+
512
+ else: # subacute
513
+ recommendations["primary_potency"] = "30C"
514
+ recommendations["frequency"] = "3 times daily"
515
+ recommendations["duration"] = "1-2 weeks"
516
+
517
+ # Add alternative potencies from remedy database
518
+ if case_type in remedy_data['potencies']:
519
+ alt_potencies = remedy_data['potencies'][case_type]
520
+ recommendations["alternative_potencies"] = [p for p in alt_potencies if p != recommendations["primary_potency"]]
521
+
522
+ # Add considerations
523
+ if remedy_data.get('aggravation'):
524
+ recommendations["considerations"].append("Monitor for initial aggravation")
525
+
526
+ if patient_data.get('intensity', 0) >= 8:
527
+ recommendations["considerations"].append("High intensity - may need more frequent dosing")
528
 
529
+ if "child" in patient_data.get('constitution', '').lower():
530
+ recommendations["considerations"].append("For children, consider lower potency (6C)")
531
 
532
  return recommendations
533
+
534
+ def generate_administration_plan(self, match: Dict, case_type: str) -> Dict:
535
+ """Generate detailed administration plan"""
536
+ remedy_data = match['data']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
537
 
538
+ plan = {
539
+ "dosage": remedy_data.get('dosage', '3-5 pellets sublingually'),
540
+ "timing": "Away from meals (15 minutes before or after)",
541
+ "instructions": "Allow pellets to dissolve under tongue, avoid touching with hands",
542
+ "cautions": [
543
+ "Avoid strong odors (coffee, mint, camphor)",
544
+ "Store in cool, dark place away from electronics"
545
+ ]
546
+ }
 
 
547
 
548
+ if case_type == "acute":
549
+ plan["additional_notes"] = "Stop when improvement begins, repeat only if symptoms return"
550
+ else:
551
+ plan["additional_notes"] = "Observe for 4-6 weeks before reassessment"
 
 
 
 
 
 
 
 
 
552
 
553
+ return plan
554
+
555
+ def differentiate_remedies(self, all_matches: List[Dict], current_remedy: str) -> List[str]:
556
+ """Provide differentiation from other similar remedies"""
557
+ differentiations = []
558
+
559
+ if len(all_matches) < 2:
560
+ return differentiations
561
+
562
+ current_data = REMEDY_DATABASE.get(current_remedy, {})
563
+
564
+ for match in all_matches[:4]: # Compare with top 4
565
+ if match['name'] != current_remedy:
566
+ other_data = REMEDY_DATABASE.get(match['name'], {})
567
+
568
+ # Find key differences
569
+ diff = self.find_key_differences(current_data, other_data)
570
+ if diff:
571
+ differentiations.append(f"Vs {match['name']}: {diff}")
572
+
573
+ return differentiations[:3] # Limit to 3 differentiations
574
+
575
+ def find_key_differences(self, remedy1: Dict, remedy2: Dict) -> str:
576
+ """Find key differences between two remedies"""
577
+ differences = []
578
+
579
+ # Compare modalities
580
+ worse1 = remedy1['modalities']['worse']
581
+ worse2 = remedy2['modalities']['worse']
582
+
583
+ unique_worse1 = [w for w in worse1 if w not in worse2]
584
+ unique_worse2 = [w for w in worse2 if w not in worse1]
585
+
586
+ if unique_worse1:
587
+ differences.append(f"Worse: {', '.join(unique_worse1[:2])}")
588
+
589
+ # Compare mental state
590
+ mental1_words = set(re.findall(r'\b\w{4,}\b', remedy1['mental'].lower()))
591
+ mental2_words = set(re.findall(r'\b\w{4,}\b', remedy2['mental'].lower()))
592
+
593
+ unique_mental1 = mental1_words - mental2_words
594
+ if unique_mental1:
595
+ differences.append(f"Mental: {', '.join(list(unique_mental1)[:2])}")
596
+
597
+ return "; ".join(differences) if differences else "Similar symptom picture"
598
+
599
+ def calculate_confidence_level(self, matches: List[Dict]) -> str:
600
+ """Calculate overall confidence level"""
601
+ if not matches:
602
+ return "Low"
603
+
604
+ top_score = matches[0]['score']
605
+
606
+ if top_score >= 80:
607
+ return "High"
608
+ elif top_score >= 60:
609
+ return "Moderate"
610
+ elif top_score >= 40:
611
+ return "Fair"
612
+ else:
613
+ return "Low"
614
+
615
+ def generate_comprehensive_report(self, matches: List[Dict], patient_data: Dict, case_type: str) -> Dict:
616
+ """Generate comprehensive analysis report"""
617
+ report = {
618
+ "summary": {
619
+ "case_type": case_type,
620
+ "top_remedy": matches[0]['name'] if matches else "None",
621
+ "confidence": matches[0]['score'] if matches else 0,
622
+ "analysis_date": datetime.now().strftime("%Y-%m-%d %H:%M")
623
+ },
624
+ "key_findings": [],
625
+ "recommendations": [],
626
+ "follow_up": []
627
+ }
628
+
629
+ if matches:
630
+ top_match = matches[0]
631
 
632
+ # Key findings
633
+ report["key_findings"].append(f"Top match: {top_match['name']} ({top_match['score']:.1f}% confidence)")
634
+ report["key_findings"].append(f"Case type: {case_type.capitalize()}")
635
+ report["key_findings"].append(f"Key modalities: {self.extract_key_modalities(patient_data)}")
636
+
637
+ # Recommendations
638
+ potency_rec = top_match.get('potency_recommendation', {})
639
+ if potency_rec:
640
+ report["recommendations"].append(f"Potency: {potency_rec.get('primary_potency', '30C')}")
641
+ report["recommendations"].append(f"Frequency: {potency_rec.get('frequency', '3 times daily')}")
642
+
643
+ admin_plan = top_match.get('administration', {})
644
+ if admin_plan:
645
+ report["recommendations"].append(f"Administration: {admin_plan.get('dosage', '3-5 pellets')}")
646
+
647
+ # Follow-up
648
+ report["follow_up"].append("Reassess in 48 hours for acute cases")
649
+ report["follow_up"].append("Monitor for remedy action or aggravation")
650
+ report["follow_up"].append("Consult practitioner if no improvement in 1 week")
651
 
652
+ return report
653
+
654
+ def extract_key_modalities(self, patient_data: Dict) -> str:
655
+ """Extract key modalities from patient data"""
656
+ modalities = []
657
+
658
+ if patient_data.get('aggravations'):
659
+ modalities.append(f"Worse: {patient_data['aggravations'][:50]}...")
660
+
661
+ if patient_data.get('ameliorations'):
662
+ modalities.append(f"Better: {patient_data['ameliorations'][:50]}...")
663
 
664
+ return "; ".join(modalities) if modalities else "Not specified"
665
 
666
+ # Initialize analyzer
667
+ analyzer = EnhancedHomeopathicAnalyzer()
668
+
669
+ # ==================== MEDICINE SEARCH PAGE ====================
670
+ def create_medicine_search_page():
671
+ """Create medicine search and details page"""
672
+
673
+ with gr.Blocks(title="Medicine Database") as medicine_page:
674
+ gr.Markdown("# ๐Ÿ” Homeopathic Medicine Database")
675
+ gr.Markdown("Search and explore detailed information about homeopathic remedies")
676
 
677
+ # Search Bar
678
  with gr.Row():
679
+ search_input = gr.Textbox(
680
+ label="Search Medicine",
681
+ placeholder="Type remedy name (e.g., Arnica, Belladonna)...",
682
+ scale=4
 
 
 
 
 
 
 
 
683
  )
684
+ search_btn = gr.Button("Search", variant="primary", scale=1)
685
 
686
+ # Search Results
687
+ search_results = gr.Dataframe(
688
+ label="Search Results",
689
+ headers=["Medicine", "Key Indications", "Common Potency"],
690
+ interactive=False,
691
+ visible=False
692
  )
693
 
694
+ # Medicine Details Display
695
+ medicine_details = gr.HTML(
696
+ label="Medicine Details",
697
+ elem_id="medicine-details"
 
 
 
 
 
 
 
698
  )
699
 
700
+ # Search function
701
+ def search_medicine(query):
702
+ """Search for medicine in database"""
703
+ if not query:
704
+ return gr.Dataframe(visible=False), ""
 
 
705
 
706
+ query_lower = query.lower()
707
+ results = []
708
+
709
+ for name, data in REMEDY_DATABASE.items():
710
+ if query_lower in name.lower() or any(query_lower in ind.lower() for ind in data['indications']):
711
+ results.append([
712
+ name,
713
+ ", ".join(data['indications'][:3]),
714
+ data['potencies'].get('first_aid', '30C')
715
+ ])
716
+
717
+ if results:
718
+ return gr.Dataframe(value=results, visible=True), ""
719
+ else:
720
+ return gr.Dataframe(visible=False), "<div style='text-align: center; padding: 20px; color: #666;'>No results found</div>"
721
 
722
+ # Display details function
723
+ def display_medicine_details(evt: gr.SelectData):
724
+ """Display detailed medicine information when selected"""
725
+ if evt.index[1] == 0: # Clicked on medicine name column
726
+ medicine_name = evt.value
727
+ return generate_medicine_details_html(medicine_name)
728
+ return ""
729
 
730
+ # Connect events
731
+ search_input.submit(
732
+ fn=search_medicine,
733
+ inputs=[search_input],
734
+ outputs=[search_results, medicine_details]
735
+ )
736
 
737
+ search_btn.click(
738
+ fn=search_medicine,
739
+ inputs=[search_input],
740
+ outputs=[search_results, medicine_details]
741
+ )
 
 
 
 
 
 
 
742
 
743
+ search_results.select(
744
+ fn=display_medicine_details,
745
+ inputs=None,
746
+ outputs=[medicine_details]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
747
  )
748
 
749
+ return medicine_page
750
 
751
+ def generate_medicine_details_html(medicine_name: str) -> str:
752
+ """Generate detailed HTML for medicine display"""
753
+ if medicine_name not in REMEDY_DATABASE:
754
+ return "<div style='color: #666; text-align: center; padding: 40px;'>Medicine not found</div>"
 
 
 
 
 
 
 
755
 
756
+ data = REMEDY_DATABASE[medicine_name]
 
757
 
758
  html = f"""
759
  <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
760
 
761
+ <!-- Medicine Header -->
762
  <div style="background: linear-gradient(135deg, #1e40af 0%, #1e3a8a 100%);
763
  padding: 24px; border-radius: 12px; margin-bottom: 24px; color: white;">
764
+ <h2 style="margin: 0; font-size: 24px; font-weight: 600;">{medicine_name}</h2>
765
+ <p style="margin: 8px 0 0 0; opacity: 0.9; font-size: 16px;">{data['description']}</p>
 
 
 
 
 
 
 
 
 
 
766
  </div>
767
 
768
+ <!-- Key Information Grid -->
769
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 24px;">
 
 
 
 
 
 
 
 
 
 
 
 
 
770
 
771
+ <!-- Indications -->
772
+ <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
773
+ <h3 style="color: #1e40af; margin: 0 0 12px 0; font-size: 18px; display: flex; align-items: center;">
774
+ <span style="margin-right: 8px;">๐ŸŽฏ</span> Key Indications
775
+ </h3>
776
+ <ul style="margin: 0; padding-left: 20px;">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
777
  """
778
 
779
+ for indication in data['indications'][:6]:
780
+ html += f"<li style='margin-bottom: 6px; color: #4b5563;'>{indication}</li>"
 
 
 
 
 
781
 
782
  html += """
783
+ </ul>
784
+ </div>
785
+
786
+ <!-- Modalities -->
787
+ <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
788
+ <h3 style="color: #1e40af; margin: 0 0 12px 0; font-size: 18px; display: flex; align-items: center;">
789
+ <span style="margin-right: 8px;">โš–๏ธ</span> Modalities
790
+ </h3>
791
+ <div style="margin-bottom: 16px;">
792
+ <div style="color: #ef4444; font-weight: 600; margin-bottom: 4px;">Worse:</div>
793
+ <div style="color: #6b7280;">"""
794
+
795
+ html += ', '.join(data['modalities']['worse'])
796
+
797
+ html += """</div>
798
  </div>
 
 
799
  <div>
800
+ <div style="color: #10b981; font-weight: 600; margin-bottom: 4px;">Better:</div>
801
+ <div style="color: #6b7280;">"""
 
 
 
 
 
802
 
803
+ html += ', '.join(data['modalities']['better'])
804
 
805
+ html += """</div>
806
+ </div>
807
+ </div>
808
+
809
+ <!-- Potency Guide -->
810
+ <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
811
+ <h3 style="color: #1e40af; margin: 0 0 12px 0; font-size: 18px; display: flex; align-items: center;">
812
+ <span style="margin-right: 8px;">๐Ÿ’Š</span> Potency Guide
813
+ </h3>
814
+ <div style="display: grid; gap: 12px;">
815
+ """
816
 
817
+ for case_type, potencies in data['potencies'].items():
818
+ if case_type != 'recommended':
819
+ html += f"""
820
+ <div style="background: #f8fafc; padding: 12px; border-radius: 8px;">
821
+ <div style="font-weight: 600; color: #4b5563; text-transform: capitalize;">{case_type}:</div>
822
+ <div style="color: #6b7280;">"""
823
+
824
+ if isinstance(potencies, list):
825
+ html += ', '.join(potencies)
826
+ else:
827
+ html += potencies
828
+
829
+ html += "</div></div>"
830
 
831
+ html += f"""
832
+ </div>
833
+ <div style="margin-top: 16px; padding: 12px; background: #f0f9ff; border-radius: 8px; border-left: 4px solid #0ea5e9;">
834
+ <div style="font-weight: 600; color: #0369a1;">Recommended:</div>
835
+ <div style="color: #0c4a6e;">{data['potencies'].get('recommended', '30C for acute cases')}</div>
836
  </div>
 
837
  </div>
838
+ </div>
839
+
840
+ <!-- Additional Information -->
841
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 24px;">
842
 
843
+ <!-- Mental/Emotional -->
844
+ <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
845
+ <h3 style="color: #1e40af; margin: 0 0 12px 0; font-size: 18px;">๐Ÿ’ญ Mental State</h3>
846
+ <div style="color: #4b5563; line-height: 1.5;">{data['mental']}</div>
847
+ </div>
848
+
849
+ <!-- Physical Symptoms -->
850
+ <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
851
+ <h3 style="color: #1e40af; margin: 0 0 12px 0; font-size: 18px;">๐Ÿ“ Physical Symptoms</h3>
852
+ <div style="color: #4b5563; line-height: 1.5;">{data['physical']}</div>
853
+ </div>
854
+
855
+ <!-- Constitutional -->
856
+ <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
857
+ <h3 style="color: #1e40af; margin: 0 0 12px 0; font-size: 18px;">๐Ÿงฌ Constitutional Type</h3>
858
+ <div style="color: #4b5563; line-height: 1.5;">{data['constitutional']}</div>
859
+ <div style="margin-top: 12px;">
860
+ <div style="font-weight: 600; color: #4b5563;">Miasmatic Influence:</div>
861
+ <div style="color: #6b7280;">{data.get('miasm', 'Not specified')}</div>
862
  </div>
863
  </div>
864
 
865
  </div>
 
 
 
 
 
 
 
 
 
866
 
867
+ <!-- Administration & Cautions -->
868
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-bottom: 24px;">
869
 
870
+ <!-- Administration -->
871
+ <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
872
+ <h3 style="color: #1e40af; margin: 0 0 12px 0; font-size: 18px;">๐Ÿ“‹ Administration</h3>
873
+ <div style="color: #4b5563; line-height: 1.5;">{data.get('dosage', '3-5 pellets sublingually')}</div>
874
+ <div style="margin-top: 12px;">
875
+ <div style="font-weight: 600; color: #4b5563;">Duration:</div>
876
+ <div style="color: #6b7280;">{data.get('duration', '48 hours for acute, 4-6 weeks for chronic')}</div>
 
 
877
  </div>
 
 
878
  </div>
879
+
880
+ <!-- Cautions -->
881
+ <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
882
+ <h3 style="color: #1e40af; margin: 0 0 12px 0; font-size: 18px;">โš ๏ธ Cautions</h3>
883
+ <div style="color: #4b5563; line-height: 1.5;">
884
+ <div style="margin-bottom: 8px;"><strong>Aggravation:</strong> {data.get('aggravation', 'Possible initial aggravation')}</div>
885
+ <div style="margin-bottom: 8px;"><strong>Antidotes:</strong> {data.get('antidotes', 'Camphor, Coffee')}</div>
886
+ <div><strong>Complementary:</strong> {data.get('complementary', 'Various based on case')}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
887
  </div>
888
  </div>
889
+
890
  </div>
891
+
 
892
  </div>
893
  """
894
 
895
  return html
896
 
897
+ # ==================== MAIN APPLICATION WITH TABS ====================
898
  with gr.Blocks(
899
+ title="Homeopathic Analyzer Pro | Enhanced Analysis",
900
  theme=gr.themes.Soft(
901
  primary_hue="blue",
902
  secondary_hue="slate",
903
  font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"]
904
  ),
905
  css="""
 
906
  .gradio-container {
907
+ max-width: 1400px !important;
908
  margin: 0 auto !important;
909
+ font-family: 'Inter', -apple-system, sans-serif !important;
 
910
  }
911
 
912
+ /* Header */
913
+ .main-header {
914
+ background: linear-gradient(135deg, #1e40af 0%, #1e3a8a 100%) !important;
915
+ padding: 32px !important;
916
+ color: white !important;
917
+ border-radius: 0 0 20px 20px !important;
918
+ margin: -20px -20px 30px -20px !important;
919
+ }
920
+
921
+ /* Tabs */
922
+ .tab-nav {
923
+ display: flex !important;
924
+ gap: 2px !important;
925
+ margin-bottom: 30px !important;
926
+ background: #f3f4f6 !important;
927
+ padding: 4px !important;
928
+ border-radius: 12px !important;
929
+ }
930
+
931
+ .tab-button {
932
+ flex: 1 !important;
933
+ padding: 16px !important;
934
+ border: none !important;
935
+ background: transparent !important;
936
+ font-weight: 500 !important;
937
+ color: #6b7280 !important;
938
+ border-radius: 8px !important;
939
+ cursor: pointer !important;
940
+ transition: all 0.2s ease !important;
941
+ }
942
+
943
+ .tab-button:hover {
944
+ background: #e5e7eb !important;
945
+ }
946
+
947
+ .tab-button.active {
948
+ background: white !important;
949
+ color: #1e40af !important;
950
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
951
  }
952
 
953
  /* Cards */
954
+ .analysis-card {
955
  background: white !important;
956
  border-radius: 12px !important;
957
+ padding: 28px !important;
958
+ margin-bottom: 24px !important;
959
  border: 1px solid #e5e7eb !important;
960
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05) !important;
 
 
 
 
 
 
961
  }
962
 
963
  /* Form Elements */
964
  .form-section {
965
+ margin-bottom: 28px !important;
966
  }
967
 
968
  .form-label {
 
988
  }
989
 
990
  /* Buttons */
991
+ .primary-btn {
992
  background: linear-gradient(135deg, #1e40af 0%, #1e3a8a 100%) !important;
993
  color: white !important;
994
  border: none !important;
 
1001
  transition: all 0.3s ease !important;
1002
  }
1003
 
1004
+ .primary-btn:hover {
1005
  transform: translateY(-2px) !important;
1006
  box-shadow: 0 8px 16px rgba(30, 64, 175, 0.2) !important;
1007
  }
1008
 
1009
+ .search-btn {
1010
+ background: #10b981 !important;
1011
+ color: white !important;
1012
+ border: none !important;
1013
+ padding: 12px 24px !important;
1014
+ border-radius: 8px !important;
1015
+ font-weight: 500 !important;
 
 
 
1016
  }
1017
 
1018
+ /* Results */
1019
+ .results-container {
1020
+ margin-top: 30px !important;
1021
  animation: fadeIn 0.5s ease !important;
1022
  }
1023
 
 
1026
  to { opacity: 1; transform: translateY(0); }
1027
  }
1028
 
1029
+ /* Potency Badges */
1030
+ .potency-badge {
1031
+ display: inline-block !important;
1032
+ padding: 6px 12px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1033
  border-radius: 20px !important;
1034
+ font-size: 13px !important;
1035
  font-weight: 500 !important;
1036
+ margin: 2px !important;
1037
  }
1038
 
1039
+ .potency-high {
1040
  background: #d1fae5 !important;
1041
  color: #065f46 !important;
1042
  }
1043
 
1044
+ .potency-medium {
1045
  background: #fef3c7 !important;
1046
  color: #92400e !important;
1047
  }
1048
 
1049
+ .potency-low {
1050
+ background: #e5e7eb !important;
1051
  color: #6b7280 !important;
1052
  }
1053
+
1054
+ /* Grid Layout */
1055
+ .grid-2 {
1056
+ display: grid !important;
1057
+ grid-template-columns: 1fr 1fr !important;
1058
+ gap: 20px !important;
1059
+ }
1060
+
1061
+ .grid-3 {
1062
+ display: grid !important;
1063
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) !important;
1064
+ gap: 20px !important;
1065
+ }
1066
+
1067
+ @media (max-width: 768px) {
1068
+ .grid-2, .grid-3 {
1069
+ grid-template-columns: 1fr !important;
1070
+ }
1071
+ }
1072
  """
1073
  ) as app:
1074
 
1075
  # Header
1076
+ gr.Markdown("""
1077
+ <div class="main-header">
1078
+ <div style="display: flex; align-items: center; justify-content: space-between;">
1079
+ <div>
1080
+ <h1 style="margin: 0; font-size: 32px; font-weight: 700;">๐Ÿฅ Homeopathic Analyzer Pro</h1>
1081
+ <p style="margin: 8px 0 0 0; opacity: 0.9; font-size: 16px;">
1082
+ Advanced AI-powered homeopathic analysis with potency recommendations
1083
+ </p>
1084
+ </div>
1085
+ <div style="font-size: 40px;">๐ŸŒฟ</div>
1086
+ </div>
1087
+ </div>
1088
+ """)
1089
+
1090
+ # Tab Navigation
1091
+ with gr.Row(elem_classes="tab-nav"):
1092
+ analysis_tab = gr.Button("Symptom Analysis", elem_classes="tab-button active")
1093
+ medicine_tab = gr.Button("Medicine Database", elem_classes="tab-button")
1094
+
1095
+ # Main Content Area
1096
+ with gr.Column(elem_id="main-content"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1097
 
1098
+ # ========== SYMPTOM ANALYSIS TAB ==========
1099
+ with gr.Group(visible=True, elem_id="analysis-tab"):
1100
+
1101
+ # Patient Information
1102
+ with gr.Group(elem_classes="analysis-card"):
1103
+ gr.Markdown("### ๐Ÿ‘ค Patient Information")
1104
+
1105
+ with gr.Row():
1106
+ age = gr.Slider(label="Age", minimum=0, maximum=120, value=35, step=1)
1107
+ gender = gr.Dropdown(label="Gender", choices=["Male", "Female", "Other"], value="Male")
1108
+ constitution = gr.Dropdown(
1109
+ label="Constitutional Type",
1110
+ choices=["Lean/Nervous", "Average", "Robust/Plethoric", "Not sure"],
1111
+ value="Average"
1112
+ )
1113
+
1114
+ # Symptom Assessment
1115
+ with gr.Group(elem_classes="analysis-card"):
1116
+ gr.Markdown("### ๐Ÿฉบ Symptom Assessment")
1117
+
1118
+ chief_complaint = gr.Textbox(
1119
+ label="Chief Complaint",
1120
+ placeholder="Describe your main health concern...",
1121
+ lines=3
1122
+ )
1123
+
1124
+ with gr.Row():
1125
+ location = gr.Textbox(
1126
+ label="Location",
1127
+ placeholder="Where exactly are symptoms?",
1128
+ lines=2
1129
+ )
1130
+ sensation = gr.Textbox(
1131
+ label="Sensation",
1132
+ placeholder="What does it feel like?",
1133
+ lines=2
1134
+ )
1135
+
1136
+ intensity = gr.Slider(label="Intensity (1-10)", min=1, max=10, value=5)
1137
+
1138
+ # Modalities
1139
+ with gr.Group(elem_classes="analysis-card"):
1140
+ gr.Markdown("### โš–๏ธ Modalities")
1141
+
1142
+ with gr.Row():
1143
+ aggravations = gr.Textbox(
1144
+ label="Aggravating Factors",
1145
+ placeholder="What makes it worse?",
1146
+ lines=2
1147
+ )
1148
+ ameliorations = gr.Textbox(
1149
+ label="Ameliorating Factors",
1150
+ placeholder="What makes it better?",
1151
+ lines=2
1152
+ )
1153
+
1154
+ timing = gr.Textbox(
1155
+ label="Timing & Periodicity",
1156
+ placeholder="When do symptoms occur?",
1157
+ lines=2
1158
+ )
1159
+
1160
+ # Emotional State
1161
+ with gr.Group(elem_classes="analysis-card"):
1162
+ gr.Markdown("### ๐Ÿ’ญ Emotional & Mental State")
1163
+
1164
+ emotional_state = gr.Textbox(
1165
+ label="Emotional State",
1166
+ placeholder="Describe emotions, fears, mental symptoms...",
1167
+ lines=2
1168
+ )
1169
+
1170
+ generalities = gr.Textbox(
1171
+ label="General Symptoms",
1172
+ placeholder="Thermal preferences, food desires/aversions...",
1173
+ lines=2
1174
+ )
1175
+
1176
+ # Analysis Button
1177
+ analyze_btn = gr.Button(
1178
+ "๐Ÿš€ Start Enhanced Analysis",
1179
+ variant="primary",
1180
+ elem_classes="primary-btn"
1181
+ )
1182
+
1183
+ # Results Display
1184
+ analysis_results = gr.HTML(
1185
+ label="Analysis Results",
1186
+ elem_classes="results-container"
1187
+ )
1188
+
1189
+ # Analysis Function
1190
+ def perform_enhanced_analysis(
1191
+ age_val, gender_val, constitution_val,
1192
+ complaint_val, location_val, sensation_val, intensity_val,
1193
+ aggravations_val, ameliorations_val, timing_val,
1194
+ emotional_val, generalities_val
1195
+ ):
1196
+ """Perform enhanced analysis"""
1197
+
1198
+ patient_data = {
1199
+ 'age': age_val,
1200
+ 'gender': gender_val,
1201
+ 'constitution': constitution_val,
1202
+ 'chief_complaint': complaint_val,
1203
+ 'location': location_val,
1204
+ 'sensation': sensation_val,
1205
+ 'intensity': intensity_val,
1206
+ 'aggravations': aggravations_val,
1207
+ 'ameliorations': ameliorations_val,
1208
+ 'timing': timing_val,
1209
+ 'emotional_state': emotional_val,
1210
+ 'generalities': generalities_val
1211
+ }
1212
+
1213
+ # Perform analysis
1214
+ analysis_result = analyzer.analyze_case(patient_data)
1215
+ matches = analysis_result['matches']
1216
+
1217
+ # Generate results HTML
1218
+ return generate_enhanced_results_html(matches, analysis_result, patient_data)
1219
+
1220
+ # Connect button
1221
+ analyze_btn.click(
1222
+ fn=perform_enhanced_analysis,
1223
  inputs=[
1224
  age, gender, constitution,
1225
  chief_complaint, location, sensation, intensity,
1226
  aggravations, ameliorations, timing,
1227
+ emotional_state, generalities
 
1228
  ],
1229
+ outputs=analysis_results
 
 
1230
  )
1231
+
1232
+ # ========== MEDICINE DATABASE TAB ==========
1233
+ with gr.Group(visible=False, elem_id="medicine-tab"):
1234
+ # Create medicine search page
1235
+ medicine_page = create_medicine_search_page()
1236
+
1237
+ # Tab Switching Logic
1238
+ def switch_to_analysis():
1239
+ return [
1240
+ gr.Group(visible=True), # analysis-tab
1241
+ gr.Group(visible=False), # medicine-tab
1242
+ gr.Button(variant="primary"), # analysis_tab
1243
+ gr.Button(variant="secondary") # medicine_tab
1244
+ ]
1245
+
1246
+ def switch_to_medicine():
1247
+ return [
1248
+ gr.Group(visible=False), # analysis-tab
1249
+ gr.Group(visible=True), # medicine-tab
1250
+ gr.Button(variant="secondary"), # analysis_tab
1251
+ gr.Button(variant="primary") # medicine_tab
1252
+ ]
1253
+
1254
+ analysis_tab.click(
1255
+ fn=switch_to_analysis,
1256
+ outputs=[gr.Group(elem_id="analysis-tab"), gr.Group(elem_id="medicine-tab"), analysis_tab, medicine_tab]
1257
+ )
1258
+
1259
+ medicine_tab.click(
1260
+ fn=switch_to_medicine,
1261
+ outputs=[gr.Group(elem_id="analysis-tab"), gr.Group(elem_id="medicine-tab"), analysis_tab, medicine_tab]
1262
+ )
1263
 
1264
+ def generate_enhanced_results_html(matches, analysis_result, patient_data):
1265
+ """Generate enhanced results HTML with potency recommendations"""
1266
+
1267
+ if not matches:
1268
+ return """
1269
+ <div style="text-align: center; padding: 40px; background: #f8f9fa; border-radius: 12px;">
1270
+ <div style="font-size: 48px; margin-bottom: 20px;">๐Ÿ”</div>
1271
+ <h3 style="color: #6b7280; margin-bottom: 10px;">No Clear Matches Found</h3>
1272
+ <p style="color: #9ca3af;">Try providing more specific symptom details.</p>
1273
+ </div>
1274
+ """
1275
+
1276
+ top_match = matches[0]
1277
+ case_type = analysis_result.get('case_type', 'acute')
1278
+
1279
+ html = f"""
1280
+ <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
1281
+
1282
+ <!-- Report Header -->
1283
+ <div style="background: linear-gradient(135deg, #1e40af 0%, #1e3a8a 100%);
1284
+ padding: 28px; border-radius: 12px; margin-bottom: 24px; color: white;">
1285
+ <div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 20px;">
1286
+ <div>
1287
+ <h2 style="margin: 0; font-size: 24px; font-weight: 600;">Enhanced Homeopathic Analysis</h2>
1288
+ <p style="margin: 8px 0 0 0; opacity: 0.9; font-size: 15px;">
1289
+ Case Type: <strong>{case_type.upper()}</strong> โ€ข Confidence: <strong>{analysis_result['confidence_level'].upper()}</strong>
1290
+ </p>
1291
+ </div>
1292
+ <div style="display: flex; gap: 16px;">
1293
+ <div style="background: rgba(255,255,255,0.15); padding: 12px 20px; border-radius: 10px; text-align: center;">
1294
+ <div style="font-size: 12px; opacity: 0.8;">Top Match</div>
1295
+ <div style="font-size: 20px; font-weight: 700;">{top_match['name']}</div>
1296
+ </div>
1297
+ <div style="background: rgba(255,255,255,0.15); padding: 12px 20px; border-radius: 10px; text-align: center;">
1298
+ <div style="font-size: 12px; opacity: 0.8;">Confidence</div>
1299
+ <div style="font-size: 20px; font-weight: 700;">{top_match['score']:.1f}%</div>
1300
+ </div>
1301
+ </div>
1302
+ </div>
1303
+ </div>
1304
+
1305
+ <!-- Primary Recommendation -->
1306
+ <div style="background: white; border-radius: 12px; padding: 28px; margin-bottom: 24px;
1307
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05); border: 1px solid #e5e7eb;">
1308
+
1309
+ <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px; flex-wrap: wrap; gap: 16px;">
1310
+ <div style="display: flex; align-items: center; gap: 16px;">
1311
+ <div style="width: 60px; height: 60px; background: linear-gradient(135deg, #10b981, #059669);
1312
+ border-radius: 12px; display: flex; align-items: center; justify-content: center;
1313
+ color: white; font-size: 28px; font-weight: 700;">
1314
+ 1
1315
+ </div>
1316
+ <div>
1317
+ <h3 style="margin: 0; color: #1f2937; font-size: 22px; font-weight: 600;">{top_match['name']}</h3>
1318
+ <p style="margin: 4px 0 0 0; color: #6b7280; font-size: 16px;">{top_match['data']['description']}</p>
1319
+ </div>
1320
+ </div>
1321
+ <div style="display: flex; gap: 12px;">
1322
+ <div style="background: #f0f9ff; color: #0369a1; padding: 8px 16px; border-radius: 20px; font-weight: 600; font-size: 14px;">
1323
+ Case: {case_type.upper()}
1324
+ </div>
1325
+ <div style="background: #d1fae5; color: #065f46; padding: 8px 16px; border-radius: 20px; font-weight: 600; font-size: 14px;">
1326
+ Match: {top_match['score']:.1f}%
1327
+ </div>
1328
+ </div>
1329
+ </div>
1330
+
1331
+ <!-- Potency Recommendations -->
1332
+ <div style="margin-bottom: 24px;">
1333
+ <h4 style="color: #1e40af; margin: 0 0 16px 0; font-size: 18px; display: flex; align-items: center;">
1334
+ <span style="margin-right: 8px;">๐Ÿ’Š</span> Potency Recommendations
1335
+ </h4>
1336
+
1337
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px;">
1338
+ """
1339
+
1340
+ potency_rec = top_match.get('potency_recommendation', {})
1341
+
1342
+ if potency_rec:
1343
+ html += f"""
1344
+ <div style="background: #f0f9ff; border-radius: 10px; padding: 20px; border-left: 4px solid #0ea5e9;">
1345
+ <div style="font-weight: 600; color: #0369a1; margin-bottom: 8px;">Primary Potency</div>
1346
+ <div style="font-size: 24px; font-weight: 700; color: #1e40af; margin-bottom: 4px;">{potency_rec.get('primary_potency', '30C')}</div>
1347
+ <div style="color: #6b7280; font-size: 14px;">For {case_type} cases</div>
1348
+ </div>
1349
+
1350
+ <div style="background: #f8fafc; border-radius: 10px; padding: 20px;">
1351
+ <div style="font-weight: 600; color: #4b5563; margin-bottom: 8px;">Administration</div>
1352
+ <div style="color: #1f2937; font-size: 16px; font-weight: 500; margin-bottom: 4px;">{potency_rec.get('frequency', '3 times daily')}</div>
1353
+ <div style="color: #6b7280; font-size: 14px;">Duration: {potency_rec.get('duration', '1-2 weeks')}</div>
1354
+ </div>
1355
+
1356
+ <div style="background: #f8fafc; border-radius: 10px; padding: 20px;">
1357
+ <div style="font-weight: 600; color: #4b5563; margin-bottom: 8px;">Alternative Potencies</div>
1358
+ <div style="display: flex; gap: 8px; flex-wrap: wrap;">
1359
+ """
1360
+
1361
+ for alt_potency in potency_rec.get('alternative_potencies', [])[:3]:
1362
+ html += f'<span class="potency-badge potency-medium">{alt_potency}</span>'
1363
+
1364
+ html += """
1365
+ </div>
1366
+ </div>
1367
+ """
1368
+
1369
+ html += """
1370
+ </div>
1371
+ </div>
1372
+
1373
+ <!-- Key Match Reasons -->
1374
+ <div style="margin-bottom: 24px;">
1375
+ <h4 style="color: #1e40af; margin: 0 0 12px 0; font-size: 18px;">๐ŸŽฏ Why This Remedy?</h4>
1376
+ <div style="background: #f8fafc; border-radius: 10px; padding: 20px;">
1377
+ <ul style="margin: 0; padding-left: 20px;">
1378
+ """
1379
+
1380
+ for reason in top_match['data'].get('match_reasons', [])[:5]:
1381
+ html += f'<li style="margin-bottom: 8px; color: #4b5563;">{reason}</li>'
1382
+
1383
+ html += """
1384
+ </ul>
1385
+ </div>
1386
+ </div>
1387
+
1388
+ <!-- Administration Details -->
1389
+ <div>
1390
+ <h4 style="color: #1e40af; margin: 0 0 12px 0; font-size: 18px;">๐Ÿ“‹ Administration Details</h4>
1391
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px;">
1392
+ """
1393
+
1394
+ admin_plan = top_match.get('administration', {})
1395
+ if admin_plan:
1396
+ html += f"""
1397
+ <div style="background: #f8fafc; padding: 16px; border-radius: 8px;">
1398
+ <div style="font-weight: 600; color: #4b5563; margin-bottom: 4px;">Dosage</div>
1399
+ <div style="color: #6b7280;">{admin_plan.get('dosage', '3-5 pellets')}</div>
1400
+ </div>
1401
+ <div style="background: #f8fafc; padding: 16px; border-radius: 8px;">
1402
+ <div style="font-weight: 600; color: #4b5563; margin-bottom: 4px;">Timing</div>
1403
+ <div style="color: #6b7280;">{admin_plan.get('timing', 'Away from meals')}</div>
1404
+ </div>
1405
+ <div style="background: #f8fafc; padding: 16px; border-radius: 8px;">
1406
+ <div style="font-weight: 600; color: #4b5563; margin-bottom: 4px;">Instructions</div>
1407
+ <div style="color: #6b7280;">{admin_plan.get('instructions', 'Dissolve under tongue')}</div>
1408
+ </div>
1409
+ """
1410
+
1411
+ html += """
1412
+ </div>
1413
+ </div>
1414
+
1415
+ </div>
1416
+
1417
+ <!-- Alternative Remedies -->
1418
+ <div style="background: white; border-radius: 12px; padding: 28px; margin-bottom: 24px;
1419
+ box-shadow: 0 2px 8px rgba(0,0,0,0.05); border: 1px solid #e5e7eb;">
1420
+
1421
+ <h3 style="color: #1f2937; margin: 0 0 20px 0; font-size: 20px; font-weight: 600;">Alternative Considerations</h3>
1422
+
1423
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px;">
1424
+ """
1425
+
1426
+ for i, match in enumerate(matches[1:5], 2):
1427
+ color = "#f59e0b" if match['score'] > 60 else "#6b7280"
1428
+
1429
+ html += f"""
1430
+ <div style="border: 1px solid #e5e7eb; border-radius: 10px; padding: 20px; transition: all 0.2s ease;">
1431
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
1432
+ <div style="display: flex; align-items: center; gap: 12px;">
1433
+ <div style="width: 36px; height: 36px; background: {color}20; color: {color};
1434
+ border-radius: 8px; display: flex; align-items: center; justify-content: center;
1435
+ font-weight: 700;">
1436
+ {i}
1437
+ </div>
1438
+ <div style="font-weight: 600; color: #1f2937;">{match['name']}</div>
1439
+ </div>
1440
+ <div style="background: {color}10; color: {color}; padding: 4px 12px; border-radius: 20px;
1441
+ font-size: 13px; font-weight: 500;">
1442
+ {match['score']:.1f}%
1443
+ </div>
1444
+ </div>
1445
+ <div style="color: #6b7280; font-size: 14px; margin-bottom: 12px;">{match['data']['description']}</div>
1446
+ <div style="display: flex; gap: 8px; flex-wrap: wrap;">
1447
+ """
1448
+
1449
+ for indication in match['data']['indications'][:2]:
1450
+ html += f'<span style="background: #f3f4f6; color: #6b7280; padding: 4px 8px; border-radius: 6px; font-size: 12px;">{indication}</span>'
1451
+
1452
+ html += """
1453
+ </div>
1454
+ </div>
1455
+ """
1456
+
1457
+ html += """
1458
+ </div>
1459
+ </div>
1460
+
1461
+ <!-- Clinical Notes -->
1462
+ <div style="background: #f0f9ff; border-radius: 12px; padding: 24px; margin-bottom: 24px; border: 1px solid #bae6fd;">
1463
+ <h3 style="color: #0369a1; margin: 0 0 16px 0; font-size: 18px;">๐Ÿ“ Clinical Notes & Cautions</h3>
1464
+
1465
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px;">
1466
+ """
1467
+
1468
+ notes = [
1469
+ ("Monitoring", "Observe for remedy action within 24-48 hours"),
1470
+ ("Aggravation", "Possible initial aggravation indicates remedy action"),
1471
+ ("Duration", f"Continue for {potency_rec.get('duration', '1-2 weeks')} before reassessment"),
1472
+ ("Follow-up", "Consult practitioner if no improvement in 1 week"),
1473
+ ("Contraindications", "Discontinue if symptoms worsen significantly"),
1474
+ ("Storage", "Store in cool, dark place away from strong odors")
1475
+ ]
1476
+
1477
+ for title, content in notes:
1478
+ html += f"""
1479
+ <div style="background: white; padding: 16px; border-radius: 8px; border-left: 4px solid #0ea5e9;">
1480
+ <div style="font-weight: 600; color: #0c4a6e; margin-bottom: 4px;">{title}</div>
1481
+ <div style="color: #475569; font-size: 14px;">{content}</div>
1482
+ </div>
1483
+ """
1484
+
1485
+ html += """
1486
+ </div>
1487
+ </div>
1488
+
1489
+ <!-- Detailed Scoring -->
1490
+ <div style="background: #f8fafc; border-radius: 12px; padding: 24px; margin-bottom: 24px; border: 1px solid #e5e7eb;">
1491
+ <h3 style="color: #1f2937; margin: 0 0 16px 0; font-size: 18px;">๐Ÿ“Š Match Analysis Breakdown</h3>
1492
+
1493
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px;">
1494
+ """
1495
+
1496
+ scores = top_match.get('detailed_scores', {})
1497
+ for category, score in scores.items():
1498
+ percentage = (score / 100) * 100
1499
+ color = "#10b981" if percentage > 60 else "#f59e0b" if percentage > 40 else "#ef4444"
1500
+
1501
+ html += f"""
1502
+ <div style="text-align: center;">
1503
+ <div style="font-weight: 600; color: #4b5563; margin-bottom: 8px; text-transform: capitalize;">{category.replace('_', ' ')}</div>
1504
+ <div style="width: 80px; height: 80px; margin: 0 auto 8px auto; position: relative;">
1505
+ <svg width="80" height="80" viewBox="0 0 80 80">
1506
+ <circle cx="40" cy="40" r="36" stroke="#e5e7eb" stroke-width="8" fill="none"/>
1507
+ <circle cx="40" cy="40" r="36" stroke="{color}" stroke-width="8" fill="none"
1508
+ stroke-linecap="round" stroke-dasharray="{percentage * 2.26} 226"
1509
+ transform="rotate(-90 40 40)"/>
1510
+ </svg>
1511
+ <div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
1512
+ font-weight: 700; color: {color}; font-size: 18px;">{score:.0f}</div>
1513
+ </div>
1514
+ </div>
1515
+ """
1516
+
1517
+ html += """
1518
+ </div>
1519
+ </div>
1520
+
1521
+ <!-- Disclaimer -->
1522
+ <div style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 20px;">
1523
+ <div style="display: flex; align-items: flex-start;">
1524
+ <div style="color: #dc2626; font-size: 24px; margin-right: 16px;">โš ๏ธ</div>
1525
+ <div>
1526
+ <div style="font-weight: 600; color: #991b1b; margin-bottom: 8px; font-size: 16px;">Medical Disclaimer</div>
1527
+ <div style="color: #7f1d1d; font-size: 14px; line-height: 1.5;">
1528
+ This analysis is generated by AI for educational purposes only. It is not a substitute for
1529
+ professional medical advice, diagnosis, or treatment. Always consult a qualified homeopath
1530
+ or medical professional for health concerns. Never disregard professional medical advice
1531
+ or delay seeking treatment because of information provided