@@ -83,14 +83,23 @@ FhirResource <- R6::R6Class(
8383 # ' @param include_recommendations Logical. Default `FALSE`.
8484 # ' @param recommendations_limit Integer. Default `5L`.
8585 # ' @param include_quality Logical. Default `FALSE`.
86+ # ' @param as_tibble Logical. When `TRUE`, returns a [tibble::tibble]
87+ # ' with one row per input coding and flat columns for the source
88+ # ' concept, standard concept, target CDM table, mapping type, and
89+ # ' resolution status. The batch `summary` (total/resolved/failed)
90+ # ' is attached as `attr(result, "summary")`. Default `FALSE`
91+ # ' keeps the legacy list-shaped return.
8692 # '
87- # ' @returns A list with `results` (per-item) and `summary`
88- # ' (total/resolved/failed).
93+ # ' @returns When `as_tibble = FALSE` (default), a list with `results`
94+ # ' (per-item) and `summary` (total/resolved/failed). When
95+ # ' `as_tibble = TRUE`, a [tibble::tibble] suitable for
96+ # ' `dplyr`/`tidyr` pipelines.
8997 resolve_batch = function (codings ,
9098 resource_type = NULL ,
9199 include_recommendations = FALSE ,
92100 recommendations_limit = 5L ,
93- include_quality = FALSE ) {
101+ include_quality = FALSE ,
102+ as_tibble = FALSE ) {
94103 stopifnot(is.list(codings ), length(codings ) > = 1 , length(codings ) < = 100 )
95104 if (! all(vapply(codings , is.list , logical (1 )))) {
96105 cli :: cli_abort(c(
@@ -108,7 +117,12 @@ FhirResource <- R6::R6Class(
108117 }
109118 if (isTRUE(include_quality )) body $ include_quality <- TRUE
110119
111- perform_post(private $ .base_req , " fhir/resolve/batch" , body = body )
120+ result <- perform_post(private $ .base_req , " fhir/resolve/batch" , body = body )
121+
122+ if (isTRUE(as_tibble )) {
123+ return (fhir_batch_to_tibble(result , codings ))
124+ }
125+ result
112126 },
113127
114128 # ' @description
@@ -168,3 +182,169 @@ compact_list <- function(...) {
168182 args <- list (... )
169183 args [! vapply(args , is.null , logical (1 ))]
170184}
185+
186+
187+ # ' Flatten a batch resolver response into a tibble.
188+ # '
189+ # ' Internal helper used by `FhirResource$resolve_batch(as_tibble = TRUE)`.
190+ # ' Produces one row per input coding with flat columns for the source
191+ # ' concept, standard concept, target CDM table, and status. The original
192+ # ' `summary` list is attached to the returned tibble via
193+ # ' `attr(x, "summary")`.
194+ # '
195+ # ' @param result The raw list result from `perform_post`.
196+ # ' @param codings The original input `codings` list, used to align rows
197+ # ' when items fail resolution and no `source_concept` is returned.
198+ # ' @returns A [tibble::tibble].
199+ # ' @keywords internal
200+ fhir_batch_to_tibble <- function (result , codings ) {
201+ items <- result $ results %|| % list ()
202+ n <- length(codings )
203+
204+ make_row <- function (i ) {
205+ input_coding <- codings [[i ]]
206+ item <- if (i < = length(items )) items [[i ]] else NULL
207+
208+ # Failed items: the API returns them without a resolution block.
209+ resolution <- item $ resolution
210+ if (is.null(resolution )) {
211+ return (tibble :: tibble(
212+ source_system = input_coding $ system %|| % NA_character_ ,
213+ source_code = input_coding $ code %|| % NA_character_ ,
214+ source_concept_id = NA_integer_ ,
215+ source_concept_name = NA_character_ ,
216+ standard_concept_id = NA_integer_ ,
217+ standard_concept_name = NA_character_ ,
218+ standard_vocabulary_id = NA_character_ ,
219+ domain_id = NA_character_ ,
220+ target_table = NA_character_ ,
221+ mapping_type = NA_character_ ,
222+ similarity_score = NA_real_ ,
223+ status = " failed" ,
224+ status_detail = item $ error %|| % " unresolved"
225+ ))
226+ }
227+
228+ src <- resolution $ source_concept %|| % list ()
229+ std <- resolution $ standard_concept %|| % list ()
230+
231+ tibble :: tibble(
232+ source_system = input_coding $ system %|| % NA_character_ ,
233+ source_code = input_coding $ code %|| % NA_character_ ,
234+ source_concept_id = src $ concept_id %|| % NA_integer_ ,
235+ source_concept_name = src $ concept_name %|| % NA_character_ ,
236+ standard_concept_id = std $ concept_id %|| % NA_integer_ ,
237+ standard_concept_name = std $ concept_name %|| % NA_character_ ,
238+ standard_vocabulary_id = std $ vocabulary_id %|| % NA_character_ ,
239+ domain_id = std $ domain_id %|| % NA_character_ ,
240+ target_table = resolution $ target_table %|| % NA_character_ ,
241+ mapping_type = resolution $ mapping_type %|| % NA_character_ ,
242+ similarity_score = resolution $ similarity_score %|| % NA_real_ ,
243+ status = " resolved" ,
244+ status_detail = NA_character_
245+ )
246+ }
247+
248+ rows <- lapply(seq_len(n ), make_row )
249+ tbl <- do.call(rbind , rows )
250+ attr(tbl , " summary" ) <- result $ summary
251+ tbl
252+ }
253+
254+
255+ # ' Null-coalescing operator.
256+ # ' @noRd
257+ `%||%` <- function (a , b ) if (is.null(a )) b else a
258+
259+
260+ # =============================================================================
261+ # Standalone wrapper functions
262+ #
263+ # Thin wrappers around the R6 `FhirResource` methods so users can write
264+ # pipe-friendly code without dereferencing the client every call:
265+ #
266+ # client |> fhir_resolve(system = "http://snomed.info/sct", code = "44054006")
267+ #
268+ # These forward all arguments to the underlying R6 method.
269+ # =============================================================================
270+
271+ # ' Resolve a FHIR Coding to an OMOP standard concept
272+ # '
273+ # ' Standalone wrapper for `client$fhir$resolve()`. See
274+ # ' [FhirResource] for full parameter documentation.
275+ # '
276+ # ' @param client An [OMOPHubClient] instance.
277+ # ' @param ... Arguments passed to `FhirResource$resolve()`.
278+ # ' @returns A list with `input` and `resolution`.
279+ # ' @seealso [FhirResource], [fhir_resolve_batch()],
280+ # ' [fhir_resolve_codeable_concept()]
281+ # ' @examples
282+ # ' \dontrun{
283+ # ' client <- OMOPHubClient$new(api_key = Sys.getenv("OMOPHUB_API_KEY"))
284+ # ' fhir_resolve(
285+ # ' client,
286+ # ' system = "http://snomed.info/sct",
287+ # ' code = "44054006",
288+ # ' resource_type = "Condition"
289+ # ' )
290+ # ' }
291+ # ' @export
292+ fhir_resolve <- function (client , ... ) {
293+ client $ fhir $ resolve(... )
294+ }
295+
296+
297+ # ' Batch-resolve FHIR Codings
298+ # '
299+ # ' Standalone wrapper for `client$fhir$resolve_batch()`. When
300+ # ' `as_tibble = TRUE`, the result is a flat `tibble` suitable for
301+ # ' `dplyr`/`tidyr` pipelines.
302+ # '
303+ # ' @param client An [OMOPHubClient] instance.
304+ # ' @param ... Arguments passed to `FhirResource$resolve_batch()`.
305+ # ' @returns See `FhirResource$resolve_batch()`.
306+ # ' @seealso [FhirResource], [fhir_resolve()],
307+ # ' [fhir_resolve_codeable_concept()]
308+ # ' @examples
309+ # ' \dontrun{
310+ # ' client <- OMOPHubClient$new(api_key = Sys.getenv("OMOPHUB_API_KEY"))
311+ # ' tbl <- fhir_resolve_batch(
312+ # ' client,
313+ # ' codings = list(
314+ # ' list(system = "http://snomed.info/sct", code = "44054006"),
315+ # ' list(system = "http://loinc.org", code = "2339-0")
316+ # ' ),
317+ # ' as_tibble = TRUE
318+ # ' )
319+ # ' dplyr::filter(tbl, status == "resolved")
320+ # ' }
321+ # ' @export
322+ fhir_resolve_batch <- function (client , ... ) {
323+ client $ fhir $ resolve_batch(... )
324+ }
325+
326+
327+ # ' Resolve a FHIR CodeableConcept with vocabulary preference
328+ # '
329+ # ' Standalone wrapper for `client$fhir$resolve_codeable_concept()`.
330+ # '
331+ # ' @param client An [OMOPHubClient] instance.
332+ # ' @param ... Arguments passed to `FhirResource$resolve_codeable_concept()`.
333+ # ' @returns See `FhirResource$resolve_codeable_concept()`.
334+ # ' @seealso [FhirResource], [fhir_resolve()], [fhir_resolve_batch()]
335+ # ' @examples
336+ # ' \dontrun{
337+ # ' client <- OMOPHubClient$new(api_key = Sys.getenv("OMOPHUB_API_KEY"))
338+ # ' fhir_resolve_codeable_concept(
339+ # ' client,
340+ # ' coding = list(
341+ # ' list(system = "http://snomed.info/sct", code = "44054006"),
342+ # ' list(system = "http://hl7.org/fhir/sid/icd-10-cm", code = "E11.9")
343+ # ' ),
344+ # ' resource_type = "Condition"
345+ # ' )
346+ # ' }
347+ # ' @export
348+ fhir_resolve_codeable_concept <- function (client , ... ) {
349+ client $ fhir $ resolve_codeable_concept(... )
350+ }
0 commit comments