@@ -103,6 +103,7 @@ def __init__(
103103 base_url : str = SANDBOX_URL ,
104104 nhsd_session_urid : str | None = None ,
105105 timeout : int = 10 ,
106+ ignore_dates : bool = False ,
106107 ) -> None :
107108 """
108109 Create a PDS client.
@@ -113,12 +114,15 @@ def __init__(
113114 :attr:`INT_URL`, :attr:`PROD_URL`). Trailing slashes are stripped.
114115 :param nhsd_session_urid: Optional ``NHSD-Session-URID`` header value.
115116 :param timeout: Default timeout in seconds for HTTP calls.
117+ :param ignore_dates: If ``True`` just get the most recent name or GP record,
118+ ignoring the date ranges.
116119 """
117120 self .auth_token = auth_token
118121 self .end_user_org_ods = end_user_org_ods
119122 self .base_url = base_url .rstrip ("/" )
120123 self .nhsd_session_urid = nhsd_session_urid
121124 self .timeout = timeout
125+ self .ignore_dates = ignore_dates
122126
123127 def _build_headers (
124128 self ,
@@ -134,12 +138,15 @@ def _build_headers(
134138 :return: Dictionary of HTTP headers for the outbound request.
135139 """
136140 headers = {
137- "Authorization" : f"Bearer { self .auth_token } " ,
138141 "X-Request-ID" : request_id or str (uuid .uuid4 ()),
139142 "NHSD-End-User-Organisation-ODS" : self .end_user_org_ods ,
140143 "Accept" : "application/fhir+json" ,
141144 }
142145
146+ # Trying to pass an auth token to the sandbox makes PDS unhappy
147+ if self .base_url != self .SANDBOX_URL :
148+ headers ["Authorization" ] = f"Bearer { self .auth_token } "
149+
143150 # NHSD-Session-URID is required in some flows; include only if configured.
144151 if self .nhsd_session_urid :
145152 headers ["NHSD-Session-URID" ] = self .nhsd_session_urid
@@ -200,8 +207,7 @@ def search_patient_by_nhs_number(
200207
201208 # --------------- internal helpers for result extraction -----------------
202209
203- @staticmethod
204- def _get_gp_ods_code (general_practitioners : ResultList ) -> str | None :
210+ def _get_gp_ods_code (self , general_practitioners : ResultList ) -> str | None :
205211 """
206212 Extract the current GP ODS code from ``Patient.generalPractitioner``.
207213
@@ -221,7 +227,7 @@ def _get_gp_ods_code(general_practitioners: ResultList) -> str | None:
221227 if len (general_practitioners ) == 0 :
222228 return None
223229
224- gp = find_current_gp (general_practitioners )
230+ gp = self . find_current_gp (general_practitioners )
225231 if gp is None :
226232 return None
227233
@@ -272,7 +278,7 @@ def _extract_single_search_result(
272278
273279 # Select current name record and extract names.
274280 names = cast ("ResultList" , patient .get ("name" , []))
275- current_name = find_current_name_record (names )
281+ current_name = self . find_current_name_record (names )
276282 if current_name is None :
277283 raise RuntimeError ("PDS patient has no current name record" )
278284
@@ -291,79 +297,88 @@ def _extract_single_search_result(
291297 gp_ods_code = gp_ods_code ,
292298 )
293299
300+ def find_current_gp (
301+ self , records : ResultList , today : date | None = None
302+ ) -> ResultStructureDict | None :
303+ """
304+ Select the current record from a ``generalPractitioner`` list.
294305
295- def find_current_gp (
296- records : ResultList , today : date | None = None
297- ) -> ResultStructureDict | None :
298- """
299- Select the current record from a ``generalPractitioner`` list.
306+ A record is "current" if its ``identifier.period`` covers ``today`` (inclusive):
300307
301- A record is "current" if its ``identifier.period`` covers `` today`` (inclusive):
308+ ``start <= today <= end``
302309
303- ``start <= today <= end``
310+ Or else if self.ignore_dates is True, the last record in the list is returned.
304311
305- The list may be in any of the following states:
312+ The list may be in any of the following states:
306313
307- * empty
308- * contains one or more records, none current
309- * contains one or more records, exactly one current
314+ * empty
315+ * contains one or more records, none current
316+ * contains one or more records, exactly one current
310317
311- :param records: List of ``generalPractitioner`` records.
312- :param today: Optional override date, intended for deterministic tests.
313- If not supplied, the current UTC date is used.
314- :return: The first record whose ``identifier.period`` covers ``today``, or ``None``
315- if no record is current.
316- :raises KeyError: If required keys are missing for a record being evaluated.
317- :raises ValueError: If ``start`` or ``end`` are not valid ISO date strings.
318- """
319- if today is None :
320- today = datetime .now (timezone .utc ).date ()
318+ :param records: List of ``generalPractitioner`` records.
319+ :param today: Optional override date, intended for deterministic tests.
320+ If not supplied, the current UTC date is used.
321+ :return: The first record whose ``identifier.period`` covers ``today``, or
322+ ``None`` if no record is current.
323+ :raises KeyError: If required keys are missing for a record being evaluated.
324+ :raises ValueError: If ``start`` or ``end`` are not valid ISO date strings.
325+ """
326+ if today is None :
327+ today = datetime .now (timezone .utc ).date ()
321328
322- for record in records :
323- identifier = cast ("ResultStructureDict" , record ["identifier" ])
324- periods = cast ("dict[str, str]" , identifier ["period" ])
325- start_str = periods ["start" ]
326- end_str = periods ["end" ]
329+ if self .ignore_dates :
330+ return records [- 1 ]
327331
328- start = date .fromisoformat (start_str )
329- end = date .fromisoformat (end_str )
332+ for record in records :
333+ identifier = cast ("ResultStructureDict" , record ["identifier" ])
334+ periods = cast ("dict[str, str]" , identifier ["period" ])
335+ start_str = periods ["start" ]
336+ end_str = periods ["end" ]
330337
331- if start <= today <= end :
332- return record
338+ start = date . fromisoformat ( start_str )
339+ end = date . fromisoformat ( end_str )
333340
334- return None
341+ if start <= today <= end :
342+ return record
335343
344+ return None
336345
337- def find_current_name_record (
338- records : ResultList , today : date | None = None
339- ) -> ResultStructureDict | None :
340- """
341- Select the current record from a ``Patient.name`` list.
346+ def find_current_name_record (
347+ self , records : ResultList , today : date | None = None
348+ ) -> ResultStructureDict | None :
349+ """
350+ Select the current record from a ``Patient.name`` list.
342351
343- A record is "current" if its ``period`` covers ``today`` (inclusive):
352+ A record is "current" if its ``period`` covers ``today`` (inclusive):
344353
345- ``start <= today <= end``
354+ ``start <= today <= end``
346355
347- :param records: List of ``Patient.name`` records.
348- :param today: Optional override date, intended for deterministic tests.
349- If not supplied, the current UTC date is used.
350- :return: The first name record whose ``period`` covers ``today``, or ``None`` if no
351- record is current.
352- :raises KeyError: If required keys (``period.start`` / ``period.end``) are missing.
353- :raises ValueError: If ``start`` or ``end`` are not valid ISO date strings.
354- """
355- if today is None :
356- today = datetime .now (timezone .utc ).date ()
356+ Or else if self.ignore_dates is True, the last record in the list is returned.
357+
358+ :param records: List of ``Patient.name`` records.
359+ :param today: Optional override date, intended for deterministic tests.
360+ If not supplied, the current UTC date is used.
361+ :return: The first name record whose ``period`` covers ``today``, or ``None`` if
362+ no record is current.
363+ :raises KeyError: If required keys (``period.start`` / ``period.end``) are
364+ missing.
365+ :raises ValueError: If ``start`` or ``end`` are not valid ISO date strings.
366+ """
367+ if today is None :
368+ today = datetime .now (timezone .utc ).date ()
369+
370+ if self .ignore_dates :
371+ return records [- 1 ]
357372
358- for record in records :
359- periods = cast ("dict[str, str]" , record ["period" ])
360- start_str = periods ["start" ]
361- end_str = periods ["end" ]
373+ for record in records :
374+ periods = cast ("dict[str, str]" , record ["period" ])
375+ start_str = periods ["start" ]
376+ end_str = periods ["end" ]
362377
363- start = date .fromisoformat (start_str )
364- end = date .fromisoformat (end_str )
378+ start = date .fromisoformat (start_str )
379+ end = date .fromisoformat (end_str )
365380
366- if start <= today <= end :
367- return record
381+ if start <= today <= end :
382+ return record
368383
369- return None
384+ return None
0 commit comments