Zarządzanie zasobami FHIR przy użyciu biblioteki silnika FHIR

1. Zanim zaczniesz

Co utworzysz

W tym ćwiczeniu utworzysz aplikację na Androida za pomocą biblioteki FHIR Engine. Aplikacja będzie używać biblioteki FHIR Engine do pobierania zasobów FHIR z serwera FHIR i przesyłania na serwer wszelkich lokalnych zmian.

Czego się nauczysz

  • Jak utworzyć lokalny serwer HAPI FHIR za pomocą Dockera
  • Jak zintegrować bibliotekę FHIR Engine z aplikacją na Androida
  • Jak używać interfejsu Sync API do konfigurowania jednorazowych lub okresowych zadań pobierania i przesyłania zasobów FHIR
  • Jak korzystać z interfejsu Search API
  • Jak używać interfejsów Data Access API do tworzenia, odczytywania, aktualizowania i usuwania zasobów FHIR lokalnie

Czego potrzebujesz

Jeśli nie masz jeszcze doświadczenia w tworzeniu aplikacji na Androida, możesz zacząć od utworzenia pierwszej aplikacji.

2. Konfigurowanie lokalnego serwera HAPI FHIR z danymi testowymi

HAPI FHIR to popularny serwer FHIR typu open source. W naszym laboratorium kodu używamy lokalnego serwera HAPI FHIR, z którym łączy się aplikacja na Androida.

Konfigurowanie lokalnego serwera HAPI FHIR

  1. Aby pobrać najnowszy obraz HAPI FHIR, uruchom w terminalu to polecenie:
    docker pull hapiproject/hapi:latest
    
  2. Utwórz kontener HAPI FHIR, uruchamiając pobrany wcześniej obraz hapiproject/hapi za pomocą Docker Desktop lub uruchamiając to polecenie:
    docker run -p 8080:8080 hapiproject/hapi:latest
    
    Więcej informacji
  3. Sprawdź serwer, otwierając adres URL http://localhost:8080/ w przeglądarce. Powinien pojawić się interfejs internetowy HAPI FHIR.Interfejs internetowy HAPI FHIR

Wypełnianie lokalnego serwera HAPI FHIR danymi testowymi

Aby przetestować aplikację, potrzebujemy na serwerze danych testowych. Użyjemy danych syntetycznych wygenerowanych przez Synthea.

  1. Najpierw musimy pobrać przykładowe dane z synthea-samples. Pobierz i rozpakuj synthea_sample_data_fhir_r4_sep2019.zip. Rozpakowane dane przykładowe zawierają wiele plików .json, z których każdy jest pakietem transakcji dla konkretnego pacjenta.
  2. Prześlemy dane testowe 3 pacjentów na lokalny serwer HAPI FHIR. Uruchom to polecenie w katalogu zawierającym pliki 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. Aby przesłać na serwer dane testowe wszystkich pacjentów, uruchom
    for f in *.json; do curl -X POST -H "Content-Type: application/json" -d @$f http://localhost:8080/fhir/ ; done
    
    Może to jednak potrwać długo i nie jest konieczne w przypadku tego ćwiczenia.
  4. Sprawdź, czy dane testowe są dostępne na serwerze, otwierając w przeglądarce adres URL http://localhost:8080/fhir/Patient/. W wynikach wyszukiwania powinien się wyświetlić tekst HTTP 200 OK oraz sekcja Response Body strony zawierająca dane pacjenta w pakiecie FHIR z liczbą total.Testowanie danych na serwerze

3. Konfigurowanie aplikacji na Androida

Pobieranie kodu

Aby pobrać kod do tego szkolenia, sklonuj repozytorium Android FHIR SDK: git clone https://github.com/google/android-fhir.git

Projekt początkowy tego ćwiczenia znajduje się w codelabs/engine.

Importowanie aplikacji do Android Studio

Zaczynamy od zaimportowania aplikacji startowej do Android Studio.

Otwórz Android Studio, wybierz Importuj projekt (Gradle, Eclipse ADT itp.) i wybierz folder codelabs/engine/ z pobranego wcześniej kodu źródłowego.

Ekran startowy Android Studio

Synchronizowanie projektu z plikami Gradle

Dla Twojej wygody zależności biblioteki FHIR Engine zostały już dodane do projektu. Umożliwia to zintegrowanie biblioteki FHIR Engine z aplikacją. Zwróć uwagę na te wiersze na końcu pliku app/build.gradle.kts w projekcie:

dependencies {
    // ...

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

Aby mieć pewność, że wszystkie zależności są dostępne dla aplikacji, na tym etapie zsynchronizuj projekt z plikami Gradle.

Na pasku narzędzi Android Studio wybierz Synchronizuj projekt z plikami Gradle (Przycisk synchronizacji Gradle). Możesz też ponownie uruchomić aplikację, aby sprawdzić, czy zależności działają prawidłowo.

Uruchamianie aplikacji startowej

Po zaimportowaniu projektu do Android Studio możesz po raz pierwszy uruchomić aplikację.

Uruchom emulator Android Studio i na pasku narzędzi Android Studio kliknij Uruchom (Przycisk Uruchom).

Aplikacja Hello World

4. Tworzenie instancji FHIR Engine

Aby zintegrować FHIR Engine z aplikacją na Androida, musisz użyć biblioteki FHIR Engine i utworzyć instancję FHIR Engine. Poniższe kroki pomogą Ci przejść przez ten proces.

  1. Otwórz klasę Application, która w tym przykładzie jest oznaczona jako FhirApplication.kt i znajduje się w app/src/main/java/com/google/android/fhir/codelabs/engine.
  2. W metodzie onCreate() dodaj ten kod, aby zainicjować 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)
                },
            ),
          ),
      )
    
    Uwagi:
    • enableEncryptionIfSupported: włącza szyfrowanie danych, jeśli urządzenie je obsługuje.
    • RECREATE_AT_OPEN: określa strategię błędów bazy danych. W takim przypadku w razie błędu podczas otwierania baza danych zostanie utworzona ponownie.
    • baseUrlServerConfiguration: jest to podstawowy adres URL serwera FHIR. Podany adres IP 10.0.2.2 jest specjalnie zarezerwowany dla hosta lokalnego, do którego można uzyskać dostęp z emulatora Androida. Więcej informacji
  3. W klasie FhirApplication dodaj ten wiersz, aby leniwie utworzyć instancję FHIR Engine:
      private val fhirEngine: FhirEngine by
          lazy { FhirEngineProvider.getInstance(this) }
    
    Dzięki temu instancja FhirEngine jest tworzona tylko wtedy, gdy jest używana po raz pierwszy, a nie od razu po uruchomieniu aplikacji.
  4. Dodaj do klasy FhirApplication tę metodę ułatwiającą dostęp w całej aplikacji:
    companion object {
        fun fhirEngine(context: Context) =
            (context.applicationContext as FhirApplication).fhirEngine
    }
    
    Ta metoda statyczna umożliwia pobranie instancji FHIR Engine z dowolnego miejsca w aplikacji za pomocą kontekstu.

5. Synchronizowanie danych z serwerem FHIR

  1. Utwórz nowe zajęcia DownloadWorkManagerImpl.kt. W tej klasie określisz, jak aplikacja pobiera z listy kolejny zasób do pobrania.
      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
        }
      }
    
    Ta klasa ma kolejkę typów zasobów, które chce pobrać. Przetwarza odpowiedzi i wyodrębnia zasoby z zwróconego pakietu, które są zapisywane w lokalnej bazie danych.
  2. Utwórz nową klasę AppFhirSyncWorker.kt Ta klasa określa, jak aplikacja będzie synchronizować się ze zdalnym serwerem FHIR za pomocą procesu działającego w tle.
    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,
        )
    }
    
    W tym miejscu zdefiniowaliśmy, którego menedżera pobierania, narzędzia do rozwiązywania konfliktów i instancji silnika FHIR używać do synchronizacji.
  3. W obiekcie ViewModel PatientListViewModel.kt skonfigurujesz jednorazowy mechanizm synchronizacji. Znajdź ten kod i dodaj go do funkcji triggerOneTimeSync():
    viewModelScope.launch {
          Sync.oneTimeSync<AppFhirSyncWorker>(getApplication())
            .shareIn(this, SharingStarted.Eagerly, 10)
            .collect { _pollState.emit(it) }
        }
    
    Ten współprogram inicjuje jednorazową synchronizację z serwerem FHIR za pomocą zdefiniowanego wcześniej elementu AppFhirSyncWorker. Następnie zaktualizuje interfejs na podstawie stanu procesu synchronizacji.
  4. W pliku PatientListFragment.kt zaktualizuj treść funkcji handleSyncJobStatus:
    when (syncJobStatus) {
        is SyncJobStatus.Finished -> {
            Toast.makeText(requireContext(), "Sync Finished", Toast.LENGTH_SHORT).show()
            viewModel.searchPatientsByName("")
        }
        else -> {}
    }
    
    Po zakończeniu procesu synchronizacji wyświetli się komunikat z powiadomieniem dla użytkownika, a następnie aplikacja wyświetli wszystkich pacjentów, wywołując wyszukiwanie z pustą nazwą.

Gdy wszystko będzie gotowe, uruchom aplikację. W menu kliknij przycisk Sync. Jeśli wszystko działa prawidłowo, pacjenci z lokalnego serwera FHIR powinni zostać pobrani i wyświetleni w aplikacji.

Lista pacjentów

6. Modyfikowanie i przesyłanie danych pacjentów

W tej sekcji przeprowadzimy Cię przez proces modyfikowania danych pacjentów na podstawie określonych kryteriów i przesyłania zaktualizowanych danych na serwer FHIR. W przypadku pacjentów mieszkających w WakefieldTaunton zamienimy miasta adresowe.

Krok 1. Skonfiguruj logikę modyfikacji w klasie PatientListViewModel

Kod w tej sekcji jest dodawany do funkcji triggerUpdate w pliku PatientListViewModel.

  1. Uzyskiwanie dostępu do FHIR Engine: zacznij od uzyskania odwołania do FHIR Engine w PatientListViewModel.kt.
    viewModelScope.launch {
       val fhirEngine = FhirApplication.fhirEngine(getApplication())
    
    Ten kod uruchamia współprogram w zakresie ViewModel i inicjuje silnik FHIR.
  2. Wyszukaj pacjentów z Wakefield: użyj silnika FHIR, aby wyszukać pacjentów, których miasto zamieszkania to Wakefield.
    val patientsFromWakefield =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Wakefield"
             }
           )
         }
    
    W tym przypadku używamy metody search silnika FHIR do filtrowania pacjentów na podstawie miasta, w którym mieszkają. Wynikiem będzie lista pacjentów z Wakefield.
  3. Wyszukaj pacjentów z Taunton: podobnie możesz wyszukać pacjentów, których miasto zamieszkania to Taunton.
    val patientsFromTaunton =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Taunton"
             }
           )
         }
    
    Mamy teraz 2 listy pacjentów – jedną z Wakefield i drugą z Taunton.
  4. Modify Patient Addresses:przejrzyj każdego pacjenta na liście patientsFromWakefield, zmień miasto na Taunton i zaktualizuj dane w silniku FHIR.
    patientsFromWakefield.forEach {
         it.resource.address.first().city = "Taunton"
         fhirEngine.update(it.resource)
    }
    
    Podobnie zaktualizuj każdego pacjenta na liście patientsFromTaunton, aby zmienić jego miasto na Wakefield.
    patientsFromTaunton.forEach {
         it.resource.address.first().city = "Wakefield"
         fhirEngine.update(it.resource)
    }
    
  5. Rozpocznij synchronizację: po zmodyfikowaniu danych lokalnie uruchom jednorazową synchronizację, aby mieć pewność, że dane są aktualizowane na serwerze FHIR.
    triggerOneTimeSync()
    }
    
    Nawias zamykający } oznacza koniec współprogramu uruchomionego na początku.

Krok 2. Przetestuj funkcję

  1. Testowanie interfejsu:uruchom aplikację. W menu kliknij przycisk Update. Powinny wyświetlić się zamienione miasta adresowe pacjentów Aaron697Abby752.
  2. Weryfikacja serwera: otwórz przeglądarkę i przejdź na stronę http://localhost:8080/fhir/Patient/. Sprawdź, czy miasto w adresie pacjentów Aaron697Abby752 zostało zaktualizowane na lokalnym serwerze FHIR.

Wykonując te czynności, udało Ci się wdrożyć mechanizm modyfikowania danych pacjenta i synchronizowania zmian z serwerem FHIR.

7. Wyszukiwanie pacjentów według imienia i nazwiska

Wyszukiwanie pacjentów po imieniu i nazwisku może być wygodnym sposobem na uzyskanie informacji. W tym artykule przeprowadzimy Cię przez proces wdrażania tej funkcji w aplikacji.

Krok 1. Zaktualizuj sygnaturę funkcji

Otwórz plik PatientListViewModel.kt i znajdź funkcję o nazwie searchPatientsByName. Dodamy do tej funkcji kod.

Aby filtrować wyniki na podstawie podanego zapytania o nazwę i emitować wyniki w celu aktualizacji interfejsu, włącz ten blok kodu warunkowego:

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

Jeśli pole nameQuery nie jest puste, funkcja wyszukiwania odfiltruje wyniki, aby uwzględniać tylko pacjentów, których imiona i nazwiska zawierają określone zapytanie.

Krok 2. Przetestuj nową funkcję wyszukiwania

  1. Ponownie uruchom aplikację: po wprowadzeniu tych zmian ponownie skompiluj i uruchom aplikację.
  2. Wyszukiwanie pacjentów: na ekranie listy pacjentów użyj funkcji wyszukiwania. Teraz możesz wpisać nazwę (lub jej część), aby odpowiednio przefiltrować listę pacjentów.

Po wykonaniu tych czynności Twoja aplikacja będzie bardziej funkcjonalna, ponieważ użytkownicy będą mogli skutecznie wyszukiwać pacjentów po nazwisku. Może to znacznie zwiększyć wygodę użytkowników i skuteczność pobierania danych.

8. Gratulacje!

Do zarządzania zasobami FHIR w aplikacji używasz biblioteki FHIR Engine:

  • Synchronizowanie zasobów FHIR z serwerem FHIR za pomocą interfejsu Sync API
  • Tworzenie, odczytywanie, aktualizowanie i usuwanie lokalnych zasobów FHIR za pomocą interfejsu Data Access API
  • Wyszukiwanie lokalnych zasobów FHIR za pomocą interfejsu Search API

Omówione zagadnienia

  • Konfigurowanie lokalnego serwera HAPI FHIR
  • Jak przesyłać dane testowe na lokalny serwer HAPI FHIR
  • Tworzenie aplikacji na Androida za pomocą biblioteki FHIR Engine
  • Jak korzystać z interfejsów Sync API, Data Access API i Search API w bibliotece FHIR Engine

Następne kroki

  • Zapoznaj się z dokumentacją biblioteki FHIR Engine
  • Poznaj zaawansowane funkcje interfejsu Search API
  • Stosowanie biblioteki FHIR Engine we własnej aplikacji na Androida

Więcej informacji