إدارة موارد FHIR باستخدام مكتبة محرك FHIR

1. قبل البدء

ما ستُنشئه

في هذا الدرس التطبيقي حول الترميز، ستُنشئ تطبيق Android باستخدام مكتبة FHIR Engine Library. سيستخدم تطبيقك مكتبة FHIR Engine لتنزيل موارد 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. سنحمّل بيانات اختبارية لثلاثة مرضى إلى خادم 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 كنتيجة بحث بعدد total.بيانات الاختبار على الخادم

3- إعداد تطبيق Android

تنزيل الرمز

لتنزيل رمز هذا الدليل التعليمي، يمكنك استنساخ مستودع حزمة تطوير البرامج لأجهزة Android لمعيار FHIR: git clone https://github.com/google/android-fhir.git

يمكن العثور على المشروع الأوّلي لهذا الدرس التطبيقي حول الترميز في codelabs/engine.

استيراد التطبيق إلى Android Studio

سنبدأ باستيراد التطبيق المبدئي إلى Android Studio.

افتح Android Studio، واختَر Import Project (استيراد المشروع) (Gradle أو Eclipse ADT أو غير ذلك)، ثم اختَر مجلد codelabs/engine/ من رمز المصدر الذي نزّلته سابقًا.

شاشة بدء "استوديو Android"

مزامنة مشروعك مع ملفات Gradle

لتسهيل الأمر عليك، سبق أن تمت إضافة تبعيات مكتبة FHIR Engine إلى المشروع. يتيح لك ذلك دمج مكتبة FHIR Engine في تطبيقك. راقِب السطور التالية حتى نهاية ملف app/build.gradle.kts في مشروعك:

dependencies {
    // ...

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

للتأكّد من توفّر جميع التبعيات لتطبيقك، عليك مزامنة مشروعك مع ملفات Gradle في هذه المرحلة.

اختَر مزامنة المشروع مع ملفات Gradle (زر مزامنة Gradle) من شريط أدوات "استوديو Android". يمكنك أيضًا تشغيل التطبيق مرة أخرى للتأكّد من عمل التبعيات بشكل صحيح.

تشغيل التطبيق النموذجي

بعد استيراد المشروع إلى "استوديو Android"، تكون جاهزًا لتشغيل التطبيق للمرة الأولى.

ابدأ محاكي 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 محجوز خصيصًا للمضيف المحلي، ويمكن الوصول إليه من محاكي 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 البعيد باستخدام عملية في الخلفية.
    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 الذي حدّدناه سابقًا. بعد ذلك، سيتم تعديل واجهة المستخدم استنادًا إلى حالة عملية المزامنة.
  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())
    
    يطلق هذا الرمز coroutine ضمن نطاق 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 Engine Library لإدارة موارد 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 الخاص بك

مزيد من المعلومات