1. قبل البدء
ما ستنشئه
في هذا الدرس التطبيقي حول الترميز، ستنشئ تطبيق Android باستخدام مكتبة FHIR Engine. سيستخدم تطبيقك مكتبة FHIR Engine لتنزيل موارد FHIR من خادم FHIR، وتحميل أي تغييرات محلية إلى الخادم.
أهداف الدورة التعليمية
- كيفية إنشاء خادم HAPI FHIR محلي باستخدام Docker
- كيفية دمج مكتبة FHIR Engine في تطبيق Android
- كيفية استخدام Sync API لإعداد مهمة لمرة واحدة أو بشكل دوري لتنزيل موارد FHIR وتحميلها
- كيفية استخدام Search API
- كيفية استخدام واجهات برمجة التطبيقات Data Access لإنشاء موارد FHIR وقراءتها وتعديلها وحذفها محليًا
المتطلبات
- Docker (تنزيل Docker)
- إصدار حديث من استوديو Android (الإصدار 4.1.2 أو إصدار أحدث)
- محاكي Android أو جهاز Android فعلي يعمل بالإصدار 7.0 Nougat أو إصدار أحدث
- الرمز النموذجي
- معرفة أساسية بتطوير تطبيقات Android باستخدام لغة Kotlin
إذا لم يسبق لك إنشاء تطبيقات Android، يمكنك البدء بإنشاء تطبيقك الأول.
2. إعداد خادم HAPI FHIR محلي باستخدام بيانات اختبار
HAPI FHIR هو خادم FHIR مفتوح المصدر وشائع الاستخدام. نستخدم خادم HAPI FHIR محليًا في درس تطبيقي حول الترميز ليتصل به تطبيق Android.
إعداد خادم HAPI FHIR المحلّي
- نفِّذ الأمر التالي في الوحدة الطرفية للحصول على أحدث صورة من HAPI FHIR
docker pull hapiproject/hapi:latest - أنشئ حاوية HAPI FHIR إما باستخدام Docker Desktop لتشغيل الصورة التي تم تنزيلها سابقًا
hapiproject/hapi، أو بتنفيذ الأمر التالي مزيد من المعلوماتdocker run -p 8080:8080 hapiproject/hapi:latest - افحص الخادم من خلال فتح عنوان URL
http://localhost:8080/في المتصفّح. من المفترض أن تظهر لك واجهة الويب HAPI FHIR.
ملء خادم HAPI FHIR المحلي ببيانات الاختبار
لاختبار تطبيقنا، سنحتاج إلى بعض بيانات الاختبار على الخادم. سنستخدم بيانات اصطناعية من إنشاء Synthea.
- أولاً، علينا تنزيل بيانات نموذجية من synthea-samples. نزِّل الملف
synthea_sample_data_fhir_r4_sep2019.zipواستخرِجه. تحتوي بيانات العيّنة غير المضغوطة على ملفات.jsonعديدة، كلّ منها عبارة عن حزمة معاملات لمريض فردي. - سنحمّل بيانات اختبارية لثلاثة مرضى إلى خادم 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/ - لتحميل بيانات الاختبار لجميع المرضى إلى الخادم، شغِّل
ومع ذلك، قد يستغرق إكمال هذه العملية وقتًا طويلاً وهي غير ضرورية في هذا الدرس العملي.for f in *.json; do curl -X POST -H "Content-Type: application/json" -d @$f http://localhost:8080/fhir/ ; done - تأكَّد من توفّر بيانات الاختبار على الخادم من خلال فتح عنوان URL
http://localhost:8080/fhir/Patient/في المتصفّح. من المفترض أن يظهر لك النصHTTP 200 OKوقسمResponse Bodyمن الصفحة التي تحتوي على بيانات المرضى في حزمة FHIR كنتيجة البحث مع عددtotal.
3- إعداد تطبيق Android
تنزيل الرمز
لتنزيل الرمز البرمجي الخاص بهذا الدرس التطبيقي حول الترميز، أنشئ نسخة طبق الأصل من مستودع حزمة تطوير البرامج (SDK) لنظام التشغيل Android FHIR: git clone https://github.com/google/android-fhir.git
يقع مشروع البداية الخاص بهذا الدرس العملي حول الترميز في codelabs/engine.
استيراد التطبيق إلى استوديو Android
نبدأ باستيراد تطبيق أوّلي إلى استوديو Android.
افتح "استوديو Android"، وانقر على استيراد مشروع (Gradle وEclipse ADT وما إلى ذلك)، ثم اختَر مجلد codelabs/engine/ من الرمز المصدري الذي نزّلته سابقًا.

مزامنة مشروعك مع ملفات Gradle
لتسهيل الأمر عليك، تمت إضافة تبعيات مكتبة FHIR Engine إلى المشروع. يتيح لك ذلك دمج مكتبة FHIR Engine في تطبيقك. راجِع الأسطر التالية في نهاية ملف app/build.gradle.kts الخاص بمشروعك:
dependencies {
// ...
implementation("com.google.android.fhir:engine:1.1.0")
}
للتأكّد من توفّر جميع التبعيات لتطبيقك، عليك مزامنة مشروعك مع ملفات Gradle في هذه المرحلة.
انقر على مزامنة المشروع مع ملفات Gradle (
) من شريط أدوات "استوديو Android". يمكنك أيضًا تشغيل التطبيق مرة أخرى للتأكّد من أنّ التبعيات تعمل بشكل صحيح.
تشغيل التطبيق النموذجي
بعد استيراد المشروع إلى "استوديو Android"، يمكنك تشغيل التطبيق للمرة الأولى.
ابدأ تشغيل محاكي "استوديو Android"، ثم انقر على "تشغيل" (
) في شريط أدوات "استوديو Android".

4. إنشاء مثيل FHIR Engine
لدمج FHIR Engine في تطبيق Android، عليك استخدام مكتبة FHIR Engine وإنشاء مثيل من FHIR Engine. ستساعدك الخطوات الموضّحة أدناه في إكمال العملية.
- انتقِل إلى فئة التطبيق، وهي
FhirApplication.ktفي هذا المثال، والموجودة فيapp/src/main/java/com/google/android/fhir/codelabs/engine. - داخل طريقة
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. عنوان IP10.0.2.2المقدَّم محجوز خصيصًا للمضيف المحلي، ويمكن الوصول إليه من محاكي Android. مزيد من المعلومات
- في فئة
FhirApplication، أضِف السطر التالي لإنشاء مثيل FHIR Engine بشكل غير مباشر: يضمن ذلك عدم إنشاء مثيل FhirEngine إلا عند الوصول إليه للمرة الأولى، وليس فور بدء تشغيل التطبيق.private val fhirEngine: FhirEngine by lazy { FhirEngineProvider.getInstance(this) } - أضِف طريقة المساعدة التالية في الفئة
FhirApplicationلتسهيل الوصول إليها في جميع أنحاء تطبيقك: تتيح لك هذه الطريقة الثابتة استرداد مثيل FHIR Engine من أي مكان في التطبيق باستخدام السياق.companion object { fun fhirEngine(context: Context) = (context.applicationContext as FhirApplication).fhirEngine }
5. مزامنة البيانات مع خادم FHIR
- أنشئ صفًا جديدًا
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 } } - إنشاء فئة جديدة
AppFhirSyncWorker.ktتحدّد هذه الفئة كيفية مزامنة التطبيق مع خادم FHIR البعيد باستخدام عملية تعمل في الخلفية. في هذا القسم، حدّدنا مدير التنزيل وحل التعارض ومثيل محرّك 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, ) } - في ViewModel،
PatientListViewModel.kt، عليك إعداد آلية مزامنة لمرة واحدة. ابحث عن الرمز التالي وأضِفه إلى الدالةtriggerOneTimeSync(): تبدأ روتين الإجراءات الفرعي هذا عملية مزامنة لمرة واحدة مع خادم FHIR باستخدام AppFhirSyncWorker الذي حدّدناه سابقًا. سيتم بعد ذلك تعديل واجهة المستخدِم استنادًا إلى حالة عملية المزامنة.viewModelScope.launch { Sync.oneTimeSync<AppFhirSyncWorker>(getApplication()) .shareIn(this, SharingStarted.Eagerly, 10) .collect { _pollState.emit(it) } } - في ملف
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
- الوصول إلى FHIR Engine:ابدأ بالحصول على مرجع إلى FHIR Engine في
PatientListViewModel.kt. يُطلق هذا الرمز روتينًا فرعيًا ضمن نطاق ViewModel ويُهيّئ محرّك FHIR.viewModelScope.launch { val fhirEngine = FhirApplication.fhirEngine(getApplication()) - البحث عن مرضى من ويكفيلد:استخدِم محرّك FHIR للبحث عن مرضى لديهم مدينة عنوان
Wakefield. في هذا المثال، نستخدم طريقةval patientsFromWakefield = fhirEngine.search<Patient> { filter( Patient.ADDRESS_CITY, { modifier = StringFilterModifier.MATCHES_EXACTLY value = "Wakefield" } ) }searchفي محرّك FHIR لفلترة المرضى استنادًا إلى مدينة عنوانهم. ستكون النتيجة قائمة بالمرضى من ويكفيلد. - البحث عن مرضى من مدينة "تونتون":يمكنك أيضًا البحث عن مرضى عنوان مدينتهم هو
Taunton. لدينا الآن قائمتان بالمرضى، إحداهما من ويكفيلد والأخرى من تونتون.val patientsFromTaunton = fhirEngine.search<Patient> { filter( Patient.ADDRESS_CITY, { modifier = StringFilterModifier.MATCHES_EXACTLY value = "Taunton" } ) } - تعديل عناوين المرضى:راجِع كل مريض في قائمة
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) } - بدء المزامنة:بعد تعديل البيانات على الجهاز، ابدأ عملية مزامنة لمرة واحدة لضمان تعديل البيانات على خادم FHIR.
تشير علامة الإغلاقtriggerOneTimeSync() }}إلى نهاية الروتين الفرعي الذي تم تشغيله في البداية.
الخطوة 2: اختبار الوظيفة
- اختبار واجهة المستخدم:شغِّل تطبيقك. انقر على الزر
Updateفي القائمة. من المفترض أن يظهر لك أنّه تم تبديل مدن العناوين للمريضَينAaron697وAbby752. - التحقّق من الخادم:افتح متصفّحًا وانتقِل إلى
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: اختبار وظيفة البحث الجديدة
- إعادة تشغيل التطبيق:بعد إجراء هذه التغييرات، أعِد إنشاء تطبيقك وشغِّله.
- البحث عن المرضى: في شاشة قائمة المرضى، استخدِم وظيفة البحث. من المفترض أن تتمكّن الآن من إدخال اسم (أو جزء من اسم) لفلترة قائمة المرضى وفقًا لذلك.
بعد إكمال هذه الخطوات، تكون قد حسّنت تطبيقك من خلال منح المستخدمين القدرة على البحث عن المرضى بكفاءة حسب أسمائهم. يمكن أن يؤدي ذلك إلى تحسين تجربة المستخدم وكفاءة استرجاع البيانات بشكل كبير.
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 Library
- استكشاف الميزات المتقدّمة لواجهة برمجة التطبيقات Search API
- تطبيق FHIR Engine Library في تطبيق Android الخاص بك