Prajith04 commited on
Commit
cbc783a
·
verified ·
1 Parent(s): faf6ae8

Upload 12 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ disease.db filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Copy requirements file
6
+ COPY requirements.txt .
7
+
8
+ # Install dependencies
9
+ RUN pip install --no-cache-dir -r requirements.txt
10
+
11
+ # Copy the rest of the application
12
+ COPY . .
13
+
14
+
15
+ # Run the application
16
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,10 +1,38 @@
1
- ---
2
- title: Qwendr
3
- emoji: 🌍
4
- colorFrom: purple
5
- colorTo: blue
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Medical Symptom Checker
2
+
3
+ An interactive medical symptom checker that helps users explore symptoms and related diseases using vector similarity search and a database of medical conditions.
4
+
5
+ ## Features
6
+
7
+ - Enter symptoms to find related diseases
8
+ - See semantically similar symptoms through vector search
9
+ - Discover related symptoms from matching diseases
10
+ - Interactive UI for refining symptom selection
11
+
12
+ ## How It Works
13
+
14
+ 1. **Vector Search**: Uses Sentence Transformers and ChromaDB to find semantically similar symptoms
15
+ 2. **Database Matching**: Finds diseases that match all selected symptoms
16
+ 3. **Related Symptoms**: Shows other symptoms from matching diseases
17
+
18
+ ## Usage
19
+
20
+ Enter a symptom in the search box (e.g., "fever", "cough", "headache") and click "Search" or press Enter.
21
+
22
+ The system will:
23
+ 1. Show diseases that have that symptom
24
+ 2. Display semantically similar symptoms
25
+ 3. Show other symptoms from the matching diseases
26
+
27
+ Click on any symptom in the "Similar Symptoms" or "Related Symptoms" sections to add it to your selection. This will narrow down the diseases to those that have ALL selected symptoms.
28
+
29
+ ## Technical Details
30
+
31
+ - **Frontend**: HTML, CSS, JavaScript
32
+ - **Backend**: FastAPI, SQLite
33
+ - **Vector Search**: Sentence Transformers, ChromaDB
34
+ - **Data**: Diseases and symptoms dataset
35
+
36
+ ## Deployment
37
+
38
+ This application is deployed on Hugging Face Spaces.
app.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Query, Request
2
+ from fastapi.responses import HTMLResponse
3
+ from fastapi.templating import Jinja2Templates
4
+ from fastapi.staticfiles import StaticFiles
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from utils.vector_search import get_similar_symptoms
7
+ from utils.sql_queries import get_diseases_by_symptoms, get_related_symptoms
8
+ import sqlite3
9
+
10
+ app = FastAPI()
11
+
12
+ # Add CORS middleware
13
+ app.add_middleware(
14
+ CORSMiddleware,
15
+ allow_origins=["*"], # Allow all origins
16
+ allow_credentials=True,
17
+ allow_methods=["*"], # Allow all methods
18
+ allow_headers=["*"], # Allow all headers
19
+ )
20
+
21
+ templates = Jinja2Templates(directory="templates")
22
+
23
+ @app.get("/", response_class=HTMLResponse)
24
+ def index(request: Request):
25
+ return templates.TemplateResponse("index.html", {"request": request})
26
+
27
+ # Helper function to check if a symptom exists in the database
28
+ def is_valid_symptom(symptom):
29
+ conn = sqlite3.connect("disease.db")
30
+ cur = conn.cursor()
31
+ cur.execute("SELECT COUNT(*) FROM symptom WHERE name = ?", (symptom,))
32
+ count = cur.fetchone()[0]
33
+ conn.close()
34
+ return count > 0
35
+
36
+ @app.get("/search")
37
+ def search(symptom: str, selected: list[str] = Query([])):
38
+ # Get similar symptoms from vector search
39
+ similar = get_similar_symptoms(symptom)
40
+
41
+ # Check if the exact symptom exists in our database
42
+ if is_valid_symptom(symptom):
43
+ # Use the exact symptom if it exists
44
+ search_symptoms = [symptom] + selected
45
+ else:
46
+ # Otherwise, try to use the first similar symptom that exists in our database
47
+ valid_symptom_found = False
48
+ for sim in similar:
49
+ if is_valid_symptom(sim):
50
+ search_symptoms = [sim] + selected
51
+ valid_symptom_found = True
52
+ break
53
+
54
+ # If no valid symptom found, just use all similar symptoms
55
+ if not valid_symptom_found:
56
+ search_symptoms = similar + selected
57
+
58
+ # Get diseases that have ALL the selected search symptoms
59
+ diseases = get_diseases_by_symptoms(search_symptoms)
60
+
61
+ # Get related symptoms from the matching diseases, excluding already selected symptoms
62
+ suggestions = get_related_symptoms(diseases, exclude=search_symptoms)
63
+
64
+ return {
65
+ "matching_diseases": diseases,
66
+ "related_symptoms": suggestions,
67
+ "semantic_matches": similar
68
+ }
disease.db ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e27f4415320952a05ceca7b615c053bc08b151ed3963e0ec714d431f519a2b1d
3
+ size 172032
main.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ def main():
2
+ print("Hello from llamadr!")
3
+
4
+
5
+ if __name__ == "__main__":
6
+ main()
pyproject.toml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "llamadr"
3
+ version = "0.1.0"
4
+ description = "Add your description here"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ dependencies = []
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi>=0.95.0
2
+ uvicorn>=0.22.0
3
+ jinja2>=3.1.2
4
+ qdrant-client>=1.13.3
5
+ sentence-transformers>=2.2.2
6
+ datasets>=2.12.0
7
+ pandas>=2.0.0
8
+ python-dotenv>=1.1.0
templates/index.html ADDED
@@ -0,0 +1,389 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <title>Medical Symptom Checker</title>
7
+ <style>
8
+ body {
9
+ font-family: 'Segoe UI', sans-serif;
10
+ background: linear-gradient(135deg, #fdfbfb, #ebedee);
11
+ margin: 0;
12
+ padding: 40px 20px;
13
+ color: #333;
14
+ }
15
+
16
+ h1 {
17
+ text-align: center;
18
+ font-size: 2.5rem;
19
+ margin-bottom: 30px;
20
+ background: linear-gradient(to right, #3b82f6, #9333ea);
21
+ -webkit-background-clip: text;
22
+ background-clip: text;
23
+ -webkit-text-fill-color: transparent;
24
+ }
25
+
26
+ .search-container {
27
+ text-align: center;
28
+ margin-bottom: 30px;
29
+ }
30
+
31
+ input[type="text"] {
32
+ padding: 12px 20px;
33
+ width: 60%;
34
+ max-width: 500px;
35
+ border-radius: 30px;
36
+ border: 1px solid #ccc;
37
+ box-shadow: 0 4px 10px rgba(0,0,0,0.05);
38
+ transition: all 0.3s ease;
39
+ }
40
+
41
+ input[type="text"]:focus {
42
+ outline: none;
43
+ border-color: #6366f1;
44
+ box-shadow: 0 4px 15px rgba(99,102,241,0.2);
45
+ }
46
+
47
+ button {
48
+ padding: 12px 25px;
49
+ margin-left: 10px;
50
+ border: none;
51
+ border-radius: 30px;
52
+ background: linear-gradient(to right, #3b82f6, #9333ea);
53
+ color: white;
54
+ cursor: pointer;
55
+ font-weight: bold;
56
+ box-shadow: 0 4px 10px rgba(0,0,0,0.1);
57
+ transition: background 0.3s ease;
58
+ }
59
+
60
+ button:hover {
61
+ background: linear-gradient(to right, #2563eb, #7e22ce);
62
+ }
63
+
64
+ .selected-symptoms {
65
+ display: flex;
66
+ flex-wrap: wrap;
67
+ justify-content: center;
68
+ gap: 10px;
69
+ margin-bottom: 30px;
70
+ }
71
+
72
+ .symptom-tag {
73
+ background: #e0e7ff;
74
+ color: #1e40af;
75
+ padding: 8px 15px;
76
+ border-radius: 20px;
77
+ display: flex;
78
+ align-items: center;
79
+ animation: fadeIn 0.3s ease;
80
+ }
81
+
82
+ .remove-symptom {
83
+ margin-left: 10px;
84
+ cursor: pointer;
85
+ color: #555;
86
+ font-weight: bold;
87
+ }
88
+
89
+ .container {
90
+ display: flex;
91
+ flex-wrap: wrap;
92
+ gap: 20px;
93
+ justify-content: center;
94
+ }
95
+
96
+ .panel {
97
+ flex: 1 1 300px;
98
+ background: white;
99
+ border-radius: 15px;
100
+ padding: 20px;
101
+ box-shadow: 0 10px 20px rgba(0,0,0,0.05);
102
+ transition: transform 0.2s ease;
103
+ animation: fadeIn 0.5s ease;
104
+ }
105
+
106
+ .panel:hover {
107
+ transform: translateY(-5px);
108
+ }
109
+
110
+ .panel h2 {
111
+ margin-top: 0;
112
+ background: linear-gradient(to right, #2563eb, #9333ea);
113
+ -webkit-background-clip: text;
114
+ background-clip: text;
115
+ -webkit-text-fill-color: transparent;
116
+ font-size: 1.5rem;
117
+ }
118
+
119
+ ul {
120
+ list-style: none;
121
+ padding: 0;
122
+ }
123
+
124
+ li {
125
+ padding: 10px 0;
126
+ border-bottom: 1px solid #eee;
127
+ transition: background 0.2s ease;
128
+ }
129
+
130
+ .clickable {
131
+ color: #6366f1;
132
+ cursor: pointer;
133
+ }
134
+
135
+ .clickable:hover {
136
+ text-decoration: underline;
137
+ }
138
+
139
+ #error-notification {
140
+ background-color: #fee2e2;
141
+ color: #b91c1c;
142
+ padding: 12px 20px;
143
+ border-radius: 8px;
144
+ margin: 0 auto 20px auto;
145
+ max-width: 600px;
146
+ text-align: center;
147
+ display: none;
148
+ }
149
+
150
+ @keyframes fadeIn {
151
+ from { opacity: 0; transform: translateY(10px); }
152
+ to { opacity: 1; transform: translateY(0); }
153
+ }
154
+
155
+ @media screen and (max-width: 768px) {
156
+ input[type="text"] {
157
+ width: 80%;
158
+ }
159
+ .container {
160
+ flex-direction: column;
161
+ align-items: stretch;
162
+ }
163
+ }
164
+ </style>
165
+ </head>
166
+ <body>
167
+ <h1>Medical Symptom Checker</h1>
168
+
169
+ <div class="search-container">
170
+ <input type="text" id="symptom-search" placeholder="Enter a symptom..."/>
171
+ <button id="search-button">Search</button>
172
+ </div>
173
+
174
+ <div id="error-notification"></div>
175
+
176
+ <div class="selected-symptoms" id="selected-symptoms">
177
+ <!-- Selected symptoms will appear here -->
178
+ </div>
179
+
180
+ <div class="container">
181
+ <div class="panel">
182
+ <h2>Matching Diseases</h2>
183
+ <ul id="diseases-list"></ul>
184
+ </div>
185
+ <div class="panel">
186
+ <h2>Similar Symptoms</h2>
187
+ <ul id="similar-list"></ul>
188
+ </div>
189
+ <div class="panel">
190
+ <h2>Related Symptoms</h2>
191
+ <ul id="related-list"></ul>
192
+ </div>
193
+ </div>
194
+
195
+ <!-- Keep your JavaScript at the bottom (same as before, or I can refactor it too if you want) -->
196
+ <script>
197
+ // Store selected symptoms
198
+ let selectedSymptoms = [];
199
+
200
+ // DOM elements
201
+ const searchInput = document.getElementById('symptom-search');
202
+ const searchButton = document.getElementById('search-button');
203
+ const selectedSymptomsDiv = document.getElementById('selected-symptoms');
204
+ const diseasesList = document.getElementById('diseases-list');
205
+ const similarList = document.getElementById('similar-list');
206
+ const relatedList = document.getElementById('related-list');
207
+ const errorElement = document.getElementById('error-notification');
208
+
209
+ // Function to show error message
210
+ function showError(message) {
211
+ errorElement.textContent = message;
212
+ errorElement.style.display = 'block';
213
+ setTimeout(() => {
214
+ errorElement.style.display = 'none';
215
+ }, 5000);
216
+ }
217
+
218
+ // New function to handle both initial search and updates after selections
219
+ async function performSearch(inputSymptom) {
220
+ // Clear any previous errors
221
+ errorElement.style.display = 'none';
222
+
223
+ try {
224
+ // Create base URL with inputSymptom if provided, otherwise use first selected symptom
225
+ let symptomParam = inputSymptom;
226
+ if (!symptomParam && selectedSymptoms.length > 0) {
227
+ symptomParam = selectedSymptoms[0];
228
+ }
229
+
230
+ if (!symptomParam) return; // Exit if no symptom to search
231
+
232
+ let url = `${window.location.origin}/search?symptom=${encodeURIComponent(symptomParam)}`;
233
+
234
+ // Add remaining selected symptoms to query
235
+ if (selectedSymptoms.length > 0) {
236
+ selectedSymptoms.forEach((s, index) => {
237
+ // Skip the first one if it's the same as symptomParam
238
+ if (index === 0 && s === symptomParam) return;
239
+ url += `&selected=${encodeURIComponent(s)}`;
240
+ });
241
+ }
242
+
243
+ const response = await fetch(url);
244
+
245
+ if (!response.ok) {
246
+ throw new Error(`HTTP error! status: ${response.status}`);
247
+ }
248
+
249
+ const data = await response.json();
250
+
251
+ displayResults(data);
252
+
253
+ // Clear search input only if this was initiated from the search box
254
+ if (inputSymptom) {
255
+ searchInput.value = '';
256
+ }
257
+ } catch (error) {
258
+ showError(`Error searching for symptom: ${error.message}. Please try again.`);
259
+ console.error('Error fetching data:', error);
260
+ }
261
+ }
262
+
263
+ // Update search function to use the new performSearch function
264
+ async function searchSymptoms() {
265
+ const symptom = searchInput.value.trim();
266
+ if (!symptom) return;
267
+ performSearch(symptom);
268
+ }
269
+
270
+ // Display results
271
+ function displayResults(data) {
272
+ // Update diseases list
273
+ diseasesList.innerHTML = '';
274
+ if (!data.matching_diseases || data.matching_diseases.length === 0) {
275
+ diseasesList.innerHTML = '<li>No matching diseases found</li>';
276
+ } else {
277
+ data.matching_diseases.forEach(disease => {
278
+ diseasesList.innerHTML += `<li>${escapeHTML(disease)}</li>`;
279
+ });
280
+ }
281
+
282
+ // Update similar symptoms
283
+ similarList.innerHTML = '';
284
+ if (data.semantic_matches && data.semantic_matches.length > 0) {
285
+ data.semantic_matches.forEach(symptom => {
286
+ if (!selectedSymptoms.includes(symptom)) {
287
+ similarList.innerHTML += `<li class="clickable" onclick="addSymptom('${escapeJS(symptom)}')">${escapeHTML(symptom)}</li>`;
288
+ }
289
+ });
290
+ } else {
291
+ similarList.innerHTML = '<li>No similar symptoms found</li>';
292
+ }
293
+
294
+ // Update related symptoms - sort them alphabetically
295
+ relatedList.innerHTML = '';
296
+ if (!data.related_symptoms || data.related_symptoms.length === 0) {
297
+ relatedList.innerHTML = '<li>No related symptoms found</li>';
298
+ } else {
299
+ // Sort the related symptoms alphabetically
300
+ const sortedSymptoms = [...data.related_symptoms].sort((a, b) => a.localeCompare(b));
301
+
302
+ sortedSymptoms.forEach(symptom => {
303
+ if (!selectedSymptoms.includes(symptom)) {
304
+ relatedList.innerHTML += `<li class="clickable" onclick="addSymptom('${escapeJS(symptom)}')">${escapeHTML(symptom)}</li>`;
305
+ }
306
+ });
307
+ }
308
+
309
+ // Update selected symptoms
310
+ updateSelectedSymptoms();
311
+ }
312
+
313
+ // Helper function to escape HTML
314
+ function escapeHTML(str) {
315
+ return str
316
+ .replace(/&/g, '&amp;')
317
+ .replace(/</g, '&lt;')
318
+ .replace(/>/g, '&gt;')
319
+ .replace(/"/g, '&quot;')
320
+ .replace(/'/g, '&#39;');
321
+ }
322
+
323
+ // Helper function to escape strings for JavaScript
324
+ function escapeJS(str) {
325
+ return str
326
+ .replace(/\\/g, '\\\\')
327
+ .replace(/'/g, "\\'")
328
+ .replace(/"/g, '\\"')
329
+ .replace(/\n/g, '\\n')
330
+ .replace(/\r/g, '\\r')
331
+ .replace(/\t/g, '\\t');
332
+ }
333
+
334
+ // Add a symptom to the selected list
335
+ function addSymptom(symptom) {
336
+ if (!selectedSymptoms.includes(symptom)) {
337
+ selectedSymptoms.push(symptom);
338
+ updateSelectedSymptoms();
339
+
340
+ // Instead of calling searchSymptoms which expects input from search box,
341
+ // directly make a request with the current selected symptoms
342
+ performSearch(null);
343
+ }
344
+ }
345
+
346
+ // Remove a symptom from the selected list
347
+ function removeSymptom(symptom) {
348
+ selectedSymptoms = selectedSymptoms.filter(s => s !== symptom);
349
+ updateSelectedSymptoms();
350
+ if (selectedSymptoms.length > 0) {
351
+ performSearch(null);
352
+ } else {
353
+ // Clear results if no symptoms are selected
354
+ diseasesList.innerHTML = '<li>Enter a symptom to get started</li>';
355
+ similarList.innerHTML = '';
356
+ relatedList.innerHTML = '';
357
+ }
358
+ }
359
+
360
+ // Update the selected symptoms display
361
+ function updateSelectedSymptoms() {
362
+ selectedSymptomsDiv.innerHTML = '';
363
+ selectedSymptoms.forEach(symptom => {
364
+ const tag = document.createElement('div');
365
+ tag.className = 'symptom-tag';
366
+ tag.innerHTML = `
367
+ ${escapeHTML(symptom)}
368
+ <span class="remove-symptom" onclick="removeSymptom('${escapeJS(symptom)}')">×</span>
369
+ `;
370
+ selectedSymptomsDiv.appendChild(tag);
371
+ });
372
+ }
373
+
374
+ // Event listeners
375
+ searchButton.addEventListener('click', searchSymptoms);
376
+ searchInput.addEventListener('keypress', function(e) {
377
+ if (e.key === 'Enter') {
378
+ searchSymptoms();
379
+ }
380
+ });
381
+
382
+ // Initialize
383
+ diseasesList.innerHTML = '<li>Enter a symptom to get started</li>';
384
+
385
+ // Paste your exact same JS from earlier here — no changes needed.
386
+ // Let me know if you'd like animation when adding/removing tags.
387
+ </script>
388
+ </body>
389
+ </html>
utils/__pycache__/sql_queries.cpython-311.pyc ADDED
Binary file (3.94 kB). View file
 
utils/__pycache__/vector_search.cpython-311.pyc ADDED
Binary file (1.55 kB). View file
 
utils/sql_queries.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sqlite3
2
+
3
+ def get_diseases_by_symptoms(symptoms):
4
+ """
5
+ Get diseases that have ALL the given symptoms.
6
+
7
+ Parameters:
8
+ - symptoms: List of symptom names
9
+ """
10
+ if not symptoms:
11
+ return []
12
+
13
+ conn = sqlite3.connect("disease.db")
14
+ cur = conn.cursor()
15
+
16
+ try:
17
+ placeholder = ",".join(["?"] * len(symptoms))
18
+
19
+ # Find diseases that have ALL the symptoms
20
+ query = f"""
21
+ SELECT d.name
22
+ FROM disease d
23
+ WHERE (
24
+ SELECT COUNT(DISTINCT s.name)
25
+ FROM disease_symptom ds
26
+ JOIN symptom s ON s.id = ds.symptom_id
27
+ WHERE ds.disease_id = d.id AND s.name IN ({placeholder})
28
+ ) = ?
29
+ """
30
+
31
+ cur.execute(query, symptoms + [len(symptoms)])
32
+ result = [row[0] for row in cur.fetchall()]
33
+ return result
34
+ except Exception as e:
35
+ print(f"SQL Error in get_diseases_by_symptoms: {e}")
36
+ return []
37
+ finally:
38
+ conn.close()
39
+
40
+ def get_related_symptoms(disease_names, exclude=[]):
41
+ """
42
+ Get ALL symptoms from the specified diseases, excluding the ones provided.
43
+
44
+ Parameters:
45
+ - disease_names: List of disease names to get symptoms from
46
+ - exclude: List of symptom names to exclude from the results
47
+ """
48
+ if not disease_names:
49
+ return []
50
+
51
+ conn = sqlite3.connect("disease.db")
52
+ cur = conn.cursor()
53
+
54
+ try:
55
+ placeholder = ",".join(["?"] * len(disease_names))
56
+ exclude_clause = ""
57
+ if exclude:
58
+ exclude_placeholder = ",".join(["?"] * len(exclude))
59
+ exclude_clause = f"AND s.name NOT IN ({exclude_placeholder})"
60
+
61
+ # Get ALL OTHER symptoms from the matching diseases
62
+ query = f"""
63
+ SELECT DISTINCT s.name
64
+ FROM symptom s
65
+ JOIN disease_symptom ds ON s.id = ds.symptom_id
66
+ JOIN disease d ON d.id = ds.disease_id
67
+ WHERE d.name IN ({placeholder}) {exclude_clause}
68
+ """
69
+
70
+ cur.execute(query, disease_names + (exclude if exclude else []))
71
+ result = [row[0] for row in cur.fetchall()]
72
+ return result
73
+ except Exception as e:
74
+ print(f"SQL Error in get_related_symptoms: {e}")
75
+ return []
76
+ finally:
77
+ conn.close()
utils/vector_search.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from qdrant_client import QdrantClient
2
+ from sentence_transformers import SentenceTransformer
3
+ import os
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+
8
+ # Initialize Qdrant Cloud client
9
+ url=os.environ.get("QDRANT_URL")
10
+ api_key=os.environ.get("QDRANT_API_KEY")
11
+ qdrant_client = QdrantClient(
12
+ url=url, # Use environment variable
13
+ api_key=api_key, # Use environment variable
14
+ timeout=30.0
15
+ )
16
+
17
+ # Initialize SentenceTransformer model
18
+ model = SentenceTransformer("all-MiniLM-L6-v2")
19
+
20
+ # Collection name (must match the one used during upsert)
21
+ collection_name = "symptoms"
22
+
23
+ def get_similar_symptoms(symptom):
24
+ # Encode the input symptom
25
+ vector = model.encode(symptom)
26
+
27
+ # Perform similarity search
28
+ results = qdrant_client.search(
29
+ collection_name=collection_name,
30
+ query_vector=vector.tolist(),
31
+ limit=5, # Return top 5 similar symptoms
32
+ with_payload=True # Include payload (e.g., symptom name)
33
+ )
34
+
35
+ # Extract symptom names from results
36
+ similar_symptoms = [result.payload["name"] for result in results]
37
+ return similar_symptoms