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

1. قبل البدء

ما الذي ستقوم ببنائه

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

تنزيل الرمز

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

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

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

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

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

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

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

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

dependencies {
    // ...

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

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

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

تشغيل تطبيق إجراء التفعيل

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

ابدأ تشغيل محاكي "استوديو Android"، ثم انقر على "تشغيل" (زر التشغيل) في شريط أدوات "استوديو Android".

تطبيق Hello World

4. إنشاء مثيل FHIR Engine

لدمج FHIR Engine في تطبيق Android، ستحتاج إلى استخدام مكتبة FHIR Engine وبدء نسخة من محرك FHIR. سترشدك الخطوات الموضحة أدناه خلال هذه العملية.

  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 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)
    }
    
    نحدد هنا تطبيق إدارة التنزيل وأداة حل التعارضات ومثيل محرك 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())
    
    تقوم هذه التعليمات البرمجية بتشغيل الكوروتين داخل نطاق View Model وإعداد محرك FHIR.
  2. البحث عن مرضى من Wakefield:يمكنك استخدام محرك 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: اختبار وظائف "بحث Google" الجديدة

  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

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