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

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

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

ใน Codelab นี้ คุณจะได้สร้างแอป Android โดยใช้ไลบรารี FHIR Engine แอปจะใช้ไลบรารี FHIR Engine เพื่อดาวน์โหลดทรัพยากร 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 หรืออุปกรณ์ Android จริงที่ใช้ Android 7.0 Nougat ขึ้นไป
  • โค้ดตัวอย่าง
  • ความรู้พื้นฐานเกี่ยวกับการพัฒนา Android ใน Kotlin

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

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

HAPI FHIR เป็นเซิร์ฟเวอร์ FHIR แบบโอเพนซอร์สที่ได้รับความนิยม เราใช้เซิร์ฟเวอร์ HAPI FHIR ในเครื่องใน Codelab สำหรับแอป 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_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
    
    อย่างไรก็ตาม การดำเนินการนี้อาจใช้เวลานานและไม่จำเป็นสำหรับ Codelab
  4. โปรดยืนยันว่ามีข้อมูลทดสอบอยู่ในเซิร์ฟเวอร์โดยเปิด URL http://localhost:8080/fhir/Patient/ ในเบราว์เซอร์ คุณควรเห็นข้อความ HTTP 200 OK และส่วน Response Body ของหน้าที่มีข้อมูลผู้ป่วยในแพ็กเกจ FHIR เป็นผลการค้นหาพร้อมจำนวน totalทดสอบข้อมูลบนเซิร์ฟเวอร์

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

ดาวน์โหลดโค้ด

หากต้องการดาวน์โหลดโค้ดสำหรับ Codelab นี้ ให้โคลนที่เก็บ 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

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

dependencies {
    // ...

    implementation("com.google.android.fhir:engine:0.1.0-beta05")
}

คุณควรซิงค์โปรเจ็กต์กับไฟล์ 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 แบบ Lazily
      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)
    }
    
    ในส่วนนี้ เราได้กำหนดไว้ว่า Download Manager, ตัวแก้ไขข้อขัดแย้ง และอินสแตนซ์ของเครื่องมือ 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:เริ่มต้นด้วยการรับการอ้างอิงเครื่องมือ 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"
             }
           )
         }
    
    ตอนนี้เรามีรายชื่อผู้ป่วย 2 ราย คือ ชื่อคนไข้จากเวกฟิลด์ ส่วนอีกคนมาจากทอนตัน
  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
  • วิธีใช้ Sync API, Data Access API และ Search API ในไลบรารี FHIR Engine

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

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

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