dylanglenister commited on
Commit
521a291
·
1 Parent(s): 56e2429

Updating patient routes

Browse files
src/api/routes/patient.py CHANGED
@@ -1,97 +1,107 @@
1
- # api/routes/patient.py
2
 
3
- from bson import ObjectId
4
- from bson.errors import InvalidId
5
- from fastapi import APIRouter, HTTPException
6
 
7
- from src.data.repositories.patient import (create_patient, get_patient_by_id,
8
- search_patients,
9
- update_patient_profile)
10
- from src.data.repositories.session import list_patient_sessions
11
- from src.models.user import PatientCreateRequest, PatientUpdateRequest
12
  from src.utils.logger import logger
13
 
14
  router = APIRouter(prefix="/patient", tags=["Patient"])
15
 
16
- @router.post("")
17
- async def create_patient_profile(req: PatientCreateRequest):
18
- try:
19
- logger().info(f"POST /patient name={req.name}")
20
- patient_id = create_patient(
21
- req.name,
22
- req.age,
23
- req.sex,
24
- req.ethnicity,
25
- req.address,
26
- req.phone,
27
- req.email,
28
- req.medications,
29
- req.past_assessment_summary,
30
- req.assigned_doctor_id
31
- )
32
- #patient_id["_id"] = str(patient_id.get("_id")) if patient_id.get("_id") else None
33
- logger().info(f"Created patient {req.name} id={patient_id}")
34
- return { "patient_id": patient_id }
35
- except Exception as e:
36
- logger().error(f"Error creating patient: {e}")
37
- raise HTTPException(status_code=500, detail=str(e))
38
-
39
- @router.get("/search")
40
- async def search_patients_route(q: str, limit: int = 20):
41
- try:
42
- logger().info(f"GET /patient/search q='{q}' limit={limit}")
43
- results = search_patients(q, limit=limit)
44
- logger().info(f"Search returned {len(results)} results")
45
- return {"results": results}
46
- except Exception as e:
47
- logger().error(f"Error searching patients: {e}")
48
- raise HTTPException(status_code=500, detail=str(e))
49
-
50
- @router.get("/{patient_id}")
51
- async def get_patient(patient_id: str):
52
- try:
53
- logger().info(f"GET /patient/{patient_id}")
54
- try:
55
- patient = get_patient_by_id(patient_id)
56
- except InvalidId:
57
- raise HTTPException(status_code=400, detail="Invalid patient ID format")
58
-
59
- if not patient:
60
- raise HTTPException(status_code=404, detail="Patient not found")
61
-
62
- return patient
63
- except HTTPException:
64
- raise
65
- except Exception as e:
66
- logger().error(f"Error getting patient: {e}")
67
- raise HTTPException(status_code=500, detail=str(e))
68
-
69
- @router.patch("/{patient_id}")
70
- async def update_patient(patient_id: str, req: PatientUpdateRequest):
71
- try:
72
- # Validate ObjectId format
73
- if not ObjectId.is_valid(patient_id):
74
- raise HTTPException(status_code=400, detail="Invalid patient ID format")
75
-
76
- payload = {k: v for k, v in req.model_dump().items() if v is not None}
77
- logger().info(f"PATCH /patient/{patient_id} fields={list(payload.keys())}")
78
- modified = update_patient_profile(patient_id, payload)
79
- if modified == 0:
80
- return {"message": "No changes"}
81
- return {"message": "Updated"}
82
- except HTTPException:
83
- raise
84
- except Exception as e:
85
- logger().error(f"Error updating patient: {e}")
86
- raise HTTPException(status_code=500, detail=str(e))
87
-
88
- @router.get("/{patient_id}/session")
89
- async def list_sessions_for_patient(patient_id: str):
90
- """List sessions for a patient from Mongo"""
91
- try:
92
- logger().info(f"GET /patient/{patient_id}/session")
93
- sessions = list_patient_sessions(patient_id)
94
- return {"sessions": sessions}
95
- except Exception as e:
96
- logger().error(f"Error listing sessions: {e}")
97
- raise HTTPException(status_code=500, detail=str(e))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/api/routes/patient.py
2
 
3
+ from fastapi import APIRouter, Depends, HTTPException, status
 
 
4
 
5
+ from src.core.state import AppState, get_state
6
+ from src.models.patient import (Patient, PatientCreateRequest,
7
+ PatientUpdateRequest)
8
+ from src.models.session import Session
 
9
  from src.utils.logger import logger
10
 
11
  router = APIRouter(prefix="/patient", tags=["Patient"])
12
 
13
+
14
+ @router.get("", response_model=list[Patient])
15
+ async def search_or_get_all_patients(
16
+ q: str | None = None,
17
+ limit: int = 20,
18
+ state: AppState = Depends(get_state)
19
+ ):
20
+ """
21
+ Searches for patients by name if a query 'q' is provided.
22
+ Otherwise, this endpoint would list all patients (functionality not yet in repo).
23
+ """
24
+ logger().info(f"GET /patient?q='{q}' limit={limit}")
25
+ if not q:
26
+ # In the future, you could add a get_all_patients method here.
27
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="A search query 'q' is required.")
28
+
29
+ patients = state.memory_manager.search_patients(q, limit=limit)
30
+ logger().info(f"Search returned {len(patients)} results")
31
+ return patients
32
+
33
+
34
+ @router.post("", response_model=Patient, status_code=status.HTTP_201_CREATED)
35
+ async def create_patient_profile(
36
+ req: PatientCreateRequest,
37
+ state: AppState = Depends(get_state)
38
+ ):
39
+ """Creates a new patient profile."""
40
+ logger().info(f"POST /patient name={req.name}")
41
+ patient_id = state.memory_manager.create_patient(**req.model_dump())
42
+
43
+ if not patient_id:
44
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Patient could not be created due to invalid data.")
45
+
46
+ new_patient = state.memory_manager.get_patient_by_id(patient_id)
47
+ if not new_patient:
48
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Could not find newly created patient.")
49
+
50
+ logger().info(f"Created patient {req.name} id={patient_id}")
51
+ return new_patient
52
+
53
+
54
+ @router.get("/{patient_id}", response_model=Patient)
55
+ async def get_patient_by_id(
56
+ patient_id: str,
57
+ state: AppState = Depends(get_state)
58
+ ):
59
+ """Retrieves a single patient by their unique ID."""
60
+ logger().info(f"GET /patient/{patient_id}")
61
+ patient = state.memory_manager.get_patient_by_id(patient_id)
62
+ if not patient:
63
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Patient not found")
64
+ return patient
65
+
66
+
67
+ @router.patch("/{patient_id}", response_model=Patient)
68
+ async def update_patient(
69
+ patient_id: str,
70
+ req: PatientUpdateRequest,
71
+ state: AppState = Depends(get_state)
72
+ ):
73
+ """Updates a patient's profile with the provided fields."""
74
+ # Get only the fields that the client actually sent
75
+ updates = req.model_dump(exclude_unset=True)
76
+ if not updates:
77
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="No update fields provided.")
78
+
79
+ logger().info(f"PATCH /patient/{patient_id} fields={list(updates.keys())}")
80
+ modified_count = state.memory_manager.update_patient_profile(patient_id, updates)
81
+
82
+ if modified_count == 0:
83
+ # Check if the patient exists to differentiate between "not found" and "no changes made"
84
+ if not state.memory_manager.get_patient_by_id(patient_id):
85
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Patient not found")
86
+
87
+ # Return the full, updated patient object
88
+ updated_patient = state.memory_manager.get_patient_by_id(patient_id)
89
+ if not updated_patient:
90
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Could not find patient after update.")
91
+
92
+ return updated_patient
93
+
94
+
95
+ @router.get("/{patient_id}/session", response_model=list[Session])
96
+ async def list_sessions_for_patient(
97
+ patient_id: str,
98
+ state: AppState = Depends(get_state)
99
+ ):
100
+ """Lists all chat sessions associated with a specific patient."""
101
+ logger().info(f"GET /patient/{patient_id}/session")
102
+ # First, verify the patient exists
103
+ if not state.memory_manager.get_patient_by_id(patient_id):
104
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Patient not found")
105
+
106
+ sessions = state.memory_manager.list_patient_sessions(patient_id)
107
+ return sessions
src/core/memory_manager.py CHANGED
@@ -3,8 +3,10 @@
3
  from src.data.connection import ActionFailed
4
  from src.data.repositories import account as account_repo
5
  from src.data.repositories import medical_memory as memory_repo
 
6
  from src.data.repositories import session as session_repo
7
  from src.models.account import Account
 
8
  from src.models.session import Session
9
  from src.services import summariser
10
  from src.services.nvidia import nvidia_chat
@@ -60,6 +62,39 @@ class MemoryManager:
60
  except ActionFailed as e:
61
  logger().error(f"Failed to search accounts in MemoryManager: {e}")
62
  return []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  # --- Session Management Facade ---
65
 
@@ -95,6 +130,14 @@ class MemoryManager:
95
  logger().error(f"Failed to update title for session '{session_id}': {e}")
96
  return False
97
 
 
 
 
 
 
 
 
 
98
  # --- Core Business Logic ---
99
 
100
  async def process_medical_exchange(
 
3
  from src.data.connection import ActionFailed
4
  from src.data.repositories import account as account_repo
5
  from src.data.repositories import medical_memory as memory_repo
6
+ from src.data.repositories import patient as patient_repo
7
  from src.data.repositories import session as session_repo
8
  from src.models.account import Account
9
+ from src.models.patient import Patient
10
  from src.models.session import Session
11
  from src.services import summariser
12
  from src.services.nvidia import nvidia_chat
 
62
  except ActionFailed as e:
63
  logger().error(f"Failed to search accounts in MemoryManager: {e}")
64
  return []
65
+ # --- Patient Management Facade ---
66
+
67
+ def create_patient(self, **kwargs) -> str | None:
68
+ """Creates a new patient record."""
69
+ try:
70
+ return patient_repo.create_patient(**kwargs)
71
+ except ActionFailed as e:
72
+ logger().error(f"Failed to create patient in MemoryManager: {e}")
73
+ return None
74
+
75
+ def get_patient_by_id(self, patient_id: str) -> Patient | None:
76
+ """Retrieves a patient by their unique ID."""
77
+ try:
78
+ return patient_repo.get_patient_by_id(patient_id)
79
+ except ActionFailed as e:
80
+ logger().error(f"Failed to get patient '{patient_id}' in MemoryManager: {e}")
81
+ return None
82
+
83
+ def update_patient_profile(self, patient_id: str, updates: dict) -> int:
84
+ """Updates a patient's profile."""
85
+ try:
86
+ return patient_repo.update_patient_profile(patient_id, updates)
87
+ except ActionFailed as e:
88
+ logger().error(f"Failed to update patient '{patient_id}' in MemoryManager: {e}")
89
+ return 0
90
+
91
+ def search_patients(self, query: str, limit: int = 10) -> list[Patient]:
92
+ """Searches for patients by name."""
93
+ try:
94
+ return patient_repo.search_patients(query, limit=limit)
95
+ except ActionFailed as e:
96
+ logger().error(f"Failed to search patients in MemoryManager: {e}")
97
+ return []
98
 
99
  # --- Session Management Facade ---
100
 
 
130
  logger().error(f"Failed to update title for session '{session_id}': {e}")
131
  return False
132
 
133
+ def list_patient_sessions(self, patient_id: str) -> list[Session]:
134
+ """Retrieves all sessions for a specific patient."""
135
+ try:
136
+ return session_repo.list_patient_sessions(patient_id, limit=self.max_sessions_per_user)
137
+ except ActionFailed as e:
138
+ logger().error(f"Failed to get sessions for patient '{patient_id}': {e}")
139
+ return []
140
+
141
  # --- Core Business Logic ---
142
 
143
  async def process_medical_exchange(
src/models/patient.py CHANGED
@@ -2,6 +2,8 @@
2
 
3
  from datetime import datetime
4
 
 
 
5
  from src.models.common import BaseMongoModel, PyObjectId
6
 
7
 
@@ -19,3 +21,27 @@ class Patient(BaseMongoModel):
19
  medications: list[str] | None = None
20
  past_assessment_summary: str | None = None
21
  assigned_doctor_id: PyObjectId | None = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  from datetime import datetime
4
 
5
+ from pydantic import BaseModel
6
+
7
  from src.models.common import BaseMongoModel, PyObjectId
8
 
9
 
 
21
  medications: list[str] | None = None
22
  past_assessment_summary: str | None = None
23
  assigned_doctor_id: PyObjectId | None = None
24
+
25
+ class PatientCreateRequest(BaseModel):
26
+ name: str
27
+ age: int
28
+ sex: str
29
+ ethnicity: str
30
+ address: str | None = None
31
+ phone: str | None = None
32
+ email: str | None = None
33
+ medications: list[str] | None = None
34
+ past_assessment_summary: str | None = None
35
+ assigned_doctor_id: str | None = None
36
+
37
+ class PatientUpdateRequest(BaseModel):
38
+ name: str | None = None
39
+ age: int | None = None
40
+ sex: str | None = None
41
+ ethnicity: str | None = None
42
+ address: str | None = None
43
+ phone: str | None = None
44
+ email: str | None = None
45
+ medications: list[str] | None = None
46
+ past_assessment_summary: str | None = None
47
+ assigned_doctor_id: str | None = None
src/models/user.py DELETED
@@ -1,28 +0,0 @@
1
- # src/model/user.py
2
-
3
- from pydantic import BaseModel
4
-
5
-
6
- class PatientCreateRequest(BaseModel):
7
- name: str
8
- age: int
9
- sex: str
10
- ethnicity: str
11
- address: str | None = None
12
- phone: str | None = None
13
- email: str | None = None
14
- medications: list[str] | None = None
15
- past_assessment_summary: str | None = None
16
- assigned_doctor_id: str | None = None
17
-
18
- class PatientUpdateRequest(BaseModel):
19
- name: str | None = None
20
- age: int | None = None
21
- sex: str | None = None
22
- ethnicity: str | None = None
23
- address: str | None = None
24
- phone: str | None = None
25
- email: str | None = None
26
- medications: list[str] | None = None
27
- past_assessment_summary: str | None = None
28
- assigned_doctor_id: str | None = None