Ali2206 commited on
Commit
cacd13a
·
verified ·
1 Parent(s): 48462d9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -458
app.py CHANGED
@@ -1,474 +1,104 @@
1
- from fastapi import APIRouter, HTTPException, Depends, Query, status, Body
2
- from db.mongo import patients_collection
3
- from core.security import get_current_user
4
- from utils.db import create_indexes
5
- from utils.helpers import calculate_age, standardize_language
6
- from models.entities import Note, PatientCreate
7
- from datetime import datetime
8
- from bson import ObjectId
9
- from bson.errors import InvalidId
10
- from typing import Optional, List, Dict
11
- from pymongo import UpdateOne
12
- from pymongo.errors import BulkWriteError
13
- import json
14
- from pathlib import Path
15
- import glob
16
- import uuid
17
- import re
18
  import logging
19
- import time
20
- import os
21
 
22
- # Configure logging
23
- logging.basicConfig(
24
- level=logging.INFO,
25
- format='%(asctime)s - %(levelname)s - %(name)s - %(message)s'
26
- )
27
  logger = logging.getLogger(__name__)
 
28
 
29
- router = APIRouter()
30
 
31
- # Configuration
32
- BASE_DIR = Path(__file__).resolve().parent.parent.parent
33
- SYNTHEA_DATA_DIR = BASE_DIR / "output" / "fhir"
34
- os.makedirs(SYNTHEA_DATA_DIR, exist_ok=True)
 
 
 
 
35
 
36
- @router.post("/", status_code=status.HTTP_201_CREATED)
37
- async def create_patient(
38
- patient_data: PatientCreate,
39
- current_user: dict = Depends(get_current_user)
40
- ):
41
- """Create a new patient in the database"""
42
- logger.info(f"Creating new patient by user {current_user.get('email')}")
43
-
44
- if current_user.get('role') not in ['admin', 'doctor']:
45
- logger.warning(f"Unauthorized create attempt by {current_user.get('email')}")
46
- raise HTTPException(
47
- status_code=status.HTTP_403_FORBIDDEN,
48
- detail="Only administrators and doctors can create patients"
49
- )
50
-
51
- try:
52
- # Prepare the patient document
53
- patient_doc = patient_data.dict()
54
- now = datetime.utcnow().isoformat()
55
-
56
- # Add system-generated fields
57
- patient_doc.update({
58
- "fhir_id": str(uuid.uuid4()),
59
- "import_date": now,
60
- "last_updated": now,
61
- "source": "manual",
62
- "created_by": current_user.get('email')
63
- })
64
-
65
- # Ensure arrays exist even if empty
66
- for field in ['conditions', 'medications', 'encounters', 'notes']:
67
- if field not in patient_doc:
68
- patient_doc[field] = []
69
-
70
- # Insert the patient document
71
- result = await patients_collection.insert_one(patient_doc)
72
-
73
- # Return the created patient with the generated ID
74
- created_patient = await patients_collection.find_one(
75
- {"_id": result.inserted_id}
76
- )
77
-
78
- if not created_patient:
79
- logger.error("Failed to retrieve created patient")
80
- raise HTTPException(
81
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
82
- detail="Failed to retrieve created patient"
83
- )
84
-
85
- created_patient["id"] = str(created_patient["_id"])
86
- del created_patient["_id"]
87
-
88
- logger.info(f"Successfully created patient {created_patient['fhir_id']}")
89
- return created_patient
90
-
91
- except Exception as e:
92
- logger.error(f"Failed to create patient: {str(e)}")
93
- raise HTTPException(
94
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
95
- detail=f"Failed to create patient: {str(e)}"
96
- )
97
 
98
- async def process_synthea_patient(bundle: dict, file_path: str) -> Optional[dict]:
99
- logger.debug(f"Processing patient from file: {file_path}")
100
- patient_data = {}
101
- notes = []
102
- conditions = []
103
- medications = []
104
- encounters = []
105
-
106
- # Validate bundle structure
107
- if not isinstance(bundle, dict) or 'entry' not in bundle:
108
- logger.error(f"Invalid FHIR bundle structure in {file_path}")
109
- return None
110
-
111
- for entry in bundle.get('entry', []):
112
- resource = entry.get('resource', {})
113
- resource_type = resource.get('resourceType')
114
-
115
- if not resource_type:
116
- logger.warning(f"Skipping entry with missing resourceType in {file_path}")
117
- continue
118
-
119
- try:
120
- if resource_type == 'Patient':
121
- name = resource.get('name', [{}])[0]
122
- address = resource.get('address', [{}])[0]
123
-
124
- # Construct full name and remove numbers
125
- raw_full_name = f"{' '.join(name.get('given', ['']))} {name.get('family', '')}".strip()
126
- clean_full_name = re.sub(r'\d+', '', raw_full_name).strip()
127
-
128
- patient_data = {
129
- 'fhir_id': resource.get('id'),
130
- 'full_name': clean_full_name,
131
- 'gender': resource.get('gender', 'unknown'),
132
- 'date_of_birth': resource.get('birthDate', ''),
133
- 'address': ' '.join(address.get('line', [''])),
134
- 'city': address.get('city', ''),
135
- 'state': address.get('state', ''),
136
- 'postal_code': address.get('postalCode', ''),
137
- 'country': address.get('country', ''),
138
- 'marital_status': resource.get('maritalStatus', {}).get('text', ''),
139
- 'language': standardize_language(resource.get('communication', [{}])[0].get('language', {}).get('text', '')),
140
- 'source': 'synthea',
141
- 'last_updated': datetime.utcnow().isoformat()
142
- }
143
-
144
- elif resource_type == 'Encounter':
145
- encounter = {
146
- 'id': resource.get('id'),
147
- 'type': resource.get('type', [{}])[0].get('text', ''),
148
- 'status': resource.get('status'),
149
- 'period': resource.get('period', {}),
150
- 'service_provider': resource.get('serviceProvider', {}).get('display', '')
151
- }
152
- encounters.append(encounter)
153
-
154
- for note in resource.get('note', []):
155
- if note.get('text'):
156
- notes.append({
157
- 'date': resource.get('period', {}).get('start', datetime.utcnow().isoformat()),
158
- 'type': resource.get('type', [{}])[0].get('text', 'Encounter Note'),
159
- 'text': note.get('text'),
160
- 'context': f"Encounter: {encounter.get('type')}",
161
- 'author': 'System Generated'
162
- })
163
-
164
- elif resource_type == 'Condition':
165
- conditions.append({
166
- 'id': resource.get('id'),
167
- 'code': resource.get('code', {}).get('text', ''),
168
- 'status': resource.get('clinicalStatus', {}).get('text', ''),
169
- 'onset_date': resource.get('onsetDateTime'),
170
- 'recorded_date': resource.get('recordedDate'),
171
- 'verification_status': resource.get('verificationStatus', {}).get('text', '')
172
- })
173
-
174
- elif resource_type == 'MedicationRequest':
175
- medications.append({
176
- 'id': resource.get('id'),
177
- 'name': resource.get('medicationCodeableConcept', {}).get('text', ''),
178
- 'status': resource.get('status'),
179
- 'prescribed_date': resource.get('authoredOn'),
180
- 'requester': resource.get('requester', {}).get('display', ''),
181
- 'dosage': resource.get('dosageInstruction', [{}])[0].get('text', '')
182
- })
183
-
184
- except Exception as e:
185
- logger.error(f"Error processing {resource_type} in {file_path}: {str(e)}")
186
- continue
187
-
188
- if patient_data:
189
- patient_data.update({
190
- 'notes': notes,
191
- 'conditions': conditions,
192
- 'medications': medications,
193
- 'encounters': encounters,
194
- 'import_date': datetime.utcnow().isoformat()
195
- })
196
- logger.info(f"Successfully processed patient {patient_data.get('fhir_id')} from {file_path}")
197
- return patient_data
198
- logger.warning(f"No valid patient data found in {file_path}")
199
- return None
200
 
201
- @router.post("/import", status_code=status.HTTP_201_CREATED)
202
- async def import_patients(
203
- limit: int = Query(100, ge=1, le=1000),
204
- current_user: dict = Depends(get_current_user)
205
- ):
206
- request_id = str(uuid.uuid4())
207
- logger.info(f"Starting import request {request_id} by user {current_user.get('email')}")
208
- start_time = time.time()
209
-
210
- if current_user.get('role') not in ['admin', 'doctor']:
211
- logger.warning(f"Unauthorized import attempt by {current_user.get('email')}")
212
- raise HTTPException(
213
- status_code=status.HTTP_403_FORBIDDEN,
214
- detail="Only administrators and doctors can import data"
215
- )
216
-
217
- try:
218
- await create_indexes()
219
-
220
- if not SYNTHEA_DATA_DIR.exists():
221
- logger.error(f"Synthea data directory not found: {SYNTHEA_DATA_DIR}")
222
- raise HTTPException(
223
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
224
- detail="Data directory not found"
225
- )
226
-
227
- # Filter out non-patient files
228
- files = [
229
- f for f in glob.glob(str(SYNTHEA_DATA_DIR / "*.json"))
230
- if not re.search(r'(hospitalInformation|practitionerInformation)\d+\.json$', f)
231
- ]
232
- if not files:
233
- logger.warning("No valid patient JSON files found in synthea data directory")
234
- return {
235
- "status": "success",
236
- "message": "No patient data files found",
237
- "imported": 0,
238
- "request_id": request_id
239
- }
240
-
241
- operations = []
242
- imported = 0
243
- errors = []
244
-
245
- for file_path in files[:limit]:
246
- try:
247
- logger.debug(f"Processing file: {file_path}")
248
-
249
- # Check file accessibility
250
- if not os.path.exists(file_path):
251
- logger.error(f"File not found: {file_path}")
252
- errors.append(f"File not found: {file_path}")
253
- continue
254
-
255
- # Check file size
256
- file_size = os.path.getsize(file_path)
257
- if file_size == 0:
258
- logger.warning(f"Empty file: {file_path}")
259
- errors.append(f"Empty file: {file_path}")
260
- continue
261
-
262
- with open(file_path, 'r', encoding='utf-8') as f:
263
- try:
264
- bundle = json.load(f)
265
- except json.JSONDecodeError as je:
266
- logger.error(f"Invalid JSON in {file_path}: {str(je)}")
267
- errors.append(f"Invalid JSON in {file_path}: {str(je)}")
268
- continue
269
-
270
- patient = await process_synthea_patient(bundle, file_path)
271
- if patient:
272
- if not patient.get('fhir_id'):
273
- logger.warning(f"Missing FHIR ID in patient data from {file_path}")
274
- errors.append(f"Missing FHIR ID in {file_path}")
275
- continue
276
-
277
- operations.append(UpdateOne(
278
- {"fhir_id": patient['fhir_id']},
279
- {"$setOnInsert": patient},
280
- upsert=True
281
- ))
282
- imported += 1
283
- else:
284
- logger.warning(f"No valid patient data in {file_path}")
285
- errors.append(f"No valid patient data in {file_path}")
286
-
287
- except Exception as e:
288
- logger.error(f"Error processing {file_path}: {str(e)}")
289
- errors.append(f"Error in {file_path}: {str(e)}")
290
- continue
291
-
292
- response = {
293
- "status": "success",
294
- "imported": imported,
295
- "errors": errors,
296
- "request_id": request_id,
297
- "duration_seconds": time.time() - start_time
298
- }
299
-
300
- if operations:
301
- try:
302
- result = await patients_collection.bulk_write(operations, ordered=False)
303
- response.update({
304
- "upserted": result.upserted_count,
305
- "existing": len(operations) - result.upserted_count
306
- })
307
- logger.info(f"Import request {request_id} completed: {imported} patients processed, "
308
- f"{result.upserted_count} upserted, {len(errors)} errors")
309
- except BulkWriteError as bwe:
310
- logger.error(f"Partial bulk write failure for request {request_id}: {str(bwe.details)}")
311
- response.update({
312
- "upserted": bwe.details.get('nUpserted', 0),
313
- "existing": len(operations) - bwe.details.get('nUpserted', 0),
314
- "write_errors": [
315
- f"Index {err['index']}: {err['errmsg']}" for err in bwe.details.get('writeErrors', [])
316
- ]
317
- })
318
- logger.info(f"Import request {request_id} partially completed: {imported} patients processed, "
319
- f"{response['upserted']} upserted, {len(errors)} errors")
320
- except Exception as e:
321
- logger.error(f"Bulk write failed for request {request_id}: {str(e)}")
322
- raise HTTPException(
323
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
324
- detail=f"Database operation failed: {str(e)}"
325
- )
326
- else:
327
- logger.info(f"Import request {request_id} completed: No new patients to import, {len(errors)} errors")
328
- response["message"] = "No new patients found to import"
329
-
330
- return response
331
-
332
- except HTTPException:
333
- raise
334
- except Exception as e:
335
- logger.error(f"Import request {request_id} failed: {str(e)}", exc_info=True)
336
- raise HTTPException(
337
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
338
- detail=f"Import failed: {str(e)}"
339
- )
340
 
341
- @router.get("/", response_model=List[dict])
342
- async def list_patients(
343
- search: Optional[str] = Query(None),
344
- min_notes: int = Query(0, ge=0),
345
- min_conditions: int = Query(0, ge=0),
346
- limit: int = Query(100, ge=1, le=500),
347
- skip: int = Query(0, ge=0),
348
- current_user: dict = Depends(get_current_user)
349
- ):
350
- logger.info(f"Listing patients with search: {search}, limit: {limit}, skip: {skip}")
351
- query = {}
352
-
353
- if search:
354
- query["$or"] = [
355
- {"full_name": {"$regex": search, "$options": "i"}},
356
- {"fhir_id": search}
357
- ]
358
-
359
- if min_notes > 0:
360
- query[f"notes.{min_notes-1}"] = {"$exists": True}
361
-
362
- if min_conditions > 0:
363
- query[f"conditions.{min_conditions-1}"] = {"$exists": True}
364
-
365
  try:
366
- cursor = patients_collection.find(query).skip(skip).limit(limit)
367
- patients = []
368
-
369
- async for patient in cursor:
370
- patient["id"] = str(patient["_id"])
371
- patient["age"] = calculate_age(patient.get("date_of_birth"))
372
- patients.append(patient)
373
-
374
- logger.info(f"Retrieved {len(patients)} patients")
375
- return patients
376
-
377
  except Exception as e:
378
- logger.error(f"Failed to list patients: {str(e)}")
379
- raise HTTPException(
380
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
381
- detail=f"Failed to retrieve patients: {str(e)}"
382
- )
383
 
384
- @router.get("/{patient_id}", response_model=dict)
385
- async def get_patient(
386
- patient_id: str,
387
- current_user: dict = Depends(get_current_user)
388
- ):
389
- logger.info(f"Retrieving patient: {patient_id}")
390
- try:
391
- # First try to find by ObjectId
392
- try:
393
- patient = await patients_collection.find_one({"_id": ObjectId(patient_id)})
394
- except (InvalidId, ValueError):
395
- # If not valid ObjectId, try by fhir_id
396
- patient = await patients_collection.find_one({"fhir_id": patient_id})
397
-
398
- if not patient:
399
- logger.warning(f"Patient not found: {patient_id}")
400
- raise HTTPException(
401
- status_code=status.HTTP_404_NOT_FOUND,
402
- detail="Patient not found"
 
 
 
 
 
 
 
 
 
 
403
  )
404
-
405
- patient["id"] = str(patient["_id"])
406
- patient["age"] = calculate_age(patient.get("date_of_birth"))
407
-
408
- logger.info(f"Successfully retrieved patient: {patient_id}")
409
- return patient
410
-
411
- except Exception as e:
412
- logger.error(f"Failed to retrieve patient {patient_id}: {str(e)}")
413
- raise HTTPException(
414
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
415
- detail=f"Failed to retrieve patient: {str(e)}"
416
- )
417
 
418
- @router.post("/{patient_id}/notes", status_code=status.HTTP_201_CREATED)
419
- async def add_note(
420
- patient_id: str,
421
- note: Note,
422
- current_user: dict = Depends(get_current_user)
423
- ):
424
- logger.info(f"Adding note for patient {patient_id} by user {current_user.get('email')}")
425
- if current_user.get('role') not in ['doctor', 'admin']:
426
- logger.warning(f"Unauthorized note addition attempt by {current_user.get('email')}")
427
- raise HTTPException(
428
- status_code=status.HTTP_403_FORBIDDEN,
429
- detail="Only clinicians can add notes"
430
- )
431
-
432
- try:
433
- note_data = note.dict()
434
- note_data.update({
435
- "author": current_user.get('full_name', 'System'),
436
- "timestamp": datetime.utcnow().isoformat()
437
- })
438
-
439
- result = await patients_collection.update_one(
440
- {"$or": [
441
- {"_id": ObjectId(patient_id)},
442
- {"fhir_id": patient_id}
443
- ]},
444
- {
445
- "$push": {"notes": note_data},
446
- "$set": {"last_updated": datetime.utcnow().isoformat()}
447
- }
448
- )
449
-
450
- if result.modified_count == 0:
451
- logger.warning(f"Patient not found for note addition: {patient_id}")
452
- raise HTTPException(
453
- status_code=status.HTTP_404_NOT_FOUND,
454
- detail="Patient not found"
455
  )
456
-
457
- logger.info(f"Note added successfully for patient {patient_id}")
458
- return {"status": "success", "message": "Note added"}
459
-
460
- except ValueError as ve:
461
- logger.error(f"Invalid patient ID format: {patient_id}, error: {str(ve)}")
462
- raise HTTPException(
463
- status_code=status.HTTP_400_BAD_REQUEST,
464
- detail="Invalid patient ID format"
465
- )
466
- except Exception as e:
467
- logger.error(f"Failed to add note for patient {patient_id}: {str(e)}")
468
- raise HTTPException(
469
- status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
470
- detail=f"Failed to add note: {str(e)}"
471
- )
472
 
473
- # Export the router as 'patients' for api.__init__.py
474
- patients = router
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import RedirectResponse
4
+ from api import api_router
5
+ import gradio as gr
6
+ import requests
 
 
 
 
 
 
 
 
 
 
 
7
  import logging
 
 
8
 
9
+ logging.basicConfig(level=logging.DEBUG)
 
 
 
 
10
  logger = logging.getLogger(__name__)
11
+ logger.debug("Initializing application")
12
 
13
+ app = FastAPI()
14
 
15
+ # CORS
16
+ app.add_middleware(
17
+ CORSMiddleware,
18
+ allow_origins=["*"],
19
+ allow_credentials=True,
20
+ allow_methods=["*"],
21
+ allow_headers=["*"],
22
+ )
23
 
24
+ app.include_router(api_router)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
+ @app.get("/")
27
+ def root():
28
+ logger.debug("Root endpoint accessed")
29
+ return {"message": "🚀 FastAPI with MongoDB + JWT is running."}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ # Redirect /login to /auth/login
32
+ @app.post("/login")
33
+ async def redirect_login(request: Request):
34
+ logger.info("Redirecting /login to /auth/login")
35
+ return RedirectResponse(url="/auth/login", status_code=307)
36
+
37
+ # Gradio doctor creation logic
38
+ BACKEND_URL = "https://rocketfarmstudios-cps-api.hf.space"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ def create_doctor(full_name, email, matricule, password, specialty):
41
+ payload = {
42
+ "full_name": full_name,
43
+ "email": email,
44
+ "license_number": matricule,
45
+ "password": password,
46
+ "specialty": specialty,
47
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  try:
49
+ res = requests.post(f"{BACKEND_URL}/auth/admin/doctors", json=payload)
50
+ if res.status_code == 201:
51
+ return "✅ Doctor created successfully!"
52
+ return f"❌ {res.json().get('detail', 'Error occurred')}"
 
 
 
 
 
 
 
53
  except Exception as e:
54
+ return f" Network Error: {str(e)}"
 
 
 
 
55
 
56
+ # Define Gradio interface as a separate function
57
+ def setup_gradio():
58
+ logger.debug("Setting up Gradio interface")
59
+ with gr.Blocks(css="""
60
+ .gradio-container {
61
+ background-color: #1A1B1F;
62
+ color: #E2E8F0;
63
+ font-family: 'Segoe UI', sans-serif;
64
+ padding: 3rem;
65
+ }
66
+ .title-text { text-align: center; font-size: 2rem; font-weight: 700; color: #37B6E9; margin-bottom: 0.5rem; }
67
+ .description-text { text-align: center; font-size: 1rem; color: #A0AEC0; margin-bottom: 2rem; }
68
+ .gr-box, .gr-form, .gr-column, .gr-panel { background-color: #2D2F36 !important; border-radius: 16px !important; padding: 2rem !important; max-width: 600px; margin: auto; box-shadow: 0 0 0 1px #3B3E47; }
69
+ label { font-weight: 600; color: #F7FAFC; margin-bottom: 6px; }
70
+ input, select, textarea { background-color: #1A1B1F !important; color: #F7FAFC !important; border: 1px solid #4A5568 !important; font-size: 14px; padding: 10px; border-radius: 10px; }
71
+ button { background-color: #37B6E9 !important; color: #1A1B1F !important; border-radius: 10px !important; font-weight: 600; padding: 12px; width: 100%; margin-top: 1.5rem; }
72
+ .output-box textarea { background-color: transparent !important; border: none; color: #90CDF4; font-size: 14px; margin-top: 1rem; }
73
+ """) as admin_ui:
74
+ gr.Markdown("<div class='title-text'>👨‍⚕️ Doctor Account Creator</div>")
75
+ gr.Markdown("<div class='description-text'>Admins can register new doctors using this secure panel. Generated at 03:43 PM CET on Saturday, May 17, 2025.</div>")
76
+
77
+ with gr.Column():
78
+ full_name = gr.Textbox(label="Full Name", placeholder="e.g. Dr. Sarah Hopkins")
79
+ email = gr.Textbox(label="Email", placeholder="e.g. doctor@clinic.org")
80
+ matricule = gr.Textbox(label="Matricule", placeholder="e.g. DOC-1234")
81
+ specialty = gr.Dropdown(
82
+ label="Specialty",
83
+ choices=["Cardiology", "Neurology", "Pediatrics", "Oncology", "General Practice", "Psychiatry", "Dermatology", "Orthopedics"],
84
+ value="Cardiology"
85
  )
86
+ password = gr.Textbox(label="Password", type="password", placeholder="Secure password")
87
+ submit_btn = gr.Button("Create Doctor Account")
88
+ output = gr.Textbox(label="", show_label=False, elem_classes=["output-box"])
 
 
 
 
 
 
 
 
 
 
89
 
90
+ submit_btn.click(
91
+ fn=create_doctor,
92
+ inputs=[full_name, email, matricule, specialty, password],
93
+ outputs=output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  )
95
+ return admin_ui
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
+ # Mount Gradio at /admin
98
+ if __name__ == "__main__":
99
+ logger.debug("Running main block")
100
+ admin_ui = setup_gradio()
101
+ app = gr.mount_gradio_app(app, admin_ui, path="/admin")
102
+ logger.debug("Gradio mounted, starting app")
103
+ import uvicorn
104
+ uvicorn.run(app, host="0.0.0.0", port=7860) # Hugging Face Spaces default port