1. قبل البدء
ما ستُنشئه
في هذا الدرس التطبيقي حول الترميز، ستُنشئ تطبيق Android باستخدام مكتبة FHIR Engine Library. سيستخدم تطبيقك مكتبة FHIR Engine لتنزيل موارد FHIR من خادم FHIR وتحميل أي تغييرات محلية إلى الخادم.
المُعطيات
- كيفية إنشاء خادم HAPI FHIR محلي باستخدام Docker
- كيفية دمج مكتبة FHIR Engine في تطبيق Android
- كيفية استخدام Sync API لإعداد مهمة لمرة واحدة أو بشكل دوري لتنزيل موارد FHIR وتحميلها
- كيفية استخدام Search API
- كيفية استخدام واجهات برمجة التطبيقات Data Access API لإنشاء موارد FHIR وقراءتها وتعديلها وحذفها على الجهاز
المتطلبات
- Docker (الحصول على Docker)
- إصدار حديث من "استوديو Android" (الإصدار 4.1.2 أو إصدار أحدث)
- محاكي Android أو جهاز 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
تنزيل الرمز
لتنزيل رمز هذا الدليل التعليمي، يمكنك استنساخ مستودع حزمة تطوير البرامج لأجهزة 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/
من رمز المصدر الذي نزّلته سابقًا.
مزامنة مشروعك مع ملفات Gradle
لتسهيل الأمر عليك، سبق أن تمت إضافة تبعيات مكتبة FHIR Engine إلى المشروع. يتيح لك ذلك دمج مكتبة FHIR Engine في تطبيقك. راقِب السطور التالية حتى نهاية ملف app/build.gradle.kts
في مشروعك:
dependencies {
// ...
implementation("com.google.android.fhir:engine:1.1.0")
}
للتأكّد من توفّر جميع التبعيات لتطبيقك، عليك مزامنة مشروعك مع ملفات Gradle في هذه المرحلة.
اختَر مزامنة المشروع مع ملفات Gradle () من شريط أدوات "استوديو Android". يمكنك أيضًا تشغيل التطبيق مرة أخرى للتأكّد من عمل التبعيات بشكل صحيح.
تشغيل التطبيق النموذجي
بعد استيراد المشروع إلى "استوديو Android"، تكون جاهزًا لتشغيل التطبيق للمرة الأولى.
ابدأ محاكي Android Studio، ثم انقر على رمز التشغيل () في شريط أدوات Android Studio.
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. إنّ عنوان IP المقدَّم10.0.2.2
محجوز خصيصًا للمضيف المحلي، ويمكن الوصول إليه من محاكي Android. مزيد من المعلومات
- في فئة
FhirApplication
، أضِف السطر التالي لإنشاء مثيل محرك FHIR بشكل كسول: يضمن ذلك عدم إنشاء مثيل 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:ابدأ بالحصول على مرجع إلى محرّك FHIR في
PatientListViewModel.kt
. يطلق هذا الرمز coroutine ضمن نطاق 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 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 الخاص بك