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

1. Zanim zaczniesz

Co utworzysz

W tym ćwiczeniu z programowania 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 zmian wprowadzonych lokalnie.

Czego się nauczysz

  • Jak utworzyć lokalny serwer HAPI FHIR za pomocą Dockera
  • Jak zintegrować bibliotekę FHIR Engine z aplikacją na Androida
  • Jak użyć interfejsu Sync API, aby skonfigurować jednorazowe lub okresowe zadanie pobierania i przesyłania zasobów FHIR
  • Jak korzystać z interfejsu Search API
  • Jak używać interfejsów API dostępu do danych do tworzenia, odczytywania, aktualizowania i usuwania zasobów FHIR lokalnie

Czego potrzebujesz

Jeśli nie tworzysz jeszcze aplikacji na Androida, możesz zacząć od utworzenia pierwszej aplikacji.

2. Konfigurowanie lokalnego serwera HAPI FHIR za pomocą danych testowych

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 uzyskać najnowszy obraz HAPI FHIR, uruchom to polecenie w terminalu:
    docker pull hapiproject/hapi:latest
    
  2. Utwórz kontener HAPI FHIR, używając Docker Desktop do uruchomienia wcześniej pobranego obrazu hapiproject/hapi lub wykonują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 wyświetlić się interfejs internetowy HAPI FHIR.Interfejs internetowy HAPI FHIR

Wypełnianie lokalnego serwera HAPI FHIR danymi testowymi

Aby przetestować naszą aplikację, potrzebujemy na serwerze niektórych danych testowych. Użyjemy danych syntetycznych wygenerowanych przez Synthea.

  1. Najpierw musimy pobrać przykładowe dane z synthea-samples. Pobierz i wyodrębnij plik synthea_sample_data_fhir_r4_sep2019.zip. Rozpakowane przykładowe dane zawierają wiele plików .json, z których każdy stanowi pakiet transakcji dotyczącej konkretnego pacjenta.
  2. Prześlemy dane testowe dotyczące 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 testowe dane wszystkich pacjentów, uruchom
    for f in *.json; do curl -X POST -H "Content-Type: application/json" -d @$f http://localhost:8080/fhir/ ; done
    
    To może jednak zająć dużo czasu i nie jest konieczne do wykonania ćwiczenia.
  4. Aby sprawdzić, czy dane testowe są dostępne na serwerze, otwórz adres URL http://localhost:8080/fhir/Patient/ w przeglądarce. W wynikach wyszukiwania powinna się wyświetlić strona zawierająca dane pacjenta w pakiecie FHIR z tekstem HTTP 200 OK i sekcją Response Body oraz liczba total.Testowanie danych na serwerze

3. Konfigurowanie aplikacji na Androida

Pobieranie kodu

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

Projekt startowy tego Codelab znajduje się w folderze codelabs/engine.

Zaimportuj aplikację do Android Studio

Zacznij od zaimportowania aplikacji startowej do Android Studio.

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

Ekran startowy Android Studio

Synchronizacja projektu z plikami Gradle

Dla Twojej wygody zależności biblioteki FHIR Engine zostały już dodane do projektu. Umożliwi Ci to zintegrowanie biblioteki FHIR Engine w swojej aplikacji. Sprawdź te wiersze na końcu pliku app/build.gradle.kts projektu:

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 kliknij 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 uruchomić aplikację po raz pierwszy.

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

Aplikacja Cześć wszystkim

4. Tworzenie instancji FHIR Engine

Aby włączyć silnik FHIR w aplikacji na Androida, musisz użyć biblioteki FHIR Engine i uruchomić instancję FHIR Engine. Poniżej znajdziesz instrukcje, które pomogą Ci przejść przez cały proces.

  1. Przejdź do klasy Application, która w tym przykładzie to FhirApplication.kt i znajduje się w app/src/main/java/com/google/android/fhir/codelabs/engine.
  2. Aby zainicjować silnik FHIR, dodaj do metody onCreate() ten kod:
      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 obsługuje tę funkcję.
    • RECREATE_AT_OPEN: określa strategię dotyczącą błędów bazy danych. W takim przypadku, jeśli podczas otwierania wystąpi błąd, baza danych zostanie utworzona ponownie.
    • baseUrlServerConfiguration: to jest podstawowy adres URL serwera FHIR. Podany adres IP 10.0.2.2 jest zarezerwowany specjalnie dla localhosta i jest dostępny z emulatora Androida. Więcej informacji
  3. W klasie FhirApplication dodaj ten wiersz, aby leniwie utworzyć instancję silnika FHIR:
      private val fhirEngine: FhirEngine by
          lazy { FhirEngineProvider.getInstance(this) }
    
    W ten sposób instancja FhirEngine jest tworzona tylko wtedy, gdy jest po raz pierwszy używana, a nie natychmiast po uruchomieniu aplikacji.
  4. Aby ułatwić dostęp do klasy FhirApplication w całej aplikacji, dodaj do niej tę wygodną metodę:
    companion object {
        fun fhirEngine(context: Context) =
            (context.applicationContext as FhirApplication).fhirEngine
    }
    
    Ta metoda statyczna umożliwia odzyskiwanie instancji FHIR Engine z dowolnego miejsca w aplikacji za pomocą kontekstu.

5. Synchronizacja danych z serwerem FHIR

  1. Utwórz nowe zajęcia DownloadWorkManagerImpl.kt. W tej lekcji określisz, jak aplikacja pobiera kolejny zasób z listy 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 ona odpowiedzi i wyodrębnia z zwróconego pakietu zasoby, 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ę z zdalnym serwerem FHIR za pomocą podprocesu 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,
        )
    }
    
    Tutaj definiujemy, którego menedżera pobierania, rozwiązywania konfliktów i instancji silnika FHIR używać do synchronizacji.
  3. W ViewModel PatientListViewModel.kt skonfigurujesz mechanizm jednorazowej synchronizacji. Zlokalizuj i dodaj do funkcji triggerOneTimeSync() ten kod:
    viewModelScope.launch {
          Sync.oneTimeSync<AppFhirSyncWorker>(getApplication())
            .shareIn(this, SharingStarted.Eagerly, 10)
            .collect { _pollState.emit(it) }
        }
    
    Ta coroutine inicjuje jednorazową synchronizację z serwerem FHIR za pomocą wcześniej zdefiniowanego obiektu AppFhirSyncWorker. Następnie zaktualizuje interfejs na podstawie stanu procesu synchronizacji.
  4. W pliku PatientListFragment.kt zaktualizuj ciało 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 informujący o tym 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 poprowadzimy Cię przez proces modyfikowania danych pacjenta na podstawie określonych kryteriów i przesyłania zaktualizowanych danych na serwer FHIR. W szczególności zamienimy miasta adresów pacjentów mieszkających w WakefieldTaunton.

Krok 1. Skonfiguruj logikę modyfikacji w PatientListViewModel.

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

  1. Uzyskiwanie dostępu do silnika FHIR: zacznij od uzyskania odwołania do silnika FHIR w PatientListViewModel.kt.
    viewModelScope.launch {
       val fhirEngine = FhirApplication.fhirEngine(getApplication())
    
    Ten kod uruchamia coroutine w zakresie ViewModel i inicjalizuje silnik FHIR.
  2. Wyszukaj pacjentów z Wakefield: użyj mechanizmu FHIR do wyszukania pacjentów z adresem w mieście Wakefield.
    val patientsFromWakefield =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Wakefield"
             }
           )
         }
    
    Tutaj używamy metody search silnika FHIR do filtrowania pacjentów według miasta ich adresu. Wynikiem będzie lista pacjentów z Wakefield.
  3. Wyszukiwanie pacjentów z Taunton: podobnie możesz wyszukać pacjentów, których adres znajduje się w mieście 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, a drugą z Taunton.
  4. Modyfikowanie adresów pacjentów: przejrzyj listę patientsFromWakefield, zmień miasto na Taunton i zaktualizuj dane w mechanizmie FHIR.
    patientsFromWakefield.forEach {
         it.resource.address.first().city = "Taunton"
         fhirEngine.update(it.resource)
    }
    
    Analogicznie 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. Rozpoczęcie synchronizacji: po zmodyfikowaniu danych lokalnie należy wywołać jednorazową synchronizację, aby dane zostały zaktualizowane na serwerze FHIR.
    triggerOneTimeSync()
    }
    
    Zamykająca klamra } oznacza koniec uruchamianej na początku łańcucha coroutine.

Krok 2. Przetestuj funkcję

  1. Testowanie interfejsu użytkownika: uruchom aplikację. W menu kliknij przycisk Update. Powinny się zmienić miasta adresów pacjentów Aaron697Abby752.
  2. Weryfikacja serwera: otwórz przeglądarkę i przejdź na stronę http://localhost:8080/fhir/Patient/. Sprawdź, czy adres miasta dla pacjentów Aaron697 i Abby752 jest zaktualizowany na lokalnym serwerze FHIR.

Po wykonaniu tych czynności udało Ci się wdrożyć mechanizm modyfikowania danych pacjenta i synchronizowania zmian z serwerem FHIR.

7. Wyszukaj pacjentów według nazwiska

Wyszukiwanie pacjentów według nazwiska może być wygodnym sposobem na uzyskiwanie informacji. Poniżej poprowadzimy Cię przez proces implementacji tej funkcji w aplikacji.

Krok 1. Zaktualizuj sygnaturę funkcji

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

Aby filtrować wyniki na podstawie podanego zapytania o nazwę i wysyłać je do interfejsu w celu ich zaktualizowania, dodaj 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 nameQuery nie jest pusty, funkcja wyszukiwania odfiltruje wyniki, aby uwzględnić tylko pacjentów, których nazwiska zawierają określone zapytanie.

Krok 2. Przetestuj nową funkcję wyszukiwania

  1. Uruchom ponownie 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 odfiltrować listę pacjentów.

Po wykonaniu tych czynności aplikacja będzie umożliwiać użytkownikom wyszukiwanie pacjentów po nazwisku. Może to znacznie ulepszyć wrażenia użytkowników i skuteczność pobierania danych.

8. Gratulacje!

Użyjesz biblioteki FHIR Engine do zarządzania zasobami FHIR w aplikacji:

  • 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
  • Używanie interfejsu Search API do wyszukiwania lokalnych zasobów FHIR

Omówione zagadnienia

  • Jak skonfigurować lokalny serwer HAPI FHIR
  • Jak przesłać dane testowe na lokalny serwer FHIR HAPI
  • Jak utworzyć aplikację na Androida za pomocą biblioteki FHIR Engine
  • Jak używać interfejsów API synchronizacji, dostępu do danych i wyszukiwania w bibliotece silnika FHIR

Następne kroki

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

Więcej informacji