1. Avant de commencer
Ce que vous allez faire
Dans cet atelier de programmation, vous allez créer une application Android à l'aide de la bibliothèque de moteur FHIR. Votre application utilisera la bibliothèque FHIR Engine pour télécharger des ressources FHIR à partir d'un serveur FHIR et importer les modifications locales sur le serveur.
Points abordés
- Créer un serveur HAPI FHIR local à l'aide de Docker
- Intégrer la bibliothèque du moteur FHIR à votre application Android
- Utiliser l'API Sync pour configurer une tâche ponctuelle ou périodique de téléchargement et d'importation de ressources FHIR
- Utiliser l'API Search
- Utiliser les API Data Access pour créer, lire, mettre à jour et supprimer des ressources FHIR localement
Prérequis
- Docker (télécharger Docker)
- Une version récente d'Android Studio (version 4.1.2 ou ultérieure)
- Android Emulator ou un appareil Android physique équipé d'Android 7.0 Nougat ou version ultérieure
- Exemple de code
- Vous disposez de connaissances de base en développement Android en Kotlin.
Si vous n'avez jamais créé d'applications Android, vous pouvez commencer par créer votre première application.
2. Configurer un serveur FHIR HAPI local avec des données de test
HAPI FHIR est un serveur FHIR Open Source populaire. Dans notre atelier de programmation, nous utilisons un serveur HAPI FHIR local auquel l'application Android peut se connecter.
Configurer le serveur FHIR HAPI local
- Exécutez la commande suivante dans un terminal pour obtenir la dernière image de HAPI FHIR.
docker pull hapiproject/hapi:latest
- Créez un conteneur HAPI FHIR à l'aide de Docker Desktop pour exécuter l'image
hapiproject/hapi
précédemment téléchargée ou en exécutant la commande suivante : En savoir plusdocker run -p 8080:8080 hapiproject/hapi:latest
- Inspectez le serveur en ouvrant l'URL
http://localhost:8080/
dans un navigateur. L'interface Web HAPI FHIR devrait s'afficher.
Remplir le serveur HAPI FHIR local avec des données de test
Pour tester notre application, nous aurons besoin de données de test sur le serveur. Nous utiliserons des données synthétiques générées par Synthea.
- Tout d'abord, nous devons télécharger des exemples de données à partir de synthea-samples. Téléchargez et extrayez
synthea_sample_data_fhir_r4_sep2019.zip
. L'exemple de données non compressées contient de nombreux fichiers.json
, chacun étant un lot de transactions pour un patient donné. - Nous allons importer des données de test pour trois patients sur le serveur FHIR HAPI local. Exécutez la commande suivante dans le répertoire contenant les fichiers 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/
- Pour importer des données de test pour tous les patients sur le serveur, exécutez
Cependant, cette opération peut prendre beaucoup de temps et n'est pas nécessaire pour l'atelier de programmation.for f in *.json; do curl -X POST -H "Content-Type: application/json" -d @$f http://localhost:8080/fhir/ ; done
- Vérifiez que les données de test sont disponibles sur le serveur en ouvrant l'URL
http://localhost:8080/fhir/Patient/
dans un navigateur. Le texteHTTP 200 OK
et la sectionResponse Body
de la page contenant les données patient dans un bundle FHIR devraient s'afficher dans les résultats de recherche avec un nombretotal
.
3. Configurer l'application Android
Télécharger le code
Pour télécharger le code de cet atelier de programmation, clonez le dépôt du SDK Android FHIR: git clone https://github.com/google/android-fhir.git
Le projet de départ de cet atelier de programmation se trouve dans codelabs/engine
.
Importer l'application dans Android Studio
Nous commençons par importer l'application de démarrage dans Android Studio.
Ouvrez Android Studio, sélectionnez Import Project (Gradle, Eclipse ADT, etc.) (Importer un projet (Gradle, Eclipse ADT, etc.)), puis choisissez le dossier codelabs/engine/
dans le code source que vous avez téléchargé précédemment.
Synchroniser votre projet avec les fichiers Gradle
Pour votre commodité, les dépendances de la bibliothèque du moteur FHIR ont déjà été ajoutées au projet. Vous pouvez ainsi intégrer la bibliothèque du moteur FHIR à votre application. Observez les lignes suivantes jusqu'à la fin du fichier app/build.gradle.kts
de votre projet:
dependencies {
// ...
implementation("com.google.android.fhir:engine:1.1.0")
}
Pour vous assurer que toutes les dépendances sont disponibles pour votre application, vous devez synchroniser votre projet avec les fichiers Gradle à ce stade.
Sélectionnez Sync Project with Gradle Files (Synchroniser le projet avec les fichiers Gradle) dans la barre d'outils d'Android Studio. Vous pouvez également exécuter à nouveau l'application pour vérifier que les dépendances fonctionnent correctement.
Exécuter l'application de démarrage
Maintenant que vous avez importé le projet dans Android Studio, vous êtes prêt à exécuter l'application pour la première fois.
Démarrez l'émulateur Android Studio, puis cliquez sur "Run" (Exécuter) dans la barre d'outils d'Android Studio.
4. Créer une instance du moteur FHIR
Pour intégrer le moteur FHIR à votre application Android, vous devez utiliser la bibliothèque du moteur FHIR et lancer une instance du moteur FHIR. Les étapes décrites ci-dessous vous guideront tout au long du processus.
- Accédez à votre classe Application, qui dans cet exemple est
FhirApplication.kt
, située dansapp/src/main/java/com/google/android/fhir/codelabs/engine
. - Dans la méthode
onCreate()
, ajoutez le code suivant pour initialiser FHIR Engine: Remarques: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
: active le chiffrement des données si l'appareil le prend en charge.RECREATE_AT_OPEN
: détermine la stratégie d'erreur de la base de données. Dans ce cas, la base de données est recréée si une erreur se produit lors de l'ouverture.baseUrl
dansServerConfiguration
: il s'agit de l'URL de base du serveur FHIR. L'adresse IP10.0.2.2
fournie est spécialement réservée à localhost, accessible depuis l'émulateur Android. En savoir plus
- Dans la classe
FhirApplication
, ajoutez la ligne suivante pour instancier de manière paresseuse le moteur FHIR: Cela garantit que l'instance FhirEngine n'est créée que lorsqu'elle est consultée pour la première fois, et non immédiatement au démarrage de l'application.private val fhirEngine: FhirEngine by lazy { FhirEngineProvider.getInstance(this) }
- Ajoutez la méthode pratique suivante dans la classe
FhirApplication
pour faciliter l'accès dans l'ensemble de votre application: Cette méthode statique vous permet de récupérer l'instance du moteur FHIR à partir de n'importe quel endroit de l'application à l'aide du contexte.companion object { fun fhirEngine(context: Context) = (context.applicationContext as FhirApplication).fhirEngine }
5. Synchroniser des données avec un serveur FHIR
- Créez une classe
DownloadWorkManagerImpl.kt
. Dans cette classe, vous allez définir comment l'application récupère la ressource suivante de la liste à télécharger : Cette classe contient une file d'attente de types de ressources qu'elle souhaite télécharger. Il traite les réponses et extrait les ressources du bundle renvoyé, qui sont enregistrées dans la base de données locale.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 } }
- Créez une classe
AppFhirSyncWorker.kt
. Cette classe définit la façon dont l'application se synchronisera avec le serveur FHIR distant à l'aide d'un worker en arrière-plan. Ici, nous avons défini le gestionnaire de téléchargement, le gestionnaire de conflits et l'instance du moteur FHIR à utiliser pour la synchronisation.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, ) }
- Dans votre ViewModel,
PatientListViewModel.kt
, vous allez configurer un mécanisme de synchronisation unique. Recherchez et ajoutez le code suivant à la fonctiontriggerOneTimeSync()
: Cette coroutine lance une synchronisation ponctuelle avec le serveur FHIR à l'aide de l'AppFhirSyncWorker que nous avons défini précédemment. Elle met ensuite à jour l'UI en fonction de l'état du processus de synchronisation.viewModelScope.launch { Sync.oneTimeSync<AppFhirSyncWorker>(getApplication()) .shareIn(this, SharingStarted.Eagerly, 10) .collect { _pollState.emit(it) } }
- Dans le fichier
PatientListFragment.kt
, mettez à jour le corps de la fonctionhandleSyncJobStatus
: Ici, une fois le processus de synchronisation terminé, un message de type "toast" s'affiche pour en informer l'utilisateur. L'application affiche ensuite tous les patients en appelant une recherche avec un nom vide.when (syncJobStatus) { is SyncJobStatus.Finished -> { Toast.makeText(requireContext(), "Sync Finished", Toast.LENGTH_SHORT).show() viewModel.searchPatientsByName("") } else -> {} }
Maintenant que tout est configuré, exécutez votre application. Cliquez sur le bouton Sync
dans le menu. Si tout fonctionne correctement, les patients de votre serveur FHIR local devraient être téléchargés et affichés dans l'application.
6. Modifier et importer des données patient
Dans cette section, nous allons vous expliquer comment modifier les données patient en fonction de critères spécifiques et importer les données mises à jour sur votre serveur FHIR. Plus précisément, nous allons échanger les villes de résidence des patients résidant à Wakefield
et Taunton
.
Étape 1: Configurer la logique de modification dans PatientListViewModel
Le code de cette section est ajouté à la fonction triggerUpdate
dans PatientListViewModel
.
- Accéder au moteur FHIR:commencez par obtenir une référence au moteur FHIR dans le
PatientListViewModel.kt
. Ce code lance une coroutine dans le champ d'application du ViewModel et initialise le moteur FHIR.viewModelScope.launch { val fhirEngine = FhirApplication.fhirEngine(getApplication())
- Rechercher des patients de Wakefield:utilisez le moteur FHIR pour rechercher des patients dont la ville de résidence est
Wakefield
. Ici, nous utilisons la méthodeval patientsFromWakefield = fhirEngine.search<Patient> { filter( Patient.ADDRESS_CITY, { modifier = StringFilterModifier.MATCHES_EXACTLY value = "Wakefield" } ) }
search
du moteur FHIR pour filtrer les patients en fonction de leur ville de résidence. Le résultat est une liste de patients de Wakefield. - Rechercher des patients de Taunton:recherchez également les patients dont la ville d'adresse est
Taunton
. Nous avons maintenant deux listes de patients, l'une de Wakefield et l'autre de Taunton.val patientsFromTaunton = fhirEngine.search<Patient> { filter( Patient.ADDRESS_CITY, { modifier = StringFilterModifier.MATCHES_EXACTLY value = "Taunton" } ) }
- Modifier les adresses des patients:parcourez chaque patient de la liste
patientsFromWakefield
, remplacez sa ville parTaunton
et mettez-le à jour dans le moteur FHIR. De même, modifiez la ville de chaque patient de la listepatientsFromWakefield.forEach { it.resource.address.first().city = "Taunton" fhirEngine.update(it.resource) }
patientsFromTaunton
pour qu'elle soit remplacée parWakefield
.patientsFromTaunton.forEach { it.resource.address.first().city = "Wakefield" fhirEngine.update(it.resource) }
- Lancer la synchronisation:après avoir modifié les données localement, déclenchez une synchronisation ponctuelle pour vous assurer qu'elles sont mises à jour sur le serveur FHIR.
L'accolade fermantetriggerOneTimeSync() }
}
indique la fin de la coroutine lancée au début.
Étape 2: Tester la fonctionnalité
- Test de l'interface utilisateur:exécutez votre application. Cliquez sur le bouton
Update
dans le menu. Les villes d'adresse des patientsAaron697
etAbby752
devraient être interchangées. - Vérification du serveur:ouvrez un navigateur et accédez à
http://localhost:8080/fhir/Patient/
. Vérifiez que la ville de l'adresse des patientsAaron697
etAbby752
est mise à jour sur le serveur FHIR local.
En suivant ces étapes, vous avez implémenté un mécanisme permettant de modifier les données patient et de synchroniser les modifications avec votre serveur FHIR.
7. Rechercher des patients par nom
La recherche de patients par leur nom peut être un moyen convivial de récupérer des informations. Nous allons vous expliquer comment implémenter cette fonctionnalité dans votre application.
Étape 1: Mettre à jour la signature de fonction
Accédez à votre fichier PatientListViewModel.kt
et recherchez la fonction searchPatientsByName
. Nous allons y ajouter du code.
Pour filtrer les résultats en fonction de la requête de nom fournie et émettre les résultats à mettre à jour dans l'UI, incorporez le bloc de code conditionnel suivant:
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 }
}
}
Ici, si nameQuery
n'est pas vide, la fonction de recherche filtre les résultats pour n'inclure que les patients dont le nom contient la requête spécifiée.
Étape 2: Tester la nouvelle fonctionnalité de recherche
- Redémarrez l'application:après avoir effectué ces modifications, recréez et exécutez votre application.
- Rechercher des patients: sur l'écran de la liste des patients, utilisez la fonctionnalité de recherche. Vous devriez maintenant pouvoir saisir un nom (ou une partie d'un nom) pour filtrer la liste des patients en conséquence.
Vous avez ainsi amélioré votre application en permettant aux utilisateurs de rechercher efficacement des patients par leur nom. Cela peut considérablement améliorer l'expérience utilisateur et l'efficacité de la récupération des données.
8. Félicitations !
Vous avez utilisé la bibliothèque du moteur FHIR pour gérer les ressources FHIR dans votre application:
- Utiliser l'API Sync pour synchroniser des ressources FHIR avec un serveur FHIR
- Utiliser l'API Data Access pour créer, lire, mettre à jour et supprimer des ressources FHIR locales
- Rechercher des ressources FHIR locales à l'aide de l'API Search
Points abordés
- Configurer un serveur FHIR HAPI local
- Importer des données de test sur le serveur HAPI FHIR local
- Créer une application Android à l'aide de la bibliothèque du moteur FHIR
- Utiliser l'API Sync, l'API Data Access et l'API Search dans la bibliothèque du moteur FHIR
Étapes suivantes
- Consulter la documentation de la bibliothèque FHIR Engine
- Explorer les fonctionnalités avancées de l'API Search
- Appliquer la bibliothèque du moteur FHIR dans votre propre application Android