FHIR-Ressourcen mit der FHIR-Engine-Bibliothek verwalten

1. Hinweis

Aufgaben

In diesem Codelab erstellen Sie eine Android-App mit der FHIR Engine Library. Ihre App verwendet die FHIR Engine Library, um FHIR-Ressourcen von einem FHIR-Server herunterzuladen und alle lokalen Änderungen auf den Server hochzuladen.

Lerninhalte

  • Lokalen HAPI FHIR-Server mit Docker erstellen
  • FHIR Engine Library in Ihre Android-App einbinden
  • Sync API verwenden, um einen einmaligen oder regelmäßigen Job zum Herunterladen und Hochladen von FHIR-Ressourcen einzurichten
  • Such-API verwenden
  • Data Access APIs verwenden, um FHIR-Ressourcen lokal zu erstellen, zu lesen, zu aktualisieren und zu löschen

Voraussetzungen

Wenn Sie noch keine Android-Apps erstellt haben, können Sie mit der Erstellung Ihrer ersten App beginnen.

2. Lokalen HAPI FHIR-Server mit Testdaten einrichten

HAPI FHIR ist ein beliebter Open-Source-FHIR-Server. In unserem Codelab verwenden wir einen lokalen HAPI FHIR-Server, mit dem sich die Android-App verbinden kann.

Lokalen HAPI FHIR-Server einrichten

  1. Führen Sie den folgenden Befehl in einem Terminal aus, um das aktuelle Image von HAPI FHIR abzurufen:
    docker pull hapiproject/hapi:latest
    
  2. Erstellen Sie einen HAPI FHIR-Container, indem Sie entweder Docker Desktop verwenden, um das zuvor heruntergeladene Image hapiproject/hapi auszuführen, oder den folgenden Befehl ausführen:
    docker run -p 8080:8080 hapiproject/hapi:latest
    
    Weitere Informationen.
  3. Prüfen Sie den Server, indem Sie die URL http://localhost:8080/ in einem Browser öffnen. Sie sollten die HAPI FHIR-Weboberfläche sehen.HAPI FHIR-Weboberfläche

Lokalen HAPI FHIR-Server mit Testdaten füllen

Zum Testen unserer Anwendung benötigen wir einige Testdaten auf dem Server. Wir verwenden synthetische Daten, die von Synthea generiert wurden.

  1. Zuerst müssen wir Beispieldaten von synthea-samples herunterladen. Laden Sie synthea_sample_data_fhir_r4_sep2019.zip herunter und extrahieren Sie die Datei. Die entpackten Beispieldaten enthalten zahlreiche .json-Dateien, die jeweils ein Transaktions-Bundle für einen einzelnen Patienten darstellen.
  2. Wir laden Testdaten für drei Patienten auf den lokalen HAPI FHIR-Server hoch. Führen Sie den folgenden Befehl im Verzeichnis mit den JSON-Dateien aus:
    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. Wenn Sie Testdaten für alle Patienten auf den Server hochladen möchten, führen Sie folgenden Befehl aus:
    for f in *.json; do curl -X POST -H "Content-Type: application/json" -d @$f http://localhost:8080/fhir/ ; done
    
    Dieser Vorgang kann jedoch lange dauern und ist für das Codelab nicht erforderlich.
  4. Prüfen Sie, ob die Testdaten auf dem Server verfügbar sind, indem Sie die URL http://localhost:8080/fhir/Patient/ in einem Browser öffnen. Sie sollten den Text HTTP 200 OK und den Abschnitt Response Body der Seite mit Patientendaten in einem FHIR-Bundle als Suchergebnis mit einer total-Anzahl sehen.Testdaten auf dem Server

3. Android-App einrichten

Code herunterladen

Wenn Sie den Code für dieses Codelab herunterladen möchten, klonen Sie das Android FHIR SDK-Repository: git clone https://github.com/ohs-foundation/android-fhir.git

Das Starterprojekt für dieses Codelab befindet sich in codelabs/engine.

App in Android Studio importieren

Zuerst importieren wir die Starter-App in Android Studio.

Öffnen Sie Android Studio, wählen Sie Projekt importieren (Gradle, Eclipse ADT usw.) aus und wählen Sie den Ordner codelabs/engine/ aus dem Quellcode aus, den Sie zuvor heruntergeladen haben.

Android Studio-Startbildschirm

Projekt mit Gradle-Dateien synchronisieren

Die Abhängigkeiten der FHIR Engine Library wurden dem Projekt bereits hinzugefügt. So können Sie die FHIR Engine Library in Ihre App einbinden. Beachten Sie die folgenden Zeilen am Ende der Datei app/build.gradle.kts Ihres Projekts:

dependencies {
    // ...

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

Damit alle Abhängigkeiten für Ihre App verfügbar sind, sollten Sie Ihr Projekt an dieser Stelle mit Gradle-Dateien synchronisieren.

Wählen Sie in der Android Studio-Symbolleiste Projekt mit Gradle-Dateien synchronisieren (Schaltfläche „Gradle-Synchronisierung“) aus. Sie können die App auch noch einmal ausführen, um zu prüfen, ob die Abhängigkeiten korrekt funktionieren.

Starter-App ausführen

Nachdem Sie das Projekt in Android Studio importiert haben, können Sie die App zum ersten Mal ausführen.

Starten Sie den Android Studio-Emulator und klicken Sie in der Android Studio-Symbolleiste auf „Ausführen“ (Button „Ausführen“).

Hello World-App

4. FHIR Engine-Instanz erstellen

Wenn Sie die FHIR Engine in Ihre Android-App einbinden möchten, müssen Sie die FHIR Engine Library verwenden und eine Instanz der FHIR Engine initiieren. Die folgenden Schritte führen Sie durch den Prozess.

  1. Rufen Sie Ihre Anwendungsklasse auf. In diesem Beispiel ist das FhirApplication.kt in app/src/main/java/com/google/android/fhir/codelabs/engine.
  2. Fügen Sie in der Methode onCreate() den folgenden Code hinzu, um die FHIR Engine zu initialisieren:
      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)
                },
            ),
          ),
      )
    
    Hinweise:
    • enableEncryptionIfSupported: Aktiviert die Datenverschlüsselung, wenn das Gerät sie unterstützt.
    • RECREATE_AT_OPEN: Bestimmt die Strategie für Datenbankfehler. In diesem Fall wird die Datenbank neu erstellt, wenn beim Öffnen ein Fehler auftritt.
    • baseUrl in ServerConfiguration: Dies ist die Basis-URL des FHIR-Servers. Die angegebene IP-Adresse 10.0.2.2 ist speziell für localhost reserviert und kann über den Android-Emulator aufgerufen werden. Weitere Informationen .
  3. Fügen Sie in der Klasse FhirApplication die folgende Zeile hinzu, um die FHIR Engine verzögert zu instanziieren:
      private val fhirEngine: FhirEngine by
          lazy { FhirEngineProvider.getInstance(this) }
    
    So wird die FhirEngine-Instanz erst erstellt, wenn zum ersten Mal darauf zugegriffen wird, nicht sofort beim Start der App.
  4. Fügen Sie der Klasse FhirApplication die folgende Hilfsmethode hinzu, um den Zugriff in der gesamten Anwendung zu erleichtern:
    companion object {
        fun fhirEngine(context: Context) =
            (context.applicationContext as FhirApplication).fhirEngine
    }
    
    Mit dieser statischen Methode können Sie die FHIR Engine-Instanz mit dem Kontext von überall in der App abrufen.

5. Daten mit FHIR-Server synchronisieren

  1. Erstellen Sie eine neue Klasse DownloadWorkManagerImpl.kt. In dieser Klasse definieren Sie, wie die Anwendung die nächste Ressource aus der Liste abruft, um sie herunterzuladen:
      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
        }
      }
    
    Diese Klasse enthält eine Warteschlange mit Ressourcentypen, die heruntergeladen werden sollen. Sie verarbeitet Antworten und extrahiert die Ressourcen aus dem zurückgegebenen Bundle, die in der lokalen Datenbank gespeichert werden.
  2. Erstellen Sie eine neue Klasse AppFhirSyncWorker.kt. Diese Klasse definiert, wie die App mit einem Hintergrundprozess mit dem Remote-FHIR-Server synchronisiert wird.
    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,
        )
    }
    
    Hier haben wir definiert, welcher Download-Manager, welcher Konfliktlöser und welche FHIR Engine-Instanz für die Synchronisierung verwendet werden sollen.
  3. In Ihrem ViewModel PatientListViewModel.kt richten Sie einen einmaligen Synchronisierungsmechanismus ein. Suchen Sie die Funktion triggerOneTimeSync() und fügen Sie diesen Code hinzu:
    viewModelScope.launch {
          Sync.oneTimeSync<AppFhirSyncWorker>(getApplication())
            .shareIn(this, SharingStarted.Eagerly, 10)
            .collect { _pollState.emit(it) }
        }
    
    Diese Coroutine initiiert eine einmalige Synchronisierung mit dem FHIR-Server über den zuvor definierten AppFhirSyncWorker. Anschließend wird die Benutzeroberfläche basierend auf dem Status des Synchronisierungsprozesses aktualisiert.
  4. Aktualisieren Sie in der Datei PatientListFragment.kt den Textkörper der Funktion handleSyncJobStatus:
    when (syncJobStatus) {
        is SyncJobStatus.Finished -> {
            Toast.makeText(requireContext(), "Sync Finished", Toast.LENGTH_SHORT).show()
            viewModel.searchPatientsByName("")
        }
        else -> {}
    }
    
    Wenn der Synchronisierungsprozess abgeschlossen ist, wird eine Toast-Nachricht angezeigt, um den Nutzer zu benachrichtigen. Anschließend werden alle Patienten in der App angezeigt, indem eine Suche mit einem leeren Namen aufgerufen wird.

Nachdem alles eingerichtet ist, führen Sie Ihre App aus. Klicken Sie im Menü auf die Schaltfläche Sync. Wenn alles korrekt funktioniert, sollten die Patienten von Ihrem lokalen FHIR-Server heruntergeladen und in der Anwendung angezeigt werden.

Patientenliste

6. Patientendaten ändern und hochladen

In diesem Abschnitt erfahren Sie, wie Sie Patientendaten anhand bestimmter Kriterien ändern und die aktualisierten Daten auf Ihren FHIR-Server hochladen. Konkret tauschen wir die Adressstädte für Patienten mit Wohnsitz in Wakefield und Taunton aus.

Schritt 1: Änderungslogik in PatientListViewModel einrichten

Der Code in diesem Abschnitt wird der Funktion triggerUpdate in PatientListViewModel hinzugefügt.

  1. Auf die FHIR Engine zugreifen:Rufen Sie zuerst einen Verweis auf die FHIR Engine in der PatientListViewModel.kt ab.
    viewModelScope.launch {
       val fhirEngine = FhirApplication.fhirEngine(getApplication())
    
    Mit diesem Code wird eine Coroutine im Bereich des ViewModels gestartet und die FHIR Engine initialisiert.
  2. Nach Patienten aus Wakefield suchen:Suchen Sie mit der FHIR Engine nach Patienten mit der Adressstadt Wakefield.
    val patientsFromWakefield =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Wakefield"
             }
           )
         }
    
    Hier verwenden wir die Methode search der FHIR Engine, um Patienten nach ihrer Adressstadt zu filtern. Das Ergebnis ist eine Liste von Patienten aus Wakefield.
  3. Nach Patienten aus Taunton suchen:Suchen Sie auf ähnliche Weise nach Patienten mit der Adressstadt Taunton.
    val patientsFromTaunton =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Taunton"
             }
           )
         }
    
    Wir haben jetzt zwei Patientenlisten: eine aus Wakefield und eine aus Taunton.
  4. Patientenadressen ändern:Gehen Sie jeden Patienten in der Liste patientsFromWakefield durch, ändern Sie die Stadt in Taunton und aktualisieren Sie sie in der FHIR Engine.
    patientsFromWakefield.forEach {
         it.resource.address.first().city = "Taunton"
         fhirEngine.update(it.resource)
    }
    
    Aktualisieren Sie auf ähnliche Weise jeden Patienten in der Liste patientsFromTaunton, sodass die Stadt in Wakefield geändert wird.
    patientsFromTaunton.forEach {
         it.resource.address.first().city = "Wakefield"
         fhirEngine.update(it.resource)
    }
    
  5. Synchronisierung initiieren:Nachdem Sie die Daten lokal geändert haben, lösen Sie eine einmalige Synchronisierung aus, damit die Daten auf dem FHIR-Server aktualisiert werden.
    triggerOneTimeSync()
    }
    
    Die schließende geschweifte Klammer } kennzeichnet das Ende der am Anfang gestarteten Coroutine.

Schritt 2: Funktionalität testen

  1. UI-Tests:Führen Sie Ihre App aus. Klicken Sie im Menü auf die Schaltfläche Update. Die Adressstädte für die Patienten Aaron697 und Abby752 sollten ausgetauscht worden sein.
  2. Serverprüfung:Öffnen Sie einen Browser und rufen Sie http://localhost:8080/fhir/Patient/ auf. Prüfen Sie, ob die Adressstadt für die Patienten Aaron697 und Abby752 auf dem lokalen FHIR-Server aktualisiert wurde.

Mit diesen Schritten haben Sie einen Mechanismus implementiert, mit dem Sie Patientendaten ändern und die Änderungen mit Ihrem FHIR-Server synchronisieren können.

7. Nach Patienten anhand des Namens suchen

Wenn Sie nach Patienten anhand ihres Namens suchen, können Sie Informationen auf nutzerfreundliche Weise abrufen. Hier erfahren Sie, wie Sie diese Funktion in Ihrer Anwendung implementieren.

Schritt 1: Funktionssignatur aktualisieren

Rufen Sie die Datei PatientListViewModel.kt auf und suchen Sie die Funktion searchPatientsByName. Wir fügen dieser Funktion Code hinzu.

Wenn Sie die Ergebnisse basierend auf der angegebenen Namensabfrage filtern und die Ergebnisse für die Aktualisierung der Benutzeroberfläche ausgeben möchten, fügen Sie den folgenden bedingten Codeblock ein:

    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 }
      }
    }

Wenn nameQuery nicht leer ist, werden die Suchergebnisse so gefiltert, dass nur Patienten angezeigt werden, deren Namen die angegebene Abfrage enthalten.

Schritt 2: Neue Suchfunktion testen

  1. App neu starten:Nachdem Sie diese Änderungen vorgenommen haben, erstellen Sie Ihre App neu und führen Sie sie aus.
  2. Nach Patienten suchen: Verwenden Sie auf dem Bildschirm mit der Patientenliste die Suchfunktion. Sie können jetzt einen Namen (oder einen Teil eines Namens) eingeben, um die Liste der Patienten entsprechend zu filtern.

Mit diesen Schritten haben Sie Ihre Anwendung verbessert, indem Sie Nutzern die Möglichkeit gegeben haben, effizient nach Patienten anhand ihres Namens zu suchen. Dadurch können die Nutzerfreundlichkeit und die Effizienz beim Abrufen von Daten erheblich verbessert werden.

8. Glückwunsch!

Sie haben die FHIR Engine Library verwendet, um FHIR-Ressourcen in Ihrer App zu verwalten:

  • Sync API verwenden, um FHIR-Ressourcen mit einem FHIR-Server zu synchronisieren
  • Data Access API verwenden, um lokale FHIR-Ressourcen zu erstellen, zu lesen, zu aktualisieren und zu löschen
  • Such-API verwenden, um lokale FHIR-Ressourcen zu suchen

Behandelte Themen

  • Lokalen HAPI FHIR-Server einrichten
  • Testdaten auf den lokalen HAPI FHIR-Server hochladen
  • Android-App mit der FHIR Engine Library erstellen
  • Sync API, Data Access API und Search API in der FHIR Engine Library verwenden

Nächste Schritte

  • Dokumentation zur FHIR Engine Library ansehen
  • Erweiterte Funktionen der Search API ansehen
  • FHIR Engine Library in Ihrer eigenen Android-App verwenden

Weitere Informationen