Ali2206 commited on
Commit
3d8532a
·
verified ·
1 Parent(s): 046255d

Update endpoints.py

Browse files
Files changed (1) hide show
  1. endpoints.py +29 -84
endpoints.py CHANGED
@@ -3,45 +3,24 @@ from fastapi.responses import StreamingResponse, JSONResponse
3
  from fastapi.encoders import jsonable_encoder
4
  from typing import Optional
5
  from models import ChatRequest, VoiceOutputRequest, RiskLevel
6
- from datetime import datetime
7
- from bson import ObjectId
8
- import asyncio
9
- from config import notifications_collection, send_push_notification, decode_access_token, AUTH_SPACE_URL, logger
10
- import httpx
11
  from utils import clean_text_response
12
  from analysis import analyze_patient_report
13
  from voice import recognize_speech, text_to_speech, extract_text_from_pdf
14
  from docx import Document
15
  import re
16
  import io
 
 
 
 
17
 
18
- async def get_current_user(token: str = Depends(lambda: None)):
19
- if not token:
20
- raise HTTPException(status_code=401, detail="Authorization token required")
21
- if token.startswith("Bearer "):
22
- token = token.split(" ")[1]
23
- payload = decode_access_token(token)
24
- if not payload:
25
- raise HTTPException(status_code=401, detail="Invalid token")
26
- email = payload.get("sub")
27
- if not email:
28
- raise HTTPException(status_code=401, detail="Invalid token payload")
29
-
30
- # Fetch user from auth Space
31
- async with httpx.AsyncClient() as client:
32
- headers = {"Authorization": f"Bearer {token}"}
33
- response = await client.get(f"{AUTH_SPACE_URL}/me", headers=headers)
34
- if response.status_code != 200:
35
- raise HTTPException(status_code=response.status_code, detail="Could not validate credentials")
36
- user = response.json()
37
- return user
38
-
39
- def create_router(agent, logger, patients_collection, analysis_collection, users_collection, notifications_collection):
40
  router = APIRouter()
41
 
42
  @router.get("/status")
43
  async def status(current_user: dict = Depends(get_current_user)):
44
- logger.info(f"Status endpoint accessed by {current_user['email']} at {datetime.utcnow().isoformat()}")
45
  return {
46
  "status": "running",
47
  "timestamp": datetime.utcnow().isoformat(),
@@ -54,7 +33,7 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
54
  name: Optional[str] = Query(None),
55
  current_user: dict = Depends(get_current_user)
56
  ):
57
- logger.info(f"Fetching analysis results by {current_user['email']} at {datetime.utcnow().isoformat()}")
58
  try:
59
  query = {}
60
  if name:
@@ -70,7 +49,7 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
70
  for analysis in analyses:
71
  patient = await patients_collection.find_one({"fhir_id": analysis.get("patient_id")})
72
  if not patient:
73
- continue
74
  analysis["full_name"] = patient.get("full_name", "Unknown")
75
  analysis["_id"] = str(analysis["_id"])
76
  enriched_results.append(analysis)
@@ -78,31 +57,15 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
78
  return enriched_results
79
 
80
  except Exception as e:
81
- logger.error(f"Error fetching analysis results: {str(e)} at {datetime.utcnow().isoformat()}")
82
  raise HTTPException(status_code=500, detail="Failed to retrieve analysis results")
83
 
84
- @router.get("/notifications")
85
- async def get_notifications(current_user: dict = Depends(get_current_user)):
86
- logger.info(f"Fetching notifications for {current_user['email']} at {datetime.utcnow().isoformat()}")
87
- try:
88
- notifications = await notifications_collection.find(
89
- {"recipient_email": current_user["email"]}
90
- ).sort("timestamp", -1).to_list(length=50)
91
- for notification in notifications:
92
- notification["_id"] = str(notification["_id"])
93
- if "timestamp" in notification:
94
- notification["timestamp"] = notification["timestamp"].isoformat()
95
- return notifications
96
- except Exception as e:
97
- logger.error(f"Error fetching notifications: {str(e)} at {datetime.utcnow().isoformat()}")
98
- raise HTTPException(status_code=500, detail="Failed to retrieve notifications")
99
-
100
  @router.post("/chat-stream")
101
  async def chat_stream_endpoint(
102
  request: ChatRequest,
103
  current_user: dict = Depends(get_current_user)
104
  ):
105
- logger.info(f"Chat stream initiated by {current_user['email']} at {datetime.utcnow().isoformat()}")
106
  async def token_stream():
107
  try:
108
  conversation = [{"role": "system", "content": agent.chat_prompt}]
@@ -128,7 +91,7 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
128
  yield chunk + " "
129
  await asyncio.sleep(0.05)
130
  except Exception as e:
131
- logger.error(f"Streaming error: {str(e)} at {datetime.utcnow().isoformat()}")
132
  yield f"⚠️ Error: {e}"
133
 
134
  return StreamingResponse(token_stream(), media_type="text/plain")
@@ -139,7 +102,7 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
139
  language: str = Query("en-US", description="Language code for speech recognition"),
140
  current_user: dict = Depends(get_current_user)
141
  ):
142
- logger.info(f"Voice transcription initiated by {current_user['email']} at {datetime.utcnow().isoformat()}")
143
  try:
144
  audio_data = await audio.read()
145
  if not audio.filename.lower().endswith(('.wav', '.mp3', '.ogg', '.flac')):
@@ -151,7 +114,7 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
151
  except HTTPException:
152
  raise
153
  except Exception as e:
154
- logger.error(f"Error in voice transcription: {str(e)} at {datetime.utcnow().isoformat()}")
155
  raise HTTPException(status_code=500, detail="Error processing voice input")
156
 
157
  @router.post("/voice/synthesize")
@@ -159,7 +122,7 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
159
  request: VoiceOutputRequest,
160
  current_user: dict = Depends(get_current_user)
161
  ):
162
- logger.info(f"Voice synthesis initiated by {current_user['email']} at {datetime.utcnow().isoformat()}")
163
  try:
164
  audio_data = text_to_speech(request.text, request.language, request.slow)
165
 
@@ -175,7 +138,7 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
175
  except HTTPException:
176
  raise
177
  except Exception as e:
178
- logger.error(f"Error in voice synthesis: {str(e)} at {datetime.utcnow().isoformat()}")
179
  raise HTTPException(status_code=500, detail="Error generating voice output")
180
 
181
  @router.post("/voice/chat")
@@ -186,7 +149,7 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
186
  max_new_tokens: int = Query(512, ge=50, le=1024),
187
  current_user: dict = Depends(get_current_user)
188
  ):
189
- logger.info(f"Voice chat initiated by {current_user['email']} at {datetime.utcnow().isoformat()}")
190
  try:
191
  audio_data = await audio.read()
192
  user_message = recognize_speech(audio_data, language)
@@ -209,7 +172,7 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
209
  except HTTPException:
210
  raise
211
  except Exception as e:
212
- logger.error(f"Error in voice chat: {str(e)} at {datetime.utcnow().isoformat()}")
213
  raise HTTPException(status_code=500, detail="Error processing voice chat")
214
 
215
  @router.post("/analyze-report")
@@ -220,7 +183,7 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
220
  max_new_tokens: int = Form(1024),
221
  current_user: dict = Depends(get_current_user)
222
  ):
223
- logger.info(f"Report analysis initiated by {current_user['email']} at {datetime.utcnow().isoformat()}")
224
  try:
225
  content_type = file.content_type
226
  allowed_types = [
@@ -266,25 +229,6 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
266
  if "timestamp" in analysis and isinstance(analysis["timestamp"], datetime):
267
  analysis["timestamp"] = analysis["timestamp"].isoformat()
268
 
269
- # Check risk level and create notification if moderate or high
270
- risk_level = analysis.get("suicide_risk", {}).get("level", "none")
271
- risk_score = analysis.get("suicide_risk", {}).get("score", 0)
272
- patient = await patients_collection.find_one({"fhir_id": patient_id})
273
- patient_name = patient.get("full_name", "Unknown Patient") if patient else "Unknown Patient"
274
- if risk_level in ["moderate", "high"]:
275
- notification = {
276
- "recipient_email": current_user["email"],
277
- "message": f"Patient {patient_name} has a {risk_level} risk with a score of {int(risk_score * 100)}%",
278
- "patient_id": patient_id,
279
- "risk_level": risk_level,
280
- "timestamp": datetime.utcnow(),
281
- "read": False
282
- }
283
- await notifications_collection.insert_one(notification)
284
- logger.info(f"Notification created for {current_user['email']} at {datetime.utcnow().isoformat()}: {notification['message']}")
285
- # Send push notification
286
- await send_push_notification(current_user["email"], notification["message"])
287
-
288
  return JSONResponse(content=jsonable_encoder({
289
  "status": "success",
290
  "analysis": analysis,
@@ -296,7 +240,7 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
296
  except HTTPException:
297
  raise
298
  except Exception as e:
299
- logger.error(f"Error in report analysis: {str(e)} at {datetime.utcnow().isoformat()}")
300
  raise HTTPException(
301
  status_code=500,
302
  detail=f"Failed to analyze report: {str(e)}"
@@ -307,30 +251,31 @@ def create_router(agent, logger, patients_collection, analysis_collection, users
307
  patient_id: str,
308
  current_user: dict = Depends(get_current_user)
309
  ):
310
- logger.info(f"Patient deletion initiated by {current_user['email']} at {datetime.utcnow().isoformat()} for patient {patient_id}")
311
  try:
 
312
  patient = await patients_collection.find_one({"fhir_id": patient_id})
313
  if not patient:
314
  raise HTTPException(status_code=404, detail="Patient not found")
315
 
 
316
  if patient.get("created_by") != current_user["email"] and not current_user.get("is_admin", False):
317
  raise HTTPException(status_code=403, detail="Not authorized to delete this patient")
318
 
 
319
  await analysis_collection.delete_many({"patient_id": patient_id})
320
- logger.info(f"Deleted analyses for patient {patient_id} at {datetime.utcnow().isoformat()}")
321
-
322
- await notifications_collection.delete_many({"patient_id": patient_id})
323
- logger.info(f"Deleted notifications for patient {patient_id} at {datetime.utcnow().isoformat()}")
324
 
 
325
  await patients_collection.delete_one({"fhir_id": patient_id})
326
- logger.info(f"Patient {patient_id} deleted successfully at {datetime.utcnow().isoformat()}")
327
 
328
- return {"status": "success", "message": f"Patient {patient_id} and associated data deleted"}
329
 
330
  except HTTPException:
331
  raise
332
  except Exception as e:
333
- logger.error(f"Error deleting patient {patient_id}: {str(e)} at {datetime.utcnow().isoformat()}")
334
  raise HTTPException(status_code=500, detail=f"Failed to delete patient: {str(e)}")
335
 
336
  return router
 
3
  from fastapi.encoders import jsonable_encoder
4
  from typing import Optional
5
  from models import ChatRequest, VoiceOutputRequest, RiskLevel
6
+ from auth import get_current_user
 
 
 
 
7
  from utils import clean_text_response
8
  from analysis import analyze_patient_report
9
  from voice import recognize_speech, text_to_speech, extract_text_from_pdf
10
  from docx import Document
11
  import re
12
  import io
13
+ from datetime import datetime
14
+ from bson import ObjectId
15
+ import asyncio
16
+ from bson.errors import InvalidId
17
 
18
+ def create_router(agent, logger, patients_collection, analysis_collection, users_collection):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  router = APIRouter()
20
 
21
  @router.get("/status")
22
  async def status(current_user: dict = Depends(get_current_user)):
23
+ logger.info(f"Status endpoint accessed by {current_user['email']}")
24
  return {
25
  "status": "running",
26
  "timestamp": datetime.utcnow().isoformat(),
 
33
  name: Optional[str] = Query(None),
34
  current_user: dict = Depends(get_current_user)
35
  ):
36
+ logger.info(f"Fetching analysis results by {current_user['email']}")
37
  try:
38
  query = {}
39
  if name:
 
49
  for analysis in analyses:
50
  patient = await patients_collection.find_one({"fhir_id": analysis.get("patient_id")})
51
  if not patient:
52
+ continue # Skip if patient no longer exists
53
  analysis["full_name"] = patient.get("full_name", "Unknown")
54
  analysis["_id"] = str(analysis["_id"])
55
  enriched_results.append(analysis)
 
57
  return enriched_results
58
 
59
  except Exception as e:
60
+ logger.error(f"Error fetching analysis results: {e}")
61
  raise HTTPException(status_code=500, detail="Failed to retrieve analysis results")
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  @router.post("/chat-stream")
64
  async def chat_stream_endpoint(
65
  request: ChatRequest,
66
  current_user: dict = Depends(get_current_user)
67
  ):
68
+ logger.info(f"Chat stream initiated by {current_user['email']}")
69
  async def token_stream():
70
  try:
71
  conversation = [{"role": "system", "content": agent.chat_prompt}]
 
91
  yield chunk + " "
92
  await asyncio.sleep(0.05)
93
  except Exception as e:
94
+ logger.error(f"Streaming error: {e}")
95
  yield f"⚠️ Error: {e}"
96
 
97
  return StreamingResponse(token_stream(), media_type="text/plain")
 
102
  language: str = Query("en-US", description="Language code for speech recognition"),
103
  current_user: dict = Depends(get_current_user)
104
  ):
105
+ logger.info(f"Voice transcription initiated by {current_user['email']}")
106
  try:
107
  audio_data = await audio.read()
108
  if not audio.filename.lower().endswith(('.wav', '.mp3', '.ogg', '.flac')):
 
114
  except HTTPException:
115
  raise
116
  except Exception as e:
117
+ logger.error(f"Error in voice transcription: {e}")
118
  raise HTTPException(status_code=500, detail="Error processing voice input")
119
 
120
  @router.post("/voice/synthesize")
 
122
  request: VoiceOutputRequest,
123
  current_user: dict = Depends(get_current_user)
124
  ):
125
+ logger.info(f"Voice synthesis initiated by {current_user['email']}")
126
  try:
127
  audio_data = text_to_speech(request.text, request.language, request.slow)
128
 
 
138
  except HTTPException:
139
  raise
140
  except Exception as e:
141
+ logger.error(f"Error in voice synthesis: {e}")
142
  raise HTTPException(status_code=500, detail="Error generating voice output")
143
 
144
  @router.post("/voice/chat")
 
149
  max_new_tokens: int = Query(512, ge=50, le=1024),
150
  current_user: dict = Depends(get_current_user)
151
  ):
152
+ logger.info(f"Voice chat initiated by {current_user['email']}")
153
  try:
154
  audio_data = await audio.read()
155
  user_message = recognize_speech(audio_data, language)
 
172
  except HTTPException:
173
  raise
174
  except Exception as e:
175
+ logger.error(f"Error in voice chat: {e}")
176
  raise HTTPException(status_code=500, detail="Error processing voice chat")
177
 
178
  @router.post("/analyze-report")
 
183
  max_new_tokens: int = Form(1024),
184
  current_user: dict = Depends(get_current_user)
185
  ):
186
+ logger.info(f"Report analysis initiated by {current_user['email']}")
187
  try:
188
  content_type = file.content_type
189
  allowed_types = [
 
229
  if "timestamp" in analysis and isinstance(analysis["timestamp"], datetime):
230
  analysis["timestamp"] = analysis["timestamp"].isoformat()
231
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  return JSONResponse(content=jsonable_encoder({
233
  "status": "success",
234
  "analysis": analysis,
 
240
  except HTTPException:
241
  raise
242
  except Exception as e:
243
+ logger.error(f"Error in report analysis: {str(e)}")
244
  raise HTTPException(
245
  status_code=500,
246
  detail=f"Failed to analyze report: {str(e)}"
 
251
  patient_id: str,
252
  current_user: dict = Depends(get_current_user)
253
  ):
254
+ logger.info(f"Patient deletion initiated by {current_user['email']} for patient {patient_id}")
255
  try:
256
+ # Check if the patient exists
257
  patient = await patients_collection.find_one({"fhir_id": patient_id})
258
  if not patient:
259
  raise HTTPException(status_code=404, detail="Patient not found")
260
 
261
+ # Check if the current user is authorized (e.g., created_by matches or is admin)
262
  if patient.get("created_by") != current_user["email"] and not current_user.get("is_admin", False):
263
  raise HTTPException(status_code=403, detail="Not authorized to delete this patient")
264
 
265
+ # Delete all analyses associated with this patient
266
  await analysis_collection.delete_many({"patient_id": patient_id})
267
+ logger.info(f"Deleted analyses for patient {patient_id}")
 
 
 
268
 
269
+ # Delete the patient
270
  await patients_collection.delete_one({"fhir_id": patient_id})
271
+ logger.info(f"Patient {patient_id} deleted successfully")
272
 
273
+ return {"status": "success", "message": f"Patient {patient_id} and associated analyses deleted"}
274
 
275
  except HTTPException:
276
  raise
277
  except Exception as e:
278
+ logger.error(f"Error deleting patient {patient_id}: {str(e)}")
279
  raise HTTPException(status_code=500, detail=f"Failed to delete patient: {str(e)}")
280
 
281
  return router