Gestisci le risorse FHIR tramite la libreria di motori FHIR

1. Prima di iniziare

Cosa creerai

In questo codelab, creerai un'app per Android utilizzando la libreria FHIR Engine. La tua app utilizzerà la libreria FHIR Engine per scaricare le risorse FHIR da un server FHIR e caricare eventuali modifiche locali sul server.

Obiettivi didattici

  • Come creare un server HAPI FHIR locale utilizzando Docker
  • Come integrare la libreria FHIR Engine nella tua applicazione Android
  • Come utilizzare l'API Sync per configurare un job una tantum o periodico per scaricare e caricare risorse FHIR
  • Come utilizzare l'API Search
  • Come utilizzare le API di accesso ai dati per creare, leggere, aggiornare ed eliminare localmente le risorse FHIR

Che cosa ti serve

Se non hai mai creato app per Android, puoi iniziare creando la tua prima app.

2. Configura un server HAPI FHIR locale con dati di test

HAPI FHIR è un popolare server FHIR open source. Nel nostro codelab utilizziamo un server HAPI FHIR locale a cui l'app per Android si connette.

Configura il server HAPI FHIR locale

  1. Esegui il seguente comando in un terminale per ottenere l'immagine più recente di HAPI FHIR
    docker pull hapiproject/hapi:latest
    
  2. Crea un contenitore HAPI FHIR utilizzando Docker Desktop per eseguire l'immagine scaricata in precedenza hapiproject/hapi o eseguendo il seguente comando
    docker run -p 8080:8080 hapiproject/hapi:latest
    
    Scopri di più.
  3. Controlla il server aprendo l'URL http://localhost:8080/ in un browser. Dovresti visualizzare l'interfaccia web HAPI FHIR.Interfaccia web HAPI FHIR

Compila il server HAPI FHIR locale con dati di test

Per testare la nostra applicazione, avremo bisogno di alcuni dati di test sul server. Utilizzeremo i dati sintetici generati da Synthea.

  1. Innanzitutto, dobbiamo scaricare i dati di esempio da synthea-samples. Scarica ed estrai synthea_sample_data_fhir_r4_sep2019.zip. I dati di esempio non compressi contengono numerosi file .json, ciascuno dei quali è un pacchetto di transazioni per un singolo paziente.
  2. Caricheremo i dati di test di tre pazienti sul server HAPI FHIR locale. Esegui il seguente comando nella directory contenente i file JSON
    curl -X POST -H "Content-Type: application/json" -d @./Aaron697_Brekke496_2fa15bc7-8866-461a-9000-f739e425860a.json http://localhost:8080/fhir/
    curl -X POST -H "Content-Type: application/json" -d @./Aaron697_Stiedemann542_41166989-975d-4d17-b9de-17f94cb3eec1.json http://localhost:8080/fhir/
    curl -X POST -H "Content-Type: application/json" -d @./Abby752_Kuvalis369_2b083021-e93f-4991-bf49-fd4f20060ef8.json http://localhost:8080/fhir/
    
  3. Per caricare i dati di test per tutti i pazienti sul server, esegui
    for f in *.json; do curl -X POST -H "Content-Type: application/json" -d @$f http://localhost:8080/fhir/ ; done
    
    Tuttavia, il completamento può richiedere molto tempo e non è necessario per il codelab.
  4. Verifica che i dati di test siano disponibili sul server aprendo l'URL http://localhost:8080/fhir/Patient/ in un browser. Dovresti vedere il testo HTTP 200 OK e la sezione Response Body della pagina contenente i dati del paziente in un bundle FHIR come risultato di ricerca con un conteggio total.Testare i dati sul server

3. Configurare l'app per Android

Scarica il codice

Per scaricare il codice di questo codelab, clona il repository Android FHIR SDK: git clone https://github.com/google/android-fhir.git

Il progetto iniziale per questo codelab si trova in codelabs/engine.

Importa l'app in Android Studio

Iniziamo importando l'app iniziale in Android Studio.

Apri Android Studio, seleziona Importa progetto (Gradle, Eclipse ADT e così via) e scegli la cartella codelabs/engine/ dal codice sorgente che hai scaricato in precedenza.

Schermata di avvio di Android Studio

Sincronizzare il progetto con i file Gradle

Per comodità, le dipendenze della libreria FHIR Engine sono già state aggiunte al progetto. In questo modo puoi integrare la libreria FHIR Engine nella tua app. Osserva le seguenti righe fino alla fine del file app/build.gradle.kts del progetto:

dependencies {
    // ...

    implementation("com.google.android.fhir:engine:1.1.0")
}

Per assicurarti che tutte le dipendenze siano disponibili per la tua app, a questo punto devi sincronizzare il progetto con i file Gradle.

Seleziona Sincronizza progetto con i file Gradle (Pulsante di sincronizzazione Gradle) dalla barra degli strumenti di Android Studio. Puoi anche eseguire di nuovo l'app per verificare che le dipendenze funzionino correttamente.

Esegui l'app di avvio

Ora che hai importato il progetto in Android Studio, puoi eseguire l'app per la prima volta.

Avvia l'emulatore Android Studio e fai clic su Esegui (Pulsante Esegui) nella barra degli strumenti di Android Studio.

App Hello World

4. Crea un'istanza di FHIR Engine

Per incorporare il motore FHIR nella tua app per Android, devi utilizzare la libreria del motore FHIR e avviare un'istanza del motore FHIR. I passaggi descritti di seguito ti guideranno nella procedura.

  1. Vai alla classe Application, che in questo esempio è FhirApplication.kt, in app/src/main/java/com/google/android/fhir/codelabs/engine.
  2. All'interno del metodo onCreate(), aggiungi il seguente codice per inizializzare FHIR Engine:
      FhirEngineProvider.init(
          FhirEngineConfiguration(
            enableEncryptionIfSupported = true,
            RECREATE_AT_OPEN,
            ServerConfiguration(
              baseUrl = "http://10.0.2.2:8080/fhir/",
              httpLogger =
                HttpLogger(
                  HttpLogger.Configuration(
                    if (BuildConfig.DEBUG) HttpLogger.Level.BODY else HttpLogger.Level.BASIC,
                  ),
                ) {
                  Log.d("App-HttpLog", it)
                },
            ),
          ),
      )
    
    Note:
    • enableEncryptionIfSupported: attiva la crittografia dei dati se il dispositivo la supporta.
    • RECREATE_AT_OPEN: determina la strategia di errore del database. In questo caso, il database viene ricreato se si verifica un errore all'apertura.
    • baseUrl in ServerConfiguration: si tratta dell'URL di base del server FHIR. L'indirizzo IP fornito 10.0.2.2 è riservato appositamente per localhost, accessibile dall'emulatore Android. Scopri di più.
  3. Nella classe FhirApplication, aggiungi la seguente riga per creare un'istanza del motore FHIR in modo lazy:
      private val fhirEngine: FhirEngine by
          lazy { FhirEngineProvider.getInstance(this) }
    
    In questo modo, l'istanza FhirEngine viene creata solo quando viene acceduta per la prima volta, non immediatamente all'avvio dell'app.
  4. Aggiungi il seguente metodo di utilità nella classe FhirApplication per un accesso più facile all'interno dell'applicazione:
    companion object {
        fun fhirEngine(context: Context) =
            (context.applicationContext as FhirApplication).fhirEngine
    }
    
    Questo metodo statico ti consente di recuperare l'istanza di FHIR Engine da qualsiasi punto dell'app utilizzando il contesto.

5. Sincronizzare i dati con il server FHIR

  1. Crea un nuovo corso DownloadWorkManagerImpl.kt. In questo corso, definirai in che modo l'applicazione recupera la risorsa successiva dall'elenco da scaricare.
      class DownloadWorkManagerImpl : DownloadWorkManager {
        private val urls = LinkedList(listOf("Patient"))
    
        override suspend fun getNextRequest(): DownloadRequest? {
          val url = urls.poll() ?: return null
          return DownloadRequest.of(url)
        }
    
        override suspend fun getSummaryRequestUrls() = mapOf<ResourceType, String>()
    
        override suspend fun processResponse(response: Resource): Collection<Resource> {
          var bundleCollection: Collection<Resource> = mutableListOf()
          if (response is Bundle && response.type == Bundle.BundleType.SEARCHSET) {
            bundleCollection = response.entry.map { it.resource }
          }
          return bundleCollection
        }
      }
    
    Questo corso ha una coda di tipi di risorse che vuole scaricare. Elabora le risposte ed estrae le risorse dal bundle restituito, che vengono salvate nel database locale.
  2. Crea una nuova classe AppFhirSyncWorker.kt Questa classe definisce la modalità di sincronizzazione dell'app con il server FHIR remoto utilizzando un worker in background.
    class AppFhirSyncWorker(appContext: Context, workerParams: WorkerParameters) :
      FhirSyncWorker(appContext, workerParams) {
    
      override fun getDownloadWorkManager() = DownloadWorkManagerImpl()
    
      override fun getConflictResolver() = AcceptLocalConflictResolver
    
      override fun getFhirEngine() = FhirApplication.fhirEngine(applicationContext)
    
      override fun getUploadStrategy() =
        UploadStrategy.forBundleRequest(
          methodForCreate = HttpCreateMethod.PUT,
          methodForUpdate = HttpUpdateMethod.PATCH,
          squash = true,
          bundleSize = 500,
        )
    }
    
    Qui abbiamo definito quale gestore dei download, quale risolutore dei conflitti e quale istanza del motore FHIR utilizzare per la sincronizzazione.
  3. Nel tuo ViewModel, PatientListViewModel.kt, imposterai un meccanismo di sincronizzazione una tantum. Individua e aggiungi questo codice alla funzione triggerOneTimeSync():
    viewModelScope.launch {
          Sync.oneTimeSync<AppFhirSyncWorker>(getApplication())
            .shareIn(this, SharingStarted.Eagerly, 10)
            .collect { _pollState.emit(it) }
        }
    
    Questa coroutine avvia una sincronizzazione una tantum con il server FHIR utilizzando AppFhirSyncWorker che abbiamo definito in precedenza. L'interfaccia utente verrà quindi aggiornata in base allo stato del processo di sincronizzazione.
  4. Nel file PatientListFragment.kt, aggiorna il corpo della funzione handleSyncJobStatus:
    when (syncJobStatus) {
        is SyncJobStatus.Finished -> {
            Toast.makeText(requireContext(), "Sync Finished", Toast.LENGTH_SHORT).show()
            viewModel.searchPatientsByName("")
        }
        else -> {}
    }
    
    Qui, al termine della procedura di sincronizzazione, viene visualizzato un messaggio popup che avvisa l'utente e l'app mostra tutti i pazienti richiamando una ricerca con un nome vuoto.

Ora che è tutto configurato, esegui l'app. Fai clic sul pulsante Sync nel menu. Se tutto funziona correttamente, dovresti vedere i pazienti del tuo server FHIR locale scaricati e visualizzati nell'applicazione.

Elenco dei pazienti

6. Modificare e caricare i dati dei pazienti

In questa sezione, ti guideremo nella procedura di modifica dei dati dei pazienti in base a criteri specifici e nel caricamento dei dati aggiornati sul tuo server FHIR. Nello specifico, scambieremo le città degli indirizzi per i pazienti residenti in Wakefield e Taunton.

Passaggio 1: configura la logica di modifica in PatientListViewModel

Il codice in questa sezione viene aggiunto alla funzione triggerUpdate in PatientListViewModel

  1. Accedi al motore FHIR:inizia recuperando un riferimento al motore FHIR in PatientListViewModel.kt.
    viewModelScope.launch {
       val fhirEngine = FhirApplication.fhirEngine(getApplication())
    
    Questo codice avvia una coroutine nell'ambito di ViewModel e inizializza il motore FHIR.
  2. Cerca i pazienti di Wakefield:utilizza il motore FHIR per cercare i pazienti con città di residenza Wakefield.
    val patientsFromWakefield =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Wakefield"
             }
           )
         }
    
    In questo caso, utilizziamo il metodo search del motore FHIR per filtrare i pazienti in base alla città del loro indirizzo. Il risultato sarà un elenco di pazienti di Wakefield.
  3. Cerca i pazienti di Taunton:in modo simile, cerca i pazienti con città di residenza Taunton.
    val patientsFromTaunton =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Taunton"
             }
           )
         }
    
    Ora abbiamo due elenchi di pazienti, uno di Wakefield e l'altro di Taunton.
  4. Modifica indirizzi dei pazienti:esamina ogni paziente nell'elenco patientsFromWakefield, modifica la città in Taunton e aggiornali nel motore FHIR.
    patientsFromWakefield.forEach {
         it.resource.address.first().city = "Taunton"
         fhirEngine.update(it.resource)
    }
    
    Analogamente, aggiorna ogni paziente nell'elenco patientsFromTaunton in modo che la città venga modificata in Wakefield.
    patientsFromTaunton.forEach {
         it.resource.address.first().city = "Wakefield"
         fhirEngine.update(it.resource)
    }
    
  5. Avvia sincronizzazione:dopo aver modificato i dati localmente, attiva una sincronizzazione una tantum per assicurarti che i dati vengano aggiornati sul server FHIR.
    triggerOneTimeSync()
    }
    
    La parentesi graffa di chiusura } indica la fine della coroutine lanciata all'inizio.

Passaggio 2: testa la funzionalità

  1. Test dell'interfaccia utente:esegui l'app. Fai clic sul pulsante Update nel menu. Dovresti vedere le città degli indirizzi dei pazienti Aaron697 e Abby752 scambiate.
  2. Verifica del server:apri un browser e vai all'indirizzo http://localhost:8080/fhir/Patient/. Verifica che la città dell'indirizzo dei pazienti Aaron697 e Abby752 sia aggiornata sul server FHIR locale.

Se hai seguito questi passaggi, hai implementato correttamente un meccanismo per modificare i dati dei pazienti e sincronizzare le modifiche con il server FHIR.

7. Cercare i pazienti per nome

La ricerca dei pazienti per nome può offrire un modo intuitivo per recuperare le informazioni. Di seguito ti guideremo nella procedura di implementazione di questa funzionalità nella tua applicazione.

Passaggio 1: aggiorna la firma della funzione

Vai al file PatientListViewModel.kt e trova la funzione denominata searchPatientsByName. Aggiungeremo del codice a questa funzione.

Per filtrare i risultati in base alla query sul nome fornita ed emettere i risultati da aggiornare nell'interfaccia utente, incorpora il seguente blocco di codice condizionale:

    viewModelScope.launch {
      val fhirEngine = FhirApplication.fhirEngine(getApplication())
      if (nameQuery.isNotEmpty()) {
        val searchResult = fhirEngine.search<Patient> {
          filter(
            Patient.NAME,
            {
              modifier = StringFilterModifier.CONTAINS
              value = nameQuery
            },
          )
        }
        liveSearchedPatients.value  =  searchResult.map { it.resource }
      }
    }

In questo caso, se nameQuery non è vuoto, la funzione di ricerca filtrerà i risultati in modo da includere solo i pazienti i cui nomi contengono la query specificata.

Passaggio 2: testa la nuova funzionalità di ricerca

  1. Riavvia l'app:dopo aver apportato queste modifiche, ricostruisci ed esegui l'app.
  2. Cerca pazienti: nella schermata dell'elenco dei pazienti, utilizza la funzionalità di ricerca. A questo punto dovresti essere in grado di inserire un nome (o parte di un nome) per filtrare di conseguenza l'elenco dei pazienti.

Una volta completati questi passaggi, avrai migliorato la tua applicazione offrendo agli utenti la possibilità di cercare in modo efficiente i pazienti in base al nome. Ciò può migliorare notevolmente l'esperienza utente e l'efficienza nel recupero dei dati.

8. Complimenti!

Hai utilizzato la libreria FHIR Engine per gestire le risorse FHIR nella tua app:

  • Utilizzare l'API Sync per sincronizzare le risorse FHIR con un server FHIR
  • Utilizza l'API Data Access per creare, leggere, aggiornare ed eliminare le risorse FHIR locali
  • Utilizzare l'API Search per cercare risorse FHIR locali

Argomenti trattati

  • Come configurare un server HAPI FHIR locale
  • Come caricare i dati di test sul server HAPI FHIR locale
  • Come creare un'app per Android utilizzando la libreria FHIR Engine
  • Come utilizzare l'API Sync, l'API Data Access e l'API Search nella libreria FHIR Engine

Passaggi successivi

  • Esplora la documentazione della libreria FHIR Engine
  • Esplora le funzionalità avanzate dell'API Search
  • Applicare la libreria FHIR Engine nella tua app per Android

Scopri di più