Quản lý tài nguyên FHIR bằng Thư viện FHIR Engine

1. Trước khi bắt đầu

Sản phẩm bạn sẽ tạo ra

Trong lớp học lập trình này, bạn sẽ tạo một ứng dụng Android bằng Thư viện FHIR Engine. Ứng dụng của bạn sẽ sử dụng Thư viện FHIR Engine để tải tài nguyên FHIR xuống từ một máy chủ FHIR và tải mọi thay đổi cục bộ lên máy chủ.

Kiến thức bạn sẽ học được

  • Cách tạo máy chủ HAPI FHIR cục bộ bằng Docker
  • Cách tích hợp Thư viện Công cụ FHIR vào ứng dụng Android
  • Cách sử dụng Sync API để thiết lập một công việc một lần hoặc định kỳ nhằm tải xuống và tải lên các tài nguyên FHIR
  • Cách sử dụng Search API
  • Cách sử dụng Data Access API để tạo, đọc, cập nhật và xoá tài nguyên FHIR cục bộ

Bạn cần có

Nếu chưa từng tạo ứng dụng Android, bạn có thể bắt đầu bằng cách tạo ứng dụng đầu tiên.

2. Thiết lập một máy chủ HAPI FHIR cục bộ bằng dữ liệu thử nghiệm

HAPI FHIR là một máy chủ FHIR nguồn mở phổ biến. Chúng tôi sử dụng một máy chủ HAPI FHIR cục bộ trong lớp học lập trình để ứng dụng Android kết nối.

Thiết lập máy chủ HAPI FHIR cục bộ

  1. Chạy lệnh sau trong một thiết bị đầu cuối để lấy hình ảnh mới nhất của HAPI FHIR
    docker pull hapiproject/hapi:latest
    
  2. Tạo một vùng chứa HAPI FHIR bằng cách dùng Docker Desktop để chạy hình ảnh đã tải xuống trước đó hapiproject/hapi hoặc chạy lệnh sau
    docker run -p 8080:8080 hapiproject/hapi:latest
    
    Tìm hiểu thêm.
  3. Kiểm tra máy chủ bằng cách mở URL http://localhost:8080/ trong trình duyệt. Bạn sẽ thấy giao diện web HAPI FHIR.Giao diện web HAPI FHIR

Điền dữ liệu kiểm thử vào máy chủ HAPI FHIR cục bộ

Để kiểm thử ứng dụng, chúng ta sẽ cần một số dữ liệu kiểm thử trên máy chủ. Chúng ta sẽ sử dụng dữ liệu tổng hợp do Synthea tạo.

  1. Trước tiên, chúng ta cần tải dữ liệu mẫu xuống từ synthea-samples. Tải synthea_sample_data_fhir_r4_sep2019.zip xuống và giải nén. Dữ liệu mẫu chưa giải nén có nhiều tệp .json, mỗi tệp là một gói giao dịch cho một bệnh nhân.
  2. Chúng ta sẽ tải dữ liệu kiểm thử cho 3 bệnh nhân lên máy chủ HAPI FHIR cục bộ. Chạy lệnh sau trong thư mục chứa các tệp 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. Để tải dữ liệu kiểm thử cho tất cả bệnh nhân lên máy chủ, hãy chạy
    for f in *.json; do curl -X POST -H "Content-Type: application/json" -d @$f http://localhost:8080/fhir/ ; done
    
    Tuy nhiên, quá trình này có thể mất nhiều thời gian và không cần thiết cho lớp học lập trình.
  4. Xác minh rằng dữ liệu kiểm thử có trên máy chủ bằng cách mở URL http://localhost:8080/fhir/Patient/ trong trình duyệt. Bạn sẽ thấy văn bản HTTP 200 OK và phần Response Body của trang chứa dữ liệu bệnh nhân trong một Gói FHIR dưới dạng kết quả tìm kiếm có số lượng total.Dữ liệu kiểm thử trên máy chủ

3. Thiết lập ứng dụng Android

Tải mã nguồn xuống

Để tải mã xuống cho lớp học lập trình này, hãy sao chép kho lưu trữ Android FHIR SDK: git clone https://github.com/google/android-fhir.git

Dự án khởi đầu cho lớp học lập trình này nằm trong codelabs/engine.

Nhập ứng dụng vào Android Studio

Chúng ta bắt đầu bằng cách nhập ứng dụng khởi đầu vào Android Studio.

Mở Android Studio, chọn Import Project (Gradle, Eclipse ADT, etc.) (Nhập dự án (Gradle, Eclipse ADT, v.v.)) rồi chọn thư mục codelabs/engine/ trong mã nguồn mà bạn đã tải xuống trước đó.

Màn hình khởi động Android Studio

Đồng bộ hoá dự án với các tệp Gradle

Để thuận tiện cho bạn, các phần phụ thuộc của Thư viện FHIR Engine đã được thêm vào dự án. Điều này cho phép bạn tích hợp Thư viện FHIR Engine vào ứng dụng của mình. Hãy quan sát các dòng sau cho đến cuối tệp app/build.gradle.kts của dự án:

dependencies {
    // ...

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

Để đảm bảo rằng tất cả các phần phụ thuộc đều có sẵn cho ứng dụng của bạn, bạn nên đồng bộ hoá dự án với các tệp gradle tại thời điểm này.

Chọn Đồng bộ hoá dự án với tệp Gradle (Nút đồng bộ hoá Gradle) trên thanh công cụ của Android Studio. Bạn cũng có thể chạy lại ứng dụng để kiểm tra xem các phần phụ thuộc có hoạt động đúng cách hay không.

Chạy ứng dụng khởi đầu

Giờ đây, sau khi nhập dự án vào Android Studio, bạn đã sẵn sàng chạy ứng dụng lần đầu tiên.

Khởi động trình mô phỏng Android Studio rồi nhấp vào Chạy (Nút Run (Chạy)) trong thanh công cụ của Android Studio.

Ứng dụng Hello World

4. Tạo thực thể FHIR Engine

Để kết hợp FHIR Engine vào ứng dụng Android, bạn cần sử dụng Thư viện FHIR Engine và khởi tạo một phiên bản của FHIR Engine. Các bước dưới đây sẽ hướng dẫn bạn thực hiện quy trình này.

  1. Chuyển đến lớp Ứng dụng của bạn. Trong ví dụ này, lớp đó là FhirApplication.kt, nằm trong app/src/main/java/com/google/android/fhir/codelabs/engine.
  2. Trong phương thức onCreate(), hãy thêm mã sau để khởi chạy 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)
                },
            ),
          ),
      )
    
    Ghi chú:
    • enableEncryptionIfSupported: Bật chế độ mã hoá dữ liệu nếu thiết bị hỗ trợ.
    • RECREATE_AT_OPEN: Xác định chiến lược lỗi cơ sở dữ liệu. Trong trường hợp này, phương thức sẽ tạo lại cơ sở dữ liệu nếu xảy ra lỗi khi mở.
    • baseUrl trong ServerConfiguration: Đây là URL cơ sở của máy chủ FHIR. Địa chỉ IP 10.0.2.2 được cung cấp được dành riêng cho máy chủ cục bộ, có thể truy cập từ trình mô phỏng Android. Tìm hiểu thêm.
  3. Trong lớp FhirApplication, hãy thêm dòng sau để tạo thực thể FHIR Engine một cách gián tiếp:
      private val fhirEngine: FhirEngine by
          lazy { FhirEngineProvider.getInstance(this) }
    
    Điều này đảm bảo rằng phiên bản FhirEngine chỉ được tạo khi được truy cập lần đầu tiên, chứ không phải ngay khi ứng dụng khởi động.
  4. Thêm phương thức tiện lợi sau đây vào lớp FhirApplication để dễ dàng truy cập trong toàn bộ ứng dụng:
    companion object {
        fun fhirEngine(context: Context) =
            (context.applicationContext as FhirApplication).fhirEngine
    }
    
    Phương thức tĩnh này cho phép bạn truy xuất thực thể FHIR Engine từ mọi nơi trong ứng dụng bằng cách sử dụng ngữ cảnh.

5. Đồng bộ hoá dữ liệu với máy chủ FHIR

  1. Tạo một lớp học mới DownloadWorkManagerImpl.kt. Trong lớp này, bạn sẽ xác định cách ứng dụng tìm nạp tài nguyên tiếp theo từ danh sách để tải xuống:
      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
        }
      }
    
    Lớp này có một hàng đợi các loại tài nguyên mà lớp muốn tải xuống. Nó xử lý các phản hồi và trích xuất các tài nguyên từ gói được trả về, sau đó lưu vào cơ sở dữ liệu cục bộ.
  2. Tạo một lớp mới AppFhirSyncWorker.kt Lớp này xác định cách ứng dụng sẽ đồng bộ hoá với máy chủ FHIR từ xa bằng một worker nền.
    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,
        )
    }
    
    Ở đây, chúng ta đã xác định trình quản lý tải xuống, trình phân giải xung đột và phiên bản công cụ FHIR nào sẽ dùng để đồng bộ hoá.
  3. Trong ViewModel, PatientListViewModel.kt, bạn sẽ thiết lập một cơ chế đồng bộ hoá một lần. Tìm và thêm mã này vào hàm triggerOneTimeSync():
    viewModelScope.launch {
          Sync.oneTimeSync<AppFhirSyncWorker>(getApplication())
            .shareIn(this, SharingStarted.Eagerly, 10)
            .collect { _pollState.emit(it) }
        }
    
    Coroutine này bắt đầu quá trình đồng bộ hoá một lần với máy chủ FHIR bằng AppFhirSyncWorker mà chúng ta đã xác định trước đó. Sau đó, ứng dụng sẽ cập nhật giao diện người dùng dựa trên trạng thái của quy trình đồng bộ hoá.
  4. Trong tệp PatientListFragment.kt, hãy cập nhật nội dung của hàm handleSyncJobStatus:
    when (syncJobStatus) {
        is SyncJobStatus.Finished -> {
            Toast.makeText(requireContext(), "Sync Finished", Toast.LENGTH_SHORT).show()
            viewModel.searchPatientsByName("")
        }
        else -> {}
    }
    
    Tại đây, khi quá trình đồng bộ hoá hoàn tất, một thông báo ngắn sẽ xuất hiện để thông báo cho người dùng, sau đó ứng dụng sẽ hiển thị tất cả bệnh nhân bằng cách gọi một lượt tìm kiếm với tên trống.

Giờ thì mọi thứ đã sẵn sàng, hãy chạy ứng dụng của bạn. Nhấp vào nút Sync trong trình đơn. Nếu mọi thứ hoạt động bình thường, bạn sẽ thấy bệnh nhân từ máy chủ FHIR cục bộ được tải xuống và hiển thị trong ứng dụng.

Danh sách bệnh nhân

6. Sửa đổi và tải dữ liệu bệnh nhân lên

Trong phần này, chúng tôi sẽ hướng dẫn bạn quy trình sửa đổi dữ liệu bệnh nhân dựa trên các tiêu chí cụ thể và tải dữ liệu đã cập nhật lên máy chủ FHIR. Cụ thể, chúng tôi sẽ hoán đổi thành phố trong địa chỉ của bệnh nhân sinh sống ở WakefieldTaunton.

Bước 1: Thiết lập logic sửa đổi trong PatientListViewModel

Mã trong phần này được thêm vào hàm triggerUpdate trong PatientListViewModel

  1. Truy cập vào FHIR Engine:Bắt đầu bằng cách tham chiếu đến FHIR Engine trong PatientListViewModel.kt.
    viewModelScope.launch {
       val fhirEngine = FhirApplication.fhirEngine(getApplication())
    
    Mã này chạy một coroutine trong phạm vi của ViewModel và khởi động công cụ FHIR.
  2. Tìm kiếm bệnh nhân ở Wakefield:Sử dụng công cụ FHIR để tìm kiếm bệnh nhân có thành phố địa chỉ là Wakefield.
    val patientsFromWakefield =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Wakefield"
             }
           )
         }
    
    Ở đây, chúng ta đang sử dụng phương thức search của công cụ FHIR để lọc bệnh nhân dựa trên thành phố trong địa chỉ của họ. Kết quả sẽ là danh sách bệnh nhân ở Wakefield.
  3. Tìm bệnh nhân ở Taunton:Tương tự, hãy tìm bệnh nhân có thành phố địa chỉ là Taunton.
    val patientsFromTaunton =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Taunton"
             }
           )
         }
    
    Hiện tại, chúng ta có hai danh sách bệnh nhân, một ở Wakefield và một ở Taunton.
  4. Sửa đổi địa chỉ của bệnh nhân:Xem xét từng bệnh nhân trong danh sách patientsFromWakefield, thay đổi thành phố của họ thành Taunton và cập nhật họ trong công cụ FHIR.
    patientsFromWakefield.forEach {
         it.resource.address.first().city = "Taunton"
         fhirEngine.update(it.resource)
    }
    
    Tương tự, hãy cập nhật từng bệnh nhân trong danh sách patientsFromTaunton để thay đổi thành phố của họ thành Wakefield.
    patientsFromTaunton.forEach {
         it.resource.address.first().city = "Wakefield"
         fhirEngine.update(it.resource)
    }
    
  5. Bắt đầu đồng bộ hoá:Sau khi sửa đổi dữ liệu cục bộ, hãy kích hoạt quá trình đồng bộ hoá một lần để đảm bảo dữ liệu được cập nhật trên máy chủ FHIR.
    triggerOneTimeSync()
    }
    
    Dấu ngoặc nhọn đóng } biểu thị phần cuối của coroutine được khởi chạy ở phần đầu.

Bước 2: Kiểm thử chức năng

  1. Kiểm thử giao diện người dùng:Chạy ứng dụng. Nhấp vào nút Update trong trình đơn. Bạn sẽ thấy thành phố trong địa chỉ của bệnh nhân Aaron697Abby752 đã được đổi chỗ.
  2. Xác minh máy chủ:Mở trình duyệt rồi chuyển đến http://localhost:8080/fhir/Patient/. Xác minh rằng thành phố trong địa chỉ của bệnh nhân Aaron697Abby752 đã được cập nhật trên máy chủ FHIR tại địa phương.

Bằng cách làm theo các bước này, bạn đã triển khai thành công một cơ chế để sửa đổi dữ liệu bệnh nhân và đồng bộ hoá các thay đổi với máy chủ FHIR.

7. Tìm kiếm bệnh nhân theo tên

Việc tìm kiếm bệnh nhân theo tên có thể là một cách truy xuất thông tin thân thiện với người dùng. Trong phần này, chúng tôi sẽ hướng dẫn bạn quy trình triển khai tính năng này trong ứng dụng của bạn.

Bước 1: Cập nhật chữ ký hàm

Chuyển đến tệp PatientListViewModel.kt rồi tìm hàm có tên là searchPatientsByName. Chúng ta sẽ thêm mã vào hàm này.

Để lọc kết quả dựa trên truy vấn tên đã cung cấp và phát kết quả để giao diện người dùng cập nhật, hãy kết hợp khối mã có điều kiện sau:

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

Ở đây, nếu nameQuery không trống, hàm tìm kiếm sẽ lọc kết quả để chỉ bao gồm những bệnh nhân có tên chứa cụm từ tìm kiếm được chỉ định.

Bước 2: Kiểm thử chức năng tìm kiếm mới

  1. Chạy lại ứng dụng:Sau khi thực hiện những thay đổi này, hãy tạo lại và chạy ứng dụng.
  2. Tìm kiếm bệnh nhân: Trên màn hình danh sách bệnh nhân, hãy sử dụng chức năng tìm kiếm. Giờ đây, bạn có thể nhập tên (hoặc một phần tên) để lọc danh sách bệnh nhân cho phù hợp.

Sau khi hoàn tất các bước này, bạn đã cải thiện ứng dụng của mình bằng cách cung cấp cho người dùng khả năng tìm kiếm bệnh nhân một cách hiệu quả theo tên của họ. Điều này có thể cải thiện đáng kể trải nghiệm người dùng và hiệu quả trong việc truy xuất dữ liệu.

8. Xin chúc mừng!

Bạn đã sử dụng Thư viện FHIR Engine để quản lý các tài nguyên FHIR trong ứng dụng của mình:

  • Sử dụng Sync API để đồng bộ hoá các tài nguyên FHIR với một máy chủ FHIR
  • Sử dụng Data Access API để tạo, đọc, cập nhật và xoá các tài nguyên FHIR cục bộ
  • Sử dụng Search API để tìm kiếm các tài nguyên FHIR cục bộ

Nội dung đã đề cập

  • Cách thiết lập máy chủ HAPI FHIR cục bộ
  • Cách tải dữ liệu kiểm thử lên Máy chủ HAPI FHIR cục bộ
  • Cách tạo ứng dụng Android bằng Thư viện FHIR Engine
  • Cách sử dụng Sync API, Data Access API và Search API trong Thư viện FHIR Engine

Các bước tiếp theo

  • Khám phá tài liệu về Thư viện FHIR Engine
  • Khám phá các tính năng nâng cao của Search API
  • Áp dụng Thư viện FHIR Engine trong ứng dụng Android của riêng bạn

Tìm hiểu thêm