ניהול משאבי FHIR באמצעות FHIR Engine Library

1. לפני שמתחילים

מה תפַתחו

ב-Codelab הזה בונים אפליקציה ל-Android באמצעות ספריית המנוע של FHIR. האפליקציה שלך תשתמש בספריית FHIR Engine כדי להוריד משאבי FHIR משרת FHIR ולהעלות את השינויים המקומיים לשרת.

מה תלמדו

  • איך יוצרים שרת HAPI FHIR מקומי באמצעות Docker
  • איך לשלב את FHIR Engine Library באפליקציה ל-Android
  • איך להשתמש ב-Sync API כדי להגדיר משימה חד-פעמית או תקופתית כדי להוריד ולהעלות משאבי FHIR
  • איך משתמשים ב-Search API
  • איך משתמשים בממשקי Data Access API כדי ליצור, לקרוא, לעדכן ולמחוק משאבי FHIR באופן מקומי

למה תזדקק?

אם עוד לא יצרתם אפליקציות ל-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. מורידים ומחלצים את 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
    
    עם זאת, התהליך עשוי להימשך זמן רב והוא לא הכרחי ב-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

לנוחותך, כבר נוספו לפרויקט יחסי התלות של ספריית FHIR Engine. כך ניתן לשלב את ספריית המנוע של FHIR באפליקציה שלך. בודקים את השורות הבאות בסוף הקובץ app/build.gradle.kts של הפרויקט:

dependencies {
    // ...

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

כדי לוודא שכל יחסי התלות זמינים לאפליקציה שלכם, צריך לסנכרן את הפרויקט עם קובצי GRid בשלב הזה.

בסרגל הכלים של Android Studio, בוחרים באפשרות Sync Project with Gradle Files (לחצן לסנכרון של Gradle). צריך גם להריץ את האפליקציה שוב כדי לבדוק שיחסי התלות פועלים כראוי.

הפעלת האפליקציה לתחילת פעולה

אחרי שייבאת את הפרויקט אל 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. בתוך method 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:
      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)
    }
    
    כאן הגדרנו באיזה מנהל הורדות, מפענח התנגשויות ומופע של מנוע 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 כדי לנהל משאבי FHIR באפליקציה שלך:

  • שימוש ב-Sync API לסנכרון משאבי FHIR עם שרת FHIR
  • אפשר להשתמש ב-Data Access API כדי ליצור, לקרוא, לעדכן ולמחוק משאבי FHIR מקומיים
  • שימוש ב-Search API לחיפוש משאבי FHIR מקומיים

הנושאים שטיפלנו בהם

  • איך להגדיר שרת HAPI FHIR מקומי
  • איך להעלות את נתוני הבדיקה לשרת HAPI FHIR המקומי
  • איך בונים אפליקציה ל-Android באמצעות ספריית המנוע של FHIR
  • איך להשתמש ב-Sync API, ב-Data Access API וב-Search API בספריית המנוע של FHIR

השלבים הבאים

  • מסמכי התיעוד של ספריית המנוע של FHIR
  • כדאי להכיר את התכונות המתקדמות של Search API
  • החלת ספריית FHIR Engine באפליקציה שלכם ל-Android

מידע נוסף