จัดการทรัพยากร FHIR โดยใช้ไลบรารี FHIR Engine

1. ก่อนเริ่มต้น

สิ่งที่คุณจะสร้าง

ในโค้ดแล็บนี้ คุณจะได้สร้างแอป Android โดยใช้ไลบรารี FHIR Engine แอปของคุณจะใช้ FHIR Engine Library เพื่อดาวน์โหลดทรัพยากร FHIR จากเซิร์ฟเวอร์ FHIR และอัปโหลดการเปลี่ยนแปลงในเครื่องไปยังเซิร์ฟเวอร์

สิ่งที่คุณจะได้เรียนรู้

  • วิธีสร้างเซิร์ฟเวอร์ HAPI FHIR ในพื้นที่โดยใช้ Docker
  • วิธีผสานรวมไลบรารี FHIR Engine เข้ากับแอปพลิเคชัน Android
  • วิธีใช้ Sync API เพื่อตั้งค่างานแบบครั้งเดียวหรือเป็นระยะเพื่อดาวน์โหลดและอัปโหลดทรัพยากร FHIR
  • วิธีใช้ Search API
  • วิธีใช้ Data Access API เพื่อสร้าง อ่าน อัปเดต และลบทรัพยากร FHIR ในเครื่อง

สิ่งที่คุณต้องมี

  • Docker (รับ Docker)
  • Android Studio (v4.1.2 ขึ้นไป) เวอร์ชันล่าสุด
  • Android Emulator หรืออุปกรณ์ Android ที่ใช้ Android 7.0 Nougat ขึ้นไป
  • โค้ดตัวอย่าง
  • ความรู้พื้นฐานเกี่ยวกับการพัฒนาแอป Android ใน Kotlin

หากไม่เคยสร้างแอป Android มาก่อน ให้เริ่มต้นด้วยการสร้างแอปแรก

2. ตั้งค่าเซิร์ฟเวอร์ HAPI FHIR ในพื้นที่ด้วยข้อมูลทดสอบ

HAPI FHIR เป็นเซิร์ฟเวอร์ FHIR แบบโอเพนซอร์สที่ได้รับความนิยม เราใช้เซิร์ฟเวอร์ HAPI FHIR ในพื้นที่ในโค้ดแล็บเพื่อให้แอป 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

ป้อนข้อมูลทดสอบลงในเซิร์ฟเวอร์ HAPI FHIR ในพื้นที่

เราต้องใช้ข้อมูลทดสอบบางอย่างในเซิร์ฟเวอร์เพื่อทดสอบแอปพลิเคชัน เราจะใช้ข้อมูลสังเคราะห์ที่ Synthea สร้างขึ้น

  1. ก่อนอื่น เราต้องดาวน์โหลดข้อมูลตัวอย่างจาก synthea-samples ดาวน์โหลดและแตกไฟล์ synthea_sample_data_fhir_r4_sep2019.zip ข้อมูลตัวอย่างที่แตกไฟล์จะมีไฟล์ .json จำนวนมาก โดยแต่ละไฟล์จะเป็นกลุ่มธุรกรรมสำหรับผู้ป่วยแต่ละราย
  2. เราจะอัปโหลดข้อมูลทดสอบสำหรับผู้ป่วย 3 รายไปยังเซิร์ฟเวอร์ 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 Bundle เป็นผลการค้นหาที่มีจำนวน totalทดสอบข้อมูลในเซิร์ฟเวอร์

3. ตั้งค่าแอป Android

ดาวน์โหลดรหัส

หากต้องการดาวน์โหลดโค้ดสําหรับโค้ดแล็บนี้ ให้โคลนที่เก็บ Android FHIR SDK: git clone https://github.com/google/android-fhir.git

โปรเจ็กต์เริ่มต้นสําหรับ Codelab นี้อยู่ใน codelabs/engine

นําเข้าแอปไปยัง Android Studio

เราเริ่มต้นด้วยการนําเข้าแอปเริ่มต้นไปยัง Android Studio

เปิด Android Studio เลือกนําเข้าโปรเจ็กต์ (Gradle, Eclipse ADT ฯลฯ) แล้วเลือกโฟลเดอร์ codelabs/engine/ จากซอร์สโค้ดที่คุณดาวน์โหลดไว้ก่อนหน้านี้

หน้าจอเริ่มต้นของ Android Studio

ซิงค์โปรเจ็กต์กับไฟล์ Gradle

เราได้เพิ่มไลบรารี FHIR Engine ลงในโปรเจ็กต์แล้วเพื่อความสะดวก ซึ่งจะช่วยให้คุณผสานรวมไลบรารี FHIR Engine ในแอปได้ โปรดดูบรรทัดต่อไปนี้จนถึงส่วนท้ายของไฟล์ app/build.gradle.kts ของโปรเจ็กต์

dependencies {
    // ...

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

คุณควรซิงค์โปรเจ็กต์กับไฟล์ Gradle ในตอนนี้เพื่อให้แน่ใจว่าแอปของคุณจะใช้ Dependency ทั้งหมดได้

เลือกซิงค์โปรเจ็กต์กับไฟล์ Gradle (ปุ่มซิงค์ Gradle) จากแถบเครื่องมือของ Android Studio นอกจากนี้ คุณยังเรียกใช้แอปอีกครั้งเพื่อตรวจสอบว่า Dependency ทํางานอย่างถูกต้องหรือไม่

เรียกใช้แอปเริ่มต้น

เมื่อนําเข้าโปรเจ็กต์ไปยัง Android Studio แล้ว คุณก็พร้อมเรียกใช้แอปเป็นครั้งแรก

เริ่มโปรแกรมจำลอง Android Studio แล้วคลิก "เรียกใช้" (ปุ่มเรียกใช้) ในแถบเครื่องมือของ Android Studio

แอป Hello World

4. สร้างอินสแตนซ์ FHIR Engine

หากต้องการรวม FHIR Engine ไว้ในแอป Android คุณจะต้องใช้ไลบรารี FHIR Engine และเริ่มต้นอินสแตนซ์ของ FHIR Engine ขั้นตอนที่ระบุไว้ด้านล่างจะช่วยแนะนำคุณตลอดกระบวนการ

  1. ไปที่คลาสแอปพลิเคชัน ซึ่งในตัวอย่างนี้คือ 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 ที่เรากําหนดไว้ก่อนหน้านี้ จากนั้นระบบจะอัปเดต UI ตามสถานะของกระบวนการซิงค์
  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 Engine:เริ่มต้นด้วยการอ้างอิง FHIR Engine ใน PatientListViewModel.kt
    viewModelScope.launch {
       val fhirEngine = FhirApplication.fhirEngine(getApplication())
    
    โค้ดนี้จะเปิด coroutine ภายในขอบเขตของ ViewModel และเริ่มต้นการทำงานของเครื่องมือ FHIR
  2. ค้นหาผู้ป่วยจาก Wakefield:ใช้เครื่องมือ FHIR เพื่อค้นหาผู้ป่วยที่มีเมืองในที่อยู่เป็น Wakefield
    val patientsFromWakefield =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Wakefield"
             }
           )
         }
    
    ในส่วนนี้ เราใช้เมธอด search ของเครื่องมือ FHIR เพื่อกรองผู้ป่วยตามเมืองที่อยู่ ผลลัพธ์ที่ได้จะเป็นรายการผู้ป่วยจากเวย์คอล์ด
  3. ค้นหาผู้ป่วยจาก Taunton:ในทํานองเดียวกัน ให้ค้นหาผู้ป่วยที่มีเมืองในที่อยู่เป็น Taunton
    val patientsFromTaunton =
         fhirEngine.search<Patient> {
           filter(
             Patient.ADDRESS_CITY,
             {
               modifier =  StringFilterModifier.MATCHES_EXACTLY
               value = "Taunton"
             }
           )
         }
    
    ตอนนี้เรามีรายชื่อผู้ป่วย 2 รายการ รายการหนึ่งจาก Wakefield และอีกรายการหนึ่งจาก 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. การทดสอบ UI:เรียกใช้แอป คลิกปุ่ม Update ในเมนู คุณควรเห็นเมืองที่อยู่ของผู้ป่วย Aaron697 และ Abby752 สลับที่กัน
  2. การยืนยันเซิร์ฟเวอร์:เปิดเบราว์เซอร์แล้วไปที่ http://localhost:8080/fhir/Patient/ ยืนยันว่าเมืองในที่อยู่ของผู้ป่วย Aaron697 และ Abby752 ได้รับการอัปเดตในเซิร์ฟเวอร์ FHIR ในพื้นที่แล้ว

เมื่อทำตามขั้นตอนเหล่านี้ แสดงว่าคุณได้ติดตั้งใช้งานกลไกในการแก้ไขข้อมูลผู้ป่วยและซิงค์การเปลี่ยนแปลงกับเซิร์ฟเวอร์ FHIR เรียบร้อยแล้ว

7. ค้นหาผู้ป่วยตามชื่อ

การค้นหาผู้ป่วยด้วยชื่อเป็นวิธีที่เรียกข้อมูลได้แบบใช้งานง่าย เราจะอธิบายขั้นตอนการใช้งานฟีเจอร์นี้ในแอปพลิเคชันของคุณ

ขั้นตอนที่ 1: อัปเดตลายเซ็นฟังก์ชัน

ไปที่ไฟล์ PatientListViewModel.kt แล้วค้นหาฟังก์ชันชื่อ searchPatientsByName เราจะเพิ่มโค้ดลงในฟังก์ชันนี้

หากต้องการกรองผลลัพธ์ตามการค้นหาชื่อที่ระบุ และแสดงผลลัพธ์เพื่อให้ UI อัปเดต ให้รวมบล็อกโค้ดแบบมีเงื่อนไขต่อไปนี้

    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 เพื่อจัดการทรัพยากร FHIR ในแอป

  • ใช้ Sync API เพื่อซิงค์ทรัพยากร FHIR กับเซิร์ฟเวอร์ FHIR
  • ใช้ Data Access API เพื่อสร้าง อ่าน อัปเดต และลบทรัพยากร FHIR ในพื้นที่
  • ใช้ Search API เพื่อค้นหาทรัพยากร FHIR ในพื้นที่

สิ่งที่เราได้พูดถึง

  • วิธีตั้งค่าเซิร์ฟเวอร์ HAPI FHIR ในพื้นที่
  • วิธีอัปโหลดข้อมูลทดสอบไปยังเซิร์ฟเวอร์ HAPI FHIR ในพื้นที่
  • วิธีสร้างแอป Android โดยใช้ FHIR Engine Library
  • วิธีใช้ Sync API, Data Access API และ Search API ในไลบรารี FHIR Engine

ขั้นตอนถัดไป

  • สำรวจเอกสารประกอบสำหรับไลบรารี FHIR Engine
  • สำรวจฟีเจอร์ขั้นสูงของ Search API
  • ใช้ FHIR Engine Library ในแอป Android ของคุณเอง

ดูข้อมูลเพิ่มเติม