@@ -220,24 +220,61 @@ def get_patient(self, id: int) -> dict[str, Any]:
220220 patient = cast (dict [str , Any ], self ._api_request (f"patients/{ id } " ))
221221 return patient
222222
223+ def lookup_patient (
224+ self , * , email : str | None = None , external_id : str | None = None
225+ ) -> dict [str , Any ] | None :
226+ """Lookup a patient by email or external id.
227+
228+ Raises KeyError if no patient is found, otherwise returns patient record.
229+ """
230+ if email is None and external_id is None :
231+ raise TypeError (
232+ "Specify one of `email` or `external_id` to lookup a patient"
233+ )
234+ if email :
235+ for patient in self ._api_request (
236+ "patients/global_lookup" , params = {"email" : email }
237+ ):
238+ return patient
239+ raise KeyError (
240+ f"No patient found with external identifier: { external_id !r} "
241+ )
242+ elif external_id :
243+ # TODO: this should be a single lookup, but no API in JHE yet
244+ for patient in self .list_patients ():
245+ if patient ["identifier" ] == external_id :
246+ return patient
247+ raise KeyError (
248+ f"No patient found with external identifier: { external_id !r} "
249+ )
250+
223251 def get_patient_by_external_id (self , external_id : str ) -> dict [str , Any ]:
224252 """Get a single patient by external id.
225253
226254 For looking up the JHE Patient record by an external (e.g. EHR) patient id.
227255 """
256+ warnings .warn (
257+ f"get_patient_by_external_id is deprecated, use lookup_patient(external_id={ external_id !r} )" ,
258+ DeprecationWarning ,
259+ stacklevel = 2 ,
260+ )
261+ return self .lookup_patient (external_id = external_id )
228262
229- # TODO: this should be a single lookup, but no API in JHE yet
230- for patient in self .list_patients ():
231- if patient ["identifier" ] == external_id :
232- return patient
233- raise KeyError (f"No patient found with external identifier: { external_id !r} " )
234-
235- def list_patients (self ) -> Generator [dict [str , dict [str , Any ]]]:
263+ def list_patients (
264+ self , organization_id : int | None = None , study_id : int | None = None
265+ ) -> Generator [dict [str , dict [str , Any ]]]:
236266 """Iterate over all patients.
237267
268+ Optionally filter by organization or study id.
269+
238270 Patient ids are the keys that may be passed to e.g. :meth:`list_observations`.
239271 """
240- yield from self ._list_api_request ("patients" )
272+ params = {}
273+ if organization_id is not None :
274+ params ["organization_id" ] = organization_id
275+ if study_id is not None :
276+ params ["study_id" ] = study_id
277+ yield from self ._list_api_request ("patients" , params = params )
241278
242279 def get_patient_consents (self , patient_id : int ) -> dict [str , Any ]:
243280 """Return patient consent status.
@@ -330,12 +367,17 @@ def get_study(self, id: int) -> dict[str, Any]:
330367 """
331368 return cast (dict [str , Any ], self ._api_request (f"studies/{ id } " ))
332369
333- def list_studies (self ) -> Generator [dict [str , dict [str , Any ]]]:
370+ def list_studies (
371+ self , * , organization_id : int | None = None
372+ ) -> Generator [dict [str , dict [str , Any ]]]:
334373 """Iterate over studies.
335374
336375 Only returns studies I have access to (i.e. owned by my organization(s)).
337376 """
338- return self ._list_api_request ("studies" )
377+ params = {}
378+ if organization_id is not None :
379+ params ["organization_id" ] = organization_id
380+ return self ._list_api_request ("studies" , params = params )
339381
340382 def get_organization (self , id : int ) -> dict [str , Any ]:
341383 """Get a single organization by id.
@@ -366,6 +408,10 @@ def list_organizations(self) -> Generator[dict[str, dict[str, Any]]]:
366408 """
367409 return self ._list_api_request ("organizations" )
368410
411+ def list_data_sources (self ) -> Generator [dict [str , dict [str , Any ]]]:
412+ """List all registered data sources"""
413+ return self ._list_api_request ("data_sources" )
414+
369415 def list_observations (
370416 self ,
371417 patient_id : int | None = None ,
0 commit comments