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

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

หากยังไม่เคยสร้างแอป 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
    
    อย่างไรก็ตาม การดำเนินการนี้อาจใช้เวลานานและไม่จำเป็นสำหรับ Codelab
  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

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

dependencies {
    // ...

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

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

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

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

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

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

แอป Hello World

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

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

  1. ไปที่คลาสแอปพลิเคชัน ซึ่งในตัวอย่างนี้คือ FhirApplication.kt อยู่ใน app/src/main/java/com/google/android/fhir/codelabs/engine
  2. เพิ่มโค้ดต่อไปนี้เพื่อเริ่มต้น FHIR Engine ภายในเมธอด onCreate()
      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 ระยะไกลโดยใช้ Worker ในเบื้องหลัง
    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 Engine ที่จะใช้สำหรับการซิงค์
  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())
    
    โค้ดนี้จะเปิดใช้โครูทีนภายในขอบเขตของ 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 รายการ รายการหนึ่งมาจากเวกฟิลด์และอีกรายการมาจากทอนตัน
  4. แก้ไขที่อยู่ของผู้ป่วย:ไปที่ผู้ป่วยแต่ละรายในรายการ patientsFromWakefield เปลี่ยนเมืองของผู้ป่วยเป็น Taunton และอัปเดตใน FHIR Engine
    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 Library

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

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

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