Управляйте ресурсами FHIR с помощью библиотеки FHIR Engine.

1. Прежде чем начать

Что вы построите

В этом практическом занятии вы создадите Android-приложение, используя библиотеку FHIR Engine Library. Ваше приложение будет использовать FHIR Engine Library для загрузки ресурсов FHIR с FHIR-сервера и отправки любых локальных изменений на сервер.

Что вы узнаете

  • Как создать локальный сервер HAPI FHIR с помощью Docker
  • Как интегрировать библиотеку FHIR Engine в ваше Android-приложение
  • Как использовать API синхронизации для настройки разовой или периодической задачи по загрузке и выгрузке ресурсов FHIR.
  • Как использовать API поиска
  • Как использовать API доступа к данным для создания, чтения, обновления и удаления ресурсов FHIR локально.

Что вам понадобится

  • Docker ( получить Docker )
  • Последняя версия Android Studio (v4.1.2+)
  • Эмулятор Android или физическое устройство Android под управлением Android 7.0 Nougat или более поздней версии.
  • Пример кода
  • Базовые знания разработки под Android на Kotlin.

Если вы раньше не создавали приложения для Android, вы можете начать с разработки своего первого приложения .

2. Настройте локальный сервер HAPI FHIR с тестовыми данными.

HAPI FHIR — популярный FHIR-сервер с открытым исходным кодом. В нашей практической работе мы используем локальный FHIR-сервер HAPI для подключения Android-приложения.

Настройте локальный сервер HAPI FHIR.

  1. Выполните следующую команду в терминале, чтобы получить последнюю версию образа HAPI FHIR.
    docker pull hapiproject/hapi:latest
    
  2. Создайте контейнер HAPI FHIR, используя Docker Desktop для запуска ранее загруженного образа hapiproject/hapi или выполнив следующую команду.
    docker run -p 8080:8080 hapiproject/hapi:latest
    
    Узнать больше .
  3. Проверьте сервер, открыв в браузере URL-адрес http://localhost:8080/ . Вы должны увидеть веб-интерфейс HAPI FHIR. HAPI FHIR web interface

Заполните локальный сервер HAPI FHIR тестовыми данными.

Для тестирования нашего приложения нам понадобятся тестовые данные на сервере. Мы будем использовать синтетические данные, сгенерированные программой Synthea.

  1. Сначала нам нужно загрузить примеры данных из synthea-samples . Скачайте и распакуйте synthea_sample_data_fhir_r4_sep2019.zip . Распакованные примеры данных содержат множество файлов .json , каждый из которых представляет собой пакет транзакций для отдельного пациента.
  2. Мы загрузим тестовые данные для трех пациентов на локальный сервер HAPI FHIR. Выполните следующую команду в каталоге, содержащем файлы 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. Для загрузки тестовых данных всех пациентов на сервер выполните следующую команду:
    for f in *.json; do curl -X POST -H "Content-Type: application/json" -d @$f http://localhost:8080/fhir/ ; done
    
    Однако выполнение этого задания может занять много времени и не является обязательным для практического занятия.
  4. Убедитесь, что тестовые данные доступны на сервере, открыв в браузере URL-адрес http://localhost:8080/fhir/Patient/ . В результатах поиска вы должны увидеть текст HTTP 200 OK и раздел « Response Body страницы, содержащей данные о пациентах в пакете FHIR, с указанием total количества результатов. Тестовые данные на сервере

3. Настройте приложение для Android.

Скачать код

Чтобы загрузить код для этого практического занятия, клонируйте репозиторий Android FHIR SDK: git clone https://github.com/google/android-fhir.git

Стартовый проект для этой практической работы находится в codelabs/engine .

Импортируйте приложение в Android Studio.

Начнём с импорта стартового приложения в Android Studio.

Откройте Android Studio, выберите «Импорт проекта» (Gradle, Eclipse ADT и т. д.) и выберите папку codelabs/engine/ из исходного кода, который вы скачали ранее.

Android Studio start screen

Синхронизируйте свой проект с файлами Gradle.

Для вашего удобства зависимости библиотеки FHIR Engine уже добавлены в проект. Это позволит вам интегрировать библиотеку FHIR Engine в ваше приложение. Обратите внимание на следующие строки в конце файла app/build.gradle.kts вашего проекта:

dependencies {
    // ...

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

Чтобы убедиться, что все зависимости доступны вашему приложению, на этом этапе следует синхронизировать проект с файлами Gradle.

Выберите «Синхронизировать проект с файлами Gradle» ( Gradle sync button ) из панели инструментов Android Studio. Вы также можете запустить приложение еще раз, чтобы проверить корректность работы зависимостей.

Запустите стартовое приложение

Теперь, когда вы импортировали проект в Android Studio, вы готовы запустить приложение в первый раз.

Запустите эмулятор Android Studio и нажмите «Запустить» ( Run button ) на панели инструментов Android Studio.

Приложение Hello World

4. Создайте экземпляр FHIR Engine.

Для интеграции FHIR Engine в ваше Android-приложение вам потребуется использовать библиотеку FHIR Engine и запустить экземпляр FHIR Engine. Ниже описаны шаги, которые помогут вам в этом процессе.

  1. Перейдите к классу Application, который в данном примере называется FhirApplication.kt и находится в папке app/src/main/java/com/google/android/fhir/codelabs/engine .
  2. Внутри метода onCreate() добавьте следующий код для инициализации 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)
                },
            ),
          ),
      )
    
    Примечания:
    • enableEncryptionIfSupported : Включает шифрование данных, если устройство его поддерживает.
    • RECREATE_AT_OPEN : Определяет стратегию обработки ошибок базы данных. В данном случае база данных пересоздается, если при открытии возникает ошибка.
    • baseUrl в ServerConfiguration : это базовый URL-адрес FHIR-сервера. Предоставленный IP-адрес 10.0.2.2 специально зарезервирован для localhost и доступен из эмулятора Android. Подробнее .
  3. В класс FhirApplication добавьте следующую строку для отложенного создания экземпляра FHIR Engine:
      private val fhirEngine: FhirEngine by
          lazy { FhirEngineProvider.getInstance(this) }
    
    Это гарантирует, что экземпляр FhirEngine будет создан только при первом обращении к нему, а не сразу после запуска приложения.
  4. Добавьте следующий вспомогательный метод в класс FhirApplication для более удобного доступа ко всему приложению:
    companion object {
        fun fhirEngine(context: Context) =
            (context.applicationContext as FhirApplication).fhirEngine
    }
    
    Этот статический метод позволяет получить доступ к экземпляру FHIR Engine из любой точки приложения, используя контекст.

5. Синхронизация данных с сервером FHIR.

  1. Создайте новый класс DownloadWorkManagerImpl.kt . В этом классе вы определите, как приложение будет выбирать следующий ресурс из списка для загрузки.
      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
        }
      }
    
    Этот класс имеет очередь типов ресурсов, которые он хочет загрузить. Он обрабатывает ответы и извлекает ресурсы из возвращенного пакета, которые сохраняются в локальной базе данных.
  2. Создайте новый класс AppFhirSyncWorker.kt Этот класс определяет, как приложение будет синхронизироваться с удаленным FHIR-сервером с помощью фонового рабочего процесса.
    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,
        )
    }
    
    Здесь мы определили, какой менеджер загрузок, средство разрешения конфликтов и экземпляр движка FHIR использовать для синхронизации.
  3. В вашем ViewModel, PatientListViewModel.kt , необходимо настроить механизм однократной синхронизации. Найдите и добавьте следующий код в функцию triggerOneTimeSync() :
    viewModelScope.launch {
          Sync.oneTimeSync<AppFhirSyncWorker>(getApplication())
            .shareIn(this, SharingStarted.Eagerly, 10)
            .collect { _pollState.emit(it) }
        }
    
    Эта сопрограмма инициирует однократную синхронизацию с FHIR-сервером, используя определенный нами ранее объект AppFhirSyncWorker. Затем она обновит пользовательский интерфейс в зависимости от состояния процесса синхронизации.
  4. В файле PatientListFragment.kt обновите тело функции handleSyncJobStatus :
    when (syncJobStatus) {
        is SyncJobStatus.Finished -> {
            Toast.makeText(requireContext(), "Sync Finished", Toast.LENGTH_SHORT).show()
            viewModel.searchPatientsByName("")
        }
        else -> {}
    }
    
    После завершения процесса синхронизации на экране появится всплывающее сообщение, уведомляющее пользователя, и приложение отобразит всех пациентов, выполнив поиск по пустому имени.

Теперь, когда все настроено, запустите приложение. Нажмите кнопку Sync в меню. Если все работает правильно, вы должны увидеть, как данные о пациентах с вашего локального FHIR-сервера загружаются и отображаются в приложении.

Список пациентов

6. Изменение и загрузка данных пациента.

В этом разделе мы расскажем вам, как изменить данные пациентов на основе определенных критериев и загрузить обновленные данные на ваш FHIR-сервер. В частности, мы заменим города-адреса для пациентов, проживающих в Wakefield и Taunton .

Шаг 1 : Настройте логику модификации в PatientListViewModel.

Код в этом разделе добавляется в функцию triggerUpdate в PatientListViewModel

  1. Для доступа к движку FHIR начните с получения ссылки на движок FHIR в файле PatientListViewModel.kt .
    viewModelScope.launch {
       val fhirEngine = FhirApplication.fhirEngine(getApplication())
    
    Этот код запускает сопрограмму в области видимости ViewModel и инициализирует движок FHIR.
  2. Поиск пациентов из Уэйкфилда : Используйте поисковую систему FHIR для поиска пациентов, адрес которых указан как Wakefield .
    val patientsFromWakefield =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Wakefield"
             }
           )
         }
    
    Здесь мы используем метод search системы FHIR для фильтрации пациентов по городу их проживания. В результате будет получен список пациентов из Уэйкфилда.
  3. Поиск пациентов из Тонтона : Аналогично, выполните поиск пациентов, у которых в качестве адреса указан город Taunton .
    val patientsFromTaunton =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Taunton"
             }
           )
         }
    
    Теперь у нас есть два списка пациентов — один из Уэйкфилда, а другой из Тонтона.
  4. Изменение адресов пациентов : Пройдитесь по каждому пациенту в списке patientsFromWakefield , измените его город на Taunton и обновите данные в системе FHIR.
    patientsFromWakefield.forEach {
         it.resource.address.first().city = "Taunton"
         fhirEngine.update(it.resource)
    }
    
    Аналогичным образом обновите данные каждого пациента в списке patientsFromTaunton , изменив его город на Wakefield .
    patientsFromTaunton.forEach {
         it.resource.address.first().city = "Wakefield"
         fhirEngine.update(it.resource)
    }
    
  5. Инициировать синхронизацию : После внесения изменений в данные локально, запустите одноразовую синхронизацию, чтобы убедиться, что данные обновлены на сервере FHIR.
    triggerOneTimeSync()
    }
    
    Закрывающая фигурная скобка } обозначает конец сопрограммы, запущенной в начале.

Шаг 2 : Проверка функциональности

  1. Тестирование пользовательского интерфейса : Запустите приложение. Нажмите кнопку Update в меню. Вы должны увидеть, что адреса городов пациентов Aaron697 и Abby752 поменяны местами.
  2. Проверка сервера : Откройте браузер и перейдите по адресу http://localhost:8080/fhir/Patient/ . Убедитесь, что город проживания пациентов Aaron697 и Abby752 обновлен на локальном FHIR-сервере.

Выполнив эти шаги, вы успешно внедрили механизм для изменения данных пациентов и синхронизации изменений с вашим FHIR-сервером.

7. Поиск пациентов по имени.

Поиск пациентов по имени может обеспечить удобный способ получения информации. В этой статье мы расскажем, как реализовать эту функцию в вашем приложении.

Шаг 1 : Обновите сигнатуру функции.

Перейдите в файл PatientListViewModel.kt и найдите функцию с именем searchPatientsByName . Мы добавим код в эту функцию.

Для фильтрации результатов на основе предоставленного запроса по имени и вывода результатов в пользовательский интерфейс для обновления, используйте следующий блок условного кода:

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

В этом случае, если поле nameQuery не пустое, функция поиска отфильтрует результаты, включив только пациентов, чьи имена содержат указанный запрос.

Шаг 2 : Проверьте новую функцию поиска.

  1. Перезапустите приложение : После внесения этих изменений пересоберите и запустите приложение.
  2. Поиск пациентов : На экране списка пациентов воспользуйтесь функцией поиска. Теперь вы сможете ввести имя (или часть имени), чтобы отфильтровать список пациентов соответствующим образом.

Выполнив эти шаги, вы улучшили свое приложение, предоставив пользователям возможность эффективно искать пациентов по их именам. Это может значительно улучшить пользовательский опыт и повысить эффективность поиска данных.

8. Поздравляем!

Вы использовали библиотеку FHIR Engine Library для управления ресурсами FHIR в своем приложении:

  • Используйте API синхронизации для синхронизации ресурсов FHIR с сервером FHIR.
  • Используйте API доступа к данным для создания, чтения, обновления и удаления локальных ресурсов FHIR.
  • Используйте Search API для поиска локальных ресурсов FHIR.

Что мы рассмотрели

  • Как настроить локальный сервер HAPI FHIR
  • Как загрузить тестовые данные на локальный сервер HAPI FHIR
  • Как создать Android-приложение с использованием библиотеки FHIR Engine.
  • Как использовать API синхронизации, API доступа к данным и API поиска в библиотеке FHIR Engine.

Следующие шаги

  • Изучите документацию по библиотеке FHIR Engine.
  • Изучите расширенные возможности API поиска.
  • Примените библиотеку FHIR Engine в своем собственном приложении для Android.

Узнать больше