Skip to content

Releases: f-slg/fhirviews

fhirviews 0.3.0: terminologías, catálogo de vistas y motor DuckDB ampliado

Choose a tag to compare

@f-slg f-slg released this 20 Oct 02:22

fhirviews 0.3.0 suma tres piezas para el trabajo real en salud pública:

  • un motor DuckDB mucho más completo, que cubre todo el perfil shareable y la mayor parte de FHIRPath N1 con la misma semántica que fhirpath.js;
  • terminologías: $expand, $lookup y $validate-code, y vistas definidas con un ValueSet;
  • un catálogo de siete vistas listas para vigilancia.

También corrige un error que afectaba a las operaciones ($expand, $everything, $export...) en algunos servidores.

No hay cambios incompatibles: el código escrito para 0.2.0 funciona igual.

# install.packages("pak")
pak::pak("f-slg/fhirviews@v0.3.0")

📖 Documentación: https://f-slg.github.io/fhirviews/ · Informe de conformidad publicado: https://f-slg.github.io/fhirviews/sof-test-results.json


Lo nuevo

📚 Catálogo de vistas para salud pública

vista_catalogo() lista siete ViewDefinition en JSON estándar de SQL on FHIR (están en inst/vistas). Sirven tal cual o como punto de partida, y vista_plantilla() las carga cambiando sus constantes (sistemas de codificación, listas de códigos...).

Vista Recurso Una fila por
pacientes Patient paciente: sexo, nacimiento, fallecimiento, cantón y provincia de residencia
signos_vitales Observation medición, con la presión sistólica y diastólica separadas
diagnosticos Condition diagnóstico, con CIE-10 y SNOMED CT
notificacion_obligatoria Condition caso de dengue, malaria o tuberculosis (lista CIE-10 configurable)
inmunizaciones Immunization dosis aplicada
encuentros Encounter atención
medicamentos MedicationRequest prescripción

Todas usan getResourceKey() y getReferenceKey() para unirse entre sí, y dan los mismos resultados con los dos motores.

dengue <- system.file("extdata", "dengue_2025.ndjson.gz", package = "fhirviews")
vista_ejecutar_duckdb(dengue, vista_plantilla("notificacion_obligatoria"))

🏷️ Terminologías

Funciones nuevas para hablar con un servidor de terminología FHIR, como tx.fhir.org o Snowstorm. Usan la misma conexión de fhir_conectar(), también con token o SMART on FHIR.

Función Operación Devuelve
tx_expandir() ValueSet/$expand, paginada con offset/count data.frame con system, code, display, version
tx_buscar_codigo() CodeSystem/$lookup nombre, designaciones y propiedades del código
tx_validar_codigo() ValueSet/$validate-code para un vector de códigos si cada código es válido, y el mensaje del servidor
vista_con_valueset() — el ViewDefinition con los códigos de la expansión dentro

vista_con_valueset() es la pieza clave. Un indicador definido con un ValueSet (por ejemplo, todos los códigos SNOMED CT de diabetes) se expande una vez, y los códigos quedan como constantes de la vista, como filtro where o como columna lógica. La vista sigue siendo JSON estándar y se ejecuta sin red con cualquiera de los dos motores, aun con miles de códigos.

tx <- fhir_conectar("https://tx.fhir.org/r4")
tx_validar_codigo(tx, "http://hl7.org/fhir/ValueSet/administrative-gender",
                  "http://hl7.org/fhir/administrative-gender",
                  c("female", "mujer"))$valido
#> [1]  TRUE FALSE

diabetes <- tx_expandir(tx, url_valueset_diabetes)
v <- vista_con_valueset(vista_plantilla("diagnosticos"), diabetes, columna = "diabetes")

🦆 Motor DuckDB: mucho más FHIRPath, misma semántica

  • Más funciones: union(), combine(), intersect(), exclude(), isDistinct(), subsetOf(), supersetOf(), allTrue() y sus variantes, indexOf(), replaceMatches(), split(), toChars(), toBoolean(), los convertsTo...(), matemática (abs(), round(), sqrt(), power()...), is()/as(), trace() y lowBoundary()/highBoundary() de fechas y horas.
  • Misma semántica que fhirpath.js en los casos límite que antes daban otro resultado sin aviso:
    • fechas con distinta precisión y zona horaria;
    • igualdad entre tipos distintos (birthDate = '1970-06-15' es falso);
    • decimales (0.1 + 0.2 = 0.3);
    • substring() fuera de rango, trim() con espacios de Unicode y el orden de |.
  • Tipos del modelo FHIR R4: sigue el tipo de cada expresión, lo que permite elegir solo las variantes correctas de los elementos de elección.
  • Nunca un resultado aproximado: lo que no se puede traducir con certeza da error al traducir (fhirviews_error_sql). Lo que depende de los datos da error al ejecutar (fhirviews_error_vista).
  • Resultado diferido: con vista_ejecutar_duckdb(..., diferido = TRUE) se obtiene una tabla de dplyr/dbplyr para filtrar y resumir dentro de DuckDB y traer a R solo el resultado.
  • Conformidad: 133/133 shareable y 7 de 11 experimentales (antes 3), las mismas que el motor V8.
  • Prueba diferencial: unas 110 expresiones con casos límite y las vistas de todas las viñetas dan lo mismo en ambos motores; otras 19 comprueban que lo dudoso da error.

El inventario completo, función por función, está en data-raw/motor-duckdb.md.

🐛 Corrección importante

Las operaciones se envían con el $ sin codificar. Antes la URL se reconstruía con httr2::req_url_query() y, con algunas versiones de libcurl (por ejemplo, la de los binarios de macOS), ValueSet/$expand viajaba como ValueSet/%24expand. Servidores como tx.fhir.org responden a eso con 404 Unknown operation. Afectaba a toda operación con parámetros: $expand, $lookup, $everything, $export... Si usas 0.2.0 contra servidores reales, conviene actualizar.

⚡ Rendimiento

Medido de punta a punta, desde el NDJSON hasta el data.frame: pacientes sintéticos, Linux con 8 núcleos, R 4.5.2 y duckdb 1.5.6.

Pacientes Filas Motor V8 Motor DuckDB
10 000 17 824 2,3 s 0,22 s
50 000 89 128 11,0 s 0,92 s
100 000 178 062 23,2 s 1,18 s
250 000 444 912 58,9 s 1,56 s
1 000 000 1 780 958 — 5,85 s

Respetar la precisión de las fechas y los tipos cuesta unas décimas de segundo frente a 0.2.0 (5,5 s con un millón): es el precio de dar exactamente lo mismo que fhirpath.js.

✅ Calidad y verificación

🧩 Dependencias opcionales nuevas

Todas en Suggests:

Paquetes Para
dplyr y dbplyr vista_ejecutar_duckdb(..., diferido = TRUE)

Cambios

Todos los cambios desde 0.2.0: v0.2.0...v0.3.0 · Detalle en NEWS.md


El archivo fhirviews_0.3.0.tar.gz es el paquete fuente, construido con R CMD build desde esta versión. Se instala con install.packages("fhirviews_0.3.0.tar.gz", repos = NULL, type = "source").

fhirviews 0.2.0: servidores reales y grandes volúmenes

Choose a tag to compare

@f-slg f-slg released this 19 Oct 11:28

fhirviews 0.2.0 prepara el paquete para servidores y volúmenes reales: se conecta a servidores protegidos con SMART on FHIR, descarga poblaciones completas con $export (FHIR Bulk Data) y suma un segundo motor de vistas que traduce el ViewDefinition a SQL de DuckDB, entre 16 y 37 veces más rápido: un millón de pacientes en 5,5 segundos en un portátil.

No hay cambios incompatibles: el código escrito para 0.1.0 funciona igual.

# install.packages("pak")
pak::pak("f-slg/fhirviews@v0.2.0")

📖 Documentación: https://f-slg.github.io/fhirviews/ · Nueva viñeta: Servidores reales y grandes volúmenes


Lo nuevo

🔐 Autenticación SMART on FHIR / OAuth2

fhir_conectar() tiene un argumento nuevo, autenticacion, para servidores protegidos. El token se pide en la primera petición, se renueva solo, se reintenta una vez si el servidor lo rechaza (401), se envía únicamente al propio servidor y nunca se imprime.

Flujo Función Cuándo usarlo
Client credentials con secreto fhir_auth_cliente() Integraciones sencillas entre sistemas
SMART Backend Services (JWT RS384 o ES384) fhir_auth_backend() Procesos automáticos: ETL, tableros, exportaciones
Authorization code + PKCE fhir_auth_interactiva() Una persona inicia sesión en el navegador
con <- fhir_conectar(
  "https://fhir.hospital.ejemplo/r4",
  autenticacion = fhir_auth_backend("tablero-vigilancia", "~/.claves/privada.pem",
                                    alcances = "system/*.rs", kid = "clave-2026")
)

fhir_smart_configuracion() descubre las URL del servidor de autorización en .well-known/smart-configuration o en el CapabilityStatement (SMART v1).

📦 Exportación masiva con $export (FHIR Bulk Data)

fhir_exportar() inicia la exportación (sistema, pacientes o un Group, con _type, _since y _typeFilter), espera respetando Retry-After y mostrando el avance, y descarga los NDJSON del manifiesto, listos para las vistas. Con esperar = FALSE, fhir_exportar_estado() y fhir_exportar_cancelar() permiten gestionar exportaciones largas.

con <- fhir_conectar("https://bulk-data.smarthealthit.org/fhir")
exp <- fhir_exportar(con, "paciente", tipos = c("Patient", "Condition", "Observation"))
exp
#> <fhir_exportacion> completada (nivel paciente)
#>   Condition                1 archivo, 639 recursos
#>   Observation              2 archivos, 16315 recursos
#>   Patient                  1 archivo, 100 recursos

(Salida real contra el servidor de referencia de SMART Health IT; tardó unos 5 segundos.)

🦆 Motor DuckDB (experimental)

vista_ejecutar_duckdb() traduce el mismo ViewDefinition a una sola consulta SQL y la ejecuta con DuckDB directamente sobre NDJSON (también comprimido o con comodines) o Parquet, sin cargar los recursos en R. vista_sql() muestra la consulta generada.

casos <- vista_ejecutar_duckdb("exportacion/*.ndjson", vista)
  • Pasa las 133/133 pruebas shareable del banco oficial de SQL on FHIR (vista_conformidad(motor = "duckdb")).
  • Una prueba diferencial exige que dé exactamente las mismas tablas (valores y tipos) que el motor V8 con las vistas de todas las viñetas.
  • Lo que no puede traducir con certeza da un error claro (fhirviews_error_sql) en lugar de un resultado aproximado.

⚡ Rendimiento

De punta a punta, desde el archivo NDJSON hasta el data.frame (pacientes sintéticos; vista con where, cuatro columnas y forEach; Linux, 8 núcleos, R 4.5.2, duckdb 1.5.6):

Pacientes Filas Motor V8 Motor DuckDB Veces más rápido
10 000 17 824 2,6 s 0,16 s 16
50 000 89 128 11,8 s 0,72 s 16
100 000 178 062 24,2 s 0,94 s 26
250 000 444 912 62,4 s 1,67 s 37
1 000 000 1 780 958 — 5,5 s —

Recomendación: desarrollar y validar la vista con vista_ejecutar() (motor completo) y ejecutarla a escala con vista_ejecutar_duckdb().

✅ Calidad y verificación

  • 773 expectativas en 12 archivos de prueba, sin fallas; CI en Windows, macOS y Ubuntu.
  • R CMD check --as-cran: 0 errores, 0 advertencias.
  • Autenticación probada contra un servidor SMART local que verifica la firma de cada JWT con una implementación independiente (Python con PyJWT): un solo token para varias peticiones, renovación al vencer, reintento tras un 401 por token revocado, y rechazo de secretos y claves incorrectos.
  • Exportación probada contra el servidor de referencia https://bulk-data.smarthealthit.org.
  • Motor DuckDB: 133/133 shareable, prueba diferencial frente al motor V8 y 200 000 pacientes con tablas idénticas en ambos motores.

📚 Documentación

🧩 Dependencias opcionales nuevas

Todas en Suggests; solo se necesitan para la función correspondiente:

Paquetes Para
duckdb (≥ 1.3.0) y DBI Motor DuckDB y vista_duckdb()
jose y openssl fhir_auth_backend() (firma del JWT)
httpuv fhir_auth_interactiva() (respuesta del navegador)

Cambios

Todos los cambios desde 0.1.0: v0.1.0...v0.2.0 · Detalle en NEWS.md


El archivo fhirviews_0.2.0.tar.gz es el paquete fuente, construido con R CMD build desde esta versión. Se instala con install.packages("fhirviews_0.2.0.tar.gz", repos = NULL, type = "source").

fhirviews 0.1.0

Choose a tag to compare

@f-slg f-slg released this 19 Oct 02:29

Primera versión de fhirviews: HL7 FHIR R4 desde R y RStudio, con un motor de SQL on FHIR (ViewDefinition) que funciona dentro de R, sin Java ni Spark, y devuelve data.frame. Pasa las 133/133 pruebas shareable del banco oficial de conformidad.

# install.packages("pak")
pak::pak("f-slg/fhirviews@v0.1.0")

Documentación: https://f-slg.github.io/fhirviews/

  • Primera versión.

Correcciones de la auditoría técnica

  • fhir_buscar() y fhir_everything() dejan de paginar (con un aviso) si el servidor repite un enlace next, en lugar de entrar en un bucle infinito.
  • fhir_crear() envía los Bundle transaction y batch a la base del servidor (POST [base]), que los procesa, en vez de guardarlos como un recurso Bundle.
  • vista_validar() rechaza column, select y unionAll vacíos (antes unionAll: [] daba un error poco claro) y las constantes con nombres reservados (resource, rowIndex...), que quedaban ocultas.
  • Las vistas sobre recursos de varios tipos leídos de JSON o NDJSON ya no vuelven a convertirlos a JSON: unas 9 veces más rápidas.
  • type ya no trunca ni convierte en NA en silencio: si un valor no es compatible, se avisa y se conserva. Las columnas sin type y sin valores toman el tipo del modelo de FHIR R4, de modo que la misma vista da siempre los mismos tipos.
  • fhir_conectar() avisa si un token viajaría sin cifrar por http.
  • fhir_recursos() informa claramente si un archivo no existe y acepta un vector con varias rutas de archivo.

Cliente FHIR

  • fhir_conectar(), fhir_buscar() (paginación automática por los enlaces next), fhir_leer(), fhir_crear(), fhir_actualizar(), fhir_borrar(), fhir_everything(), fhir_validar(), fhir_capacidades(), fhir_peticion(), fhir_ejecutar_texto() y fhir_disponible(), con httr2.
  • application/fhir+json, token bearer (solo se envía al servidor de la conexión), reintentos ante 429/503 y errores legibles a partir del OperationOutcome, con una clase por código HTTP.
  • fhir_peticion() devuelve un objeto fhir_respuesta que se imprime con el estado y el JSON con sangría, como Postman.

Vistas SQL on FHIR

  • vista_ejecutar() aplica un ViewDefinition a recursos (listas, Bundles, JSON, NDJSON o archivos .ndjson.gz) y devuelve un data.frame. Admite select anidados, column (type, collection), forEach, forEachOrNull, repeat, unionAll, where, constant y %rowIndex. Evalúa por lotes, con una llamada a FHIRPath por expresión y nivel.
  • vista_validar(), vista_leer(), vista_crear(), vista_json(), vista_fhirpath(), fhir_recursos(), fhir_tabla() y fhir_vista() (descargar del servidor y aplicar la vista en un paso).
  • FHIRPath con fhirpath.js 5.2.1 (HL7/fhirpath.js) en V8, con getResourceKey() y getReferenceKey(). La capa de FHIRPath está separada (fhirviews_motor()) para poder sustituirla por un motor en R.
  • vista_conformidad() ejecuta el banco oficial de pruebas de SQL on FHIR (incluido; o el más reciente con descargar = TRUE) y genera el informe en el formato oficial. Pasan las 133 pruebas shareable y 7 de las 11 experimentales.
  • vista_duckdb() guarda vistas como tablas de DuckDB (o de otra conexión DBI) para consultarlas con SQL.

RStudio

  • El servidor se registra en el panel Connections (tipos de recurso, columnas, vista previa) y aparece en el diálogo New Connection.
  • Addins Ejecutar petición FHIR y Ejecutar ViewDefinition.

Datos y documentación

  • Caso clínico ficticio del taller aprende-fhir (inst/extdata/caso_clinico.ndjson) y casos ficticios de dengue 2025 por cantón (inst/extdata/dengue_2025.ndjson.gz).
  • Viñetas Primeros pasos con fhirviews, ViewDefinition en detalle y Tasas por cantón con FHIR y pobecu.

El archivo fhirviews_0.1.0.tar.gz es el paquete fuente, construido con R CMD build desde esta versión. Se instala con install.packages("fhirviews_0.1.0.tar.gz", repos = NULL, type = "source").