Skip to content

BLSQ/openHIE_simulation

Repository files navigation

Systeme Distribue d'Information de Sante — Multi-Services

Objectif pedagogique : Comprendre l'interoperabilite en sante a travers un systeme distribue realiste de 11 conteneurs Docker communiquant via des API REST et le standard FHIR R5, avec gestion des terminologies medicales (ICD-11 & LOINC) et alias locaux par structure.


Table des matieres

  1. Vue d'ensemble
  2. Architecture du systeme
  3. Guide Enseignant — Mise en route
  4. Guide Etudiant — Explorer et comprendre
  5. Standards et terminologies utilises
  6. Terminologies et alias locaux
  7. Structure des fichiers
  8. Reference API
  9. Scenarios cliniques de demonstration
  10. Aller plus loin

1. Vue d'ensemble

Ce projet simule un echange d'informations de sante (Health Information Exchange — HIE) entre :

  • 2 hopitaux ayant chacun leur propre systeme d'information (SIH) et leur base de donnees
  • 4 registres nationaux (identite, etablissements, professionnels de sante, index central)
  • 1 serveur de terminologies (ICD-11 & LOINC) servant de reference pour les codes medicaux
  • 1 portail patient permettant a un citoyen de consulter son dossier medical unifie

Chaque composant tourne dans son propre conteneur Docker avec sa propre adresse IP, exactement comme dans un systeme de sante reel ou les hopitaux, registres et portails sont des systemes independants qui communiquent par le reseau.

Pourquoi ce projet ?

Dans la realite, quand un patient consulte dans plusieurs hopitaux :

  • Chaque hopital a sa propre base de donnees (pas de base partagee)
  • Pour reunir le dossier complet, il faut interroger chaque hopital via des API standardisees
  • Les registres nationaux servent de "pages jaunes" pour localiser les donnees du patient
  • Le standard FHIR garantit que tous les systemes parlent le meme langage
  • Les terminologies (ICD-11, LOINC) permettent de coder les diagnostics et analyses avec un langage universel, meme si chaque hopital utilise ses propres abbreviations (alias locaux)

Ce projet reproduit exactement ce fonctionnement.


2. Architecture du systeme

Schema d'architecture

                           ┌─────────────────────────┐
                           │    Portail Patient       │ :3003
                           │   (React + FastAPI)      │
                           └────────────┬─────────────┘
                                        │
          ┌─────────────────────────────┼────────────────────────────┐
          │                             │                            │
  ┌───────▼────────┐         ┌──────────▼──────────┐      ┌─────────▼────────┐
  │ Registre NNI   │         │  Registre Central    │      │Registre Etab.    │
  │    :8001       │         │     :8004            │◄────►│    :8002         │
  │  (Identite)    │         │  (Index patients)    │      │    (MFL)         │
  └────────────────┘         └──────────┬───────────┘      └─────────┬────────┘
                                        │                            │
                          ┌─────────────┼──────────────┐    ┌────────▼────────┐
                          │                            │    │  Registre RHS   │
                  ┌───────▼───────┐            ┌───────▼───────┐   :8003     │
                  │  SIH Struct.A │            │  SIH Struct.B │  (HWR)      │
                  │  :3001/:8010  │            │  :3002/:8020  │             │
                  │  (Webapp)     │            │  (Webapp)     │─────────────┘
                  └───────┬───────┘            └───────┬───────┘
                          │                            │
                          └──────────┬─────────────────┘
                           ┌─────────▼──────────┐
                           │  Terminology Server │ :8005
                           │  ICD-11 & LOINC     │
                           │  (~10 000 codes)    │
                           └─────────────────────┘

Carte des 11 conteneurs

Conteneur Role Port hote IP interne Type
nni-registry Registre National d'Identite (NNI) 8001 172.28.0.10 Backend API
facility-registry Master Facility List (MFL) 8002 172.28.0.11 Backend API
hwr-registry Registre des Ressources Humaines en Sante 8003 172.28.0.12 Backend API
central-registry Index Central des Patients 8004 172.28.0.13 Backend API
terminology-server Serveur de terminologies ICD-11 & LOINC 8005 172.28.0.14 Backend API
sih-a-backend SIH Hopital National de Nouakchott 8010 172.28.0.20 Backend API
sih-a-frontend Interface web Hopital National 3001 172.28.0.21 Frontend React
sih-b-backend SIH Hopital Cheikh Zayed 8020 172.28.0.30 Backend API
sih-b-frontend Interface web Hopital Cheikh Zayed 3002 172.28.0.31 Frontend React
portal-backend Aggregateur du Portail Patient 8040 172.28.0.40 Backend API
portal-frontend Interface web Portail Patient 3003 172.28.0.41 Frontend React

3. Guide Enseignant — Mise en route

Pre-requis

  • Docker et Docker Compose installes (docker.com)
  • 8 Go de RAM minimum disponibles pour Docker
  • Ports 3001-3003 et 8001-8005, 8010, 8020, 8040 libres sur la machine

Lancement (une seule commande)

cd multi_services
docker compose up --build -d

Le premier build prend 5 a 10 minutes (telechargement des images Python et Node, installation des dependances, compilation React). Les lancements suivants sont quasi-instantanes grace au cache.

Verification que tout fonctionne

# Voir l'etat des 11 conteneurs (tous doivent etre "healthy" ou "Up")
docker compose ps

Resultat attendu : 11 conteneurs, tous en etat Up ou Up (healthy).

Verification rapide par les API

# 1. Registre NNI — verifier une identite
curl -s -X POST http://localhost:8001/api/verify \
  -H "Content-Type: application/json" \
  -d '{"nni":"1234567890"}' | python3 -m json.tool

# 2. Registre des etablissements — lister les hopitaux
curl -s http://localhost:8002/api/facilities | python3 -m json.tool

# 3. Registre central — localiser un patient
curl -s http://localhost:8004/api/locate/1234567890 | python3 -m json.tool

# 4. SIH Structure A — recuperer le dossier FHIR
curl -s "http://localhost:8010/api/fhir/patient-record?patient_nni=1234567890" | python3 -m json.tool

# 5. Portail Patient — dossier unifie complet
curl -s http://localhost:8040/api/dossier/1234567890 | python3 -m json.tool

# 6. Terminology Server — rechercher un code ICD-11
curl -s "http://localhost:8005/api/terminology/icd11/search?q=diabete&limit=5" | python3 -m json.tool

# 7. Terminology Server — rechercher un code LOINC
curl -s "http://localhost:8005/api/terminology/loinc/search?q=glucose&limit=5" | python3 -m json.tool

# 8. SIH Structure A — lister les alias locaux
curl -s http://localhost:8010/api/aliases | python3 -m json.tool

Arret et nettoyage

# Arreter tous les conteneurs
docker compose down

# Arreter ET supprimer les volumes (remet les bases de donnees a zero)
docker compose down -v

Documentation Swagger interactive

Chaque backend expose une documentation interactive a /docs :

Service URL Swagger
Registre NNI http://localhost:8001/docs
Registre Etablissements http://localhost:8002/docs
Registre RHS http://localhost:8003/docs
Registre Central http://localhost:8004/docs
Terminology Server http://localhost:8005/docs
SIH Structure A http://localhost:8010/docs
SIH Structure B http://localhost:8020/docs
Portail Patient http://localhost:8040/docs

Conseils pedagogiques

  • Montrer le schema d'architecture (section 2) avant de lancer le systeme
  • Commencer par le portail patient (port 3003) — c'est le plus visuel et le plus impressionnant
  • Ouvrir deux onglets SIH cote a cote (3001 et 3002) pour montrer que les donnees sont differentes
  • Utiliser l'onglet Journal Inter-Services dans les SIH pour voir les appels en temps reel
  • Montrer les terminologies : ouvrir l'onglet "Terminologies" dans un SIH, puis ouvrir le meme onglet dans l'autre SIH et comparer les alias pour le meme code (ex: BA00 = "HTA" vs "Maladie hypertensive")
  • Utiliser la Loupe de Terminologie : dans le detail d'un sejour, basculer entre les modes alias / standard / code pour montrer la difference entre affichage local et code FHIR
  • Faire les exercices de la section 4 en TD

4. Guide Etudiant — Explorer et comprendre

Etape 1 — Lancer le systeme

cd multi_services
docker compose up --build -d

Attendez que tous les conteneurs soient demarres (environ 2 minutes apres le build) :

docker compose ps

Etape 2 — Decouvrir le Portail Patient

  1. Ouvrez http://localhost:3003 dans votre navigateur
  2. Cliquez sur Fatima Mint Ahmed (bouton de demonstration)
  3. Observez ce qui se passe :
Etape 1 : Le portail envoie le NNI "1234567890" au Registre NNI
          → Le registre confirme l'identite de Fatima

Etape 2 : Le portail demande au Registre Central "ou Fatima a-t-elle des dossiers ?"
          → Reponse : Hopital National (MFL-NKC-001) + Hopital Cheikh Zayed (MFL-NKC-002)

Etape 3 : Le portail interroge CHAQUE hopital pour recuperer son dossier FHIR
          → Hopital A repond : Bundle FHIR (admission urgence, appendicite, analyses)
          → Hopital B repond : Bundle FHIR (transfert, appendicectomie, suivi post-op)

Etape 4 : Le portail fusionne les deux Bundle en un Dossier Patient Unifie

Point cle : Aucun hopital n'a acces a la base de donnees de l'autre. Les donnees sont echangees uniquement par des appels HTTP au format FHIR.

Etape 3 — Comparer les deux hopitaux

Ouvrez deux onglets :

Remarquez :

  • Les deux interfaces sont identiques (meme code) mais affichent des donnees differentes
  • Chaque hopital ne voit que ses propres patients
  • La Structure A a 4 patients, la Structure B en a 3

Etape 4 — Observer les echanges inter-services

  1. Dans le SIH de la Structure A (port 3001), allez dans Journal Inter-Services
  2. Puis allez dans Dossier Patient Unifie et recherchez le NNI 1234567890
  3. Revenez dans le Journal Inter-Services : vous verrez les appels HTTP qui viennent d'etre faits

Chaque ligne montre :

  • Direction : IN (appel recu) ou OUT (appel envoye)
  • Methode : GET ou POST
  • Service cible : quel registre ou hopital a ete contacte
  • Code HTTP : 200 = succes, 404 = non trouve
  • Duree : temps de reponse en millisecondes

Etape 5 — Tester les API manuellement avec curl

Ouvrez un terminal et reproduisez le parcours du portail pas a pas :

# Etape 1 : Qui est le NNI 1234567890 ?
curl -s -X POST http://localhost:8001/api/verify \
  -H "Content-Type: application/json" \
  -d '{"nni":"1234567890"}' | python3 -m json.tool

Observez la reponse : l'identite ET une ressource FHIR Patient sont retournees.

# Etape 2 : Dans quels hopitaux Fatima a-t-elle des dossiers ?
curl -s http://localhost:8004/api/locate/1234567890 | python3 -m json.tool

Observez : le registre central retourne une liste d'etablissements avec leurs URLs FHIR.

# Etape 3a : Recuperer le dossier FHIR de l'hopital A
curl -s "http://localhost:8010/api/fhir/patient-record?patient_nni=1234567890" \
  | python3 -m json.tool | head -80

# Etape 3b : Recuperer le dossier FHIR de l'hopital B
curl -s "http://localhost:8020/api/fhir/patient-record?patient_nni=1234567890" \
  | python3 -m json.tool | head -80

Comparez les deux reponses : ce sont des FHIR Bundle avec des ressources differentes.

Etape 6 — Explorer la documentation Swagger

Ouvrez http://localhost:8001/docs dans votre navigateur. C'est la documentation interactive du Registre NNI. Vous pouvez :

  1. Cliquer sur un endpoint (ex: POST /api/verify)
  2. Cliquer sur Try it out
  3. Entrer un NNI dans le corps de la requete
  4. Cliquer sur Execute
  5. Voir la reponse en direct

Faites de meme pour chaque registre (ports 8002, 8003, 8004) et chaque SIH (8010, 8020).

Etape 7 — Comprendre les flux de donnees

Flux 1 : Admission d'un patient au SIH

Quand un hopital admet un patient, il effectue cette sequence :

SIH ──POST /api/verify──────────► Registre NNI
     (envoie le NNI)              (verifie l'identite)
                                  ◄── repond : identite + FHIR Patient

SIH ──POST /api/register─────────► Registre Central
     (NNI + UID etablissement)     (enregistre la localisation)
                                   ◄── repond : OK

SIH ──INSERT local DB             (cree le patient + admission localement)

Flux 2 : Dossier Patient Unifie (requete federee)

Quand un hopital ou le portail veut le dossier complet d'un patient :

Demandeur ──GET /api/locate/{nni}──► Registre Central
                                     ◄── repond : liste des hopitaux

   Pour CHAQUE hopital dans la liste :
Demandeur ──GET /api/fhir/patient-record──► SIH cible
                                            ◄── repond : Bundle FHIR

Demandeur ── fusionne tous les Bundle en un dossier unifie

C'est le principe du profil IHE XCA (Cross-Community Access) utilise dans les systemes de sante reels comme le DMP en France ou le eHealth en Belgique.

Flux 3 : Recherche de terminologie et alias

Quand un clinicien recherche un code dans le formulaire de consultation :

Navigateur ──recherche "diabete"──────────► SIH Backend
   SIH Backend ──GET /api/terminology/icd11/search──► Terminology Server
                                                       ◄── repond : liste de codes
   SIH Backend ──SELECT FROM terminology_aliases──► SQLite locale
                                                     ◄── alias locaux pour ces codes
   SIH Backend ──fusionne (codes + alias)──────────► Navigateur

Le clinicien voit les codes standards enrichis de ses alias locaux. Quand il selectionne un code, c'est le code standard (ex: 5A11) qui est enregistre dans la consultation, pas l'alias ("Diabete T2").

Flux 4 : Le Portail Patient

Le portail est un aggregateur sans base de donnees. Il ne stocke rien. A chaque requete, il interroge les registres et hopitaux en temps reel :

Navigateur ──GET /api/dossier/{nni}──► Portail Backend
   Portail ──POST /api/verify────────► Registre NNI
   Portail ──GET /api/locate/{nni}───► Registre Central
   Portail ──GET /fhir/patient-record─► SIH A
   Portail ──GET /fhir/patient-record─► SIH B
   Portail ── fusionne + retourne au navigateur

5. Standards et terminologies utilises

FHIR R5 (Fast Healthcare Interoperability Resources)

FHIR est LE standard international pour l'echange de donnees de sante. Ce projet utilise la version R5 (la plus recente) avec ces ressources :

Ressource FHIR Description Exemple dans le projet
Patient Identite du patient Fatima Mint Ahmed, NNI 1234567890
Encounter Sejour / admission Urgence le 01/04, transfert le 01/04
Condition Diagnostic / pathologie Appendicite (SNOMED: 74400008)
Observation Resultat d'examen WBC = 15.2 10^9/L (LOINC: 6690-2)
Organization Etablissement de sante Hopital National (MFL-NKC-001)
Practitioner Professionnel de sante Dr. Ahmed Mohamed (LIC001)
Bundle Conteneur de ressources Collection de toutes les ressources d'un patient

Exemple de ressource FHIR Patient

{
  "resourceType": "Patient",
  "id": "abc-123",
  "identifier": [{
    "system": "http://registre-nni.mr/nni",
    "value": "1234567890",
    "type": {
      "coding": [{
        "system": "http://terminology.hl7.org/CodeSystem/v2-0203",
        "code": "NIIP",
        "display": "National Individual Identification"
      }]
    }
  }],
  "name": [{ "use": "official", "family": "Mint Ahmed", "given": ["Fatima"] }],
  "gender": "female",
  "birthDate": "1985-03-15"
}

Exemple de ressource FHIR Condition (diagnostic)

{
  "resourceType": "Condition",
  "subject": { "reference": "Patient?identifier=http://example.com/nni|1234567890" },
  "code": {
    "coding": [
      { "system": "http://snomed.info/sct", "code": "74400008", "display": "Appendicitis" },
      { "system": "http://hl7.org/fhir/sid/icd-11", "code": "DC11", "display": "Appendicite" }
    ]
  },
  "clinicalStatus": {
    "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", "code": "active" }]
  },
  "onsetDateTime": "2026-04-01T00:00:00"
}

A retenir : Une meme pathologie est codee avec deux systemes (SNOMED CT + ICD-11) dans le champ coding[]. C'est une bonne pratique FHIR qui garantit l'interoperabilite meme si les systemes cibles ne supportent qu'un seul des deux referentiels.

Terminologies medicales

SNOMED CT (Systematized Nomenclature of Medicine)

Le referentiel clinique le plus complet au monde (~350 000 concepts). Utilise pour decrire avec precision les diagnostics, symptomes et actes.

Code Libelle Usage dans le projet
74400008 Appendicitis Diagnostic de Fatima
44054006 Type 2 diabetes mellitus Diagnostic de Mohamed
61462000 Malaria Diagnostic d'Aicha
38341003 Essential hypertension Diagnostic de Mariam
263102004 Fracture Diagnostic d'Ousmane
309343006 Physician Profession (Dr. Ahmed)
106292003 Nurse Profession (Fatima infirmiere)

ICD-11 (Classification Internationale des Maladies, 11e revision)

Le referentiel de l'OMS pour la classification des maladies, utilise mondialement pour les statistiques de sante et la facturation.

Code Libelle Equivalent SNOMED
DC11 Appendicite 74400008
5A11 Diabete sucre de type 2 44054006
1F40 Paludisme a P. falciparum 61462000
BA00 Hypertension arterielle essentielle 38341003
NA10 Fracture de l'avant-bras 263102004

LOINC (Logical Observation Identifiers Names and Codes)

Le referentiel international pour les analyses de laboratoire et les observations cliniques.

Code Libelle Valeur demo Normale Interpretation
6690-2 Leucocytes (WBC) 15.2 x10^9/L 4.0–10.0 ELEVE (infection)
1988-5 Proteine C-reactive (CRP) 85 mg/L 0–5 ELEVE (inflammation)
2345-7 Glucose sanguin 1.85 g/L 0.7–1.1 ELEVE (diabete)

Profils IHE utilises (conceptuellement)

Profil Description Simulation dans le projet
PIX/PDQ Patient Identity Cross-referencing Registre NNI + Registre Central
XCA Cross-Community Access Requete federee du dossier unifie
MHD Mobile access to Health Documents Endpoints FHIR des SIH
mCSD Mobile Care Services Discovery Registre des etablissements
HPD Healthcare Provider Directory Registre RHS

6. Terminologies et alias locaux

Le probleme de la terminologie en sante

Dans un hopital, un medecin dira "HTA" pour designer l'hypertension arterielle essentielle. Dans un autre, on parlera de "Maladie hypertensive". Pourtant, il s'agit du meme diagnostic, identifie par le code BA00 dans la classification internationale ICD-11.

Pour que les systemes d'information se comprennent, il faut un langage commun : les terminologies medicales internationales. Mais pour que les cliniciens soient a l'aise, il faut que l'interface affiche le vocabulaire local de leur etablissement.

Architecture de la terminologie dans ce projet

┌─────────────────────────────────────────────────────────────────┐
│                    Terminology Server (:8005)                    │
│  Charge ~10 000 codes en memoire depuis des fichiers JSON       │
│  ┌──────────────────┐  ┌──────────────────┐                    │
│  │   ICD-11          │  │   LOINC           │                    │
│  │ ~9 600 codes      │  │ ~5 000 codes      │                    │
│  │ 20 chapitres      │  │ 10 categories     │                    │
│  │ Diagnostics       │  │ Analyses labo     │                    │
│  └──────────────────┘  └──────────────────┘                    │
└───────────────────────────┬─────────────────────────────────────┘
                            │ HTTP (recherche, lookup)
              ┌─────────────┼─────────────────┐
              │                               │
    ┌─────────▼──────────┐          ┌─────────▼──────────┐
    │ SIH Structure A    │          │ SIH Structure B    │
    │                    │          │                    │
    │ Alias locaux :     │          │ Alias locaux :     │
    │  BA00 → "HTA"      │          │  BA00 → "Maladie   │
    │  6690-2 → "GB"     │          │         hypertens." │
    │  1988-5 → "CRP"    │          │  6690-2 → "Numera-  │
    │                    │          │         tion leuco." │
    └────────────────────┘          └────────────────────┘
              │                               │
              └───────── FHIR echange ────────┘
              (toujours le code standard BA00,
               jamais l'alias local)

Serveur de terminologies

Le Terminology Server est un microservice stateless qui charge les catalogues ICD-11 et LOINC en memoire au demarrage depuis des fichiers JSON statiques. Il offre :

  • Recherche fuzzy : accent-insensitive, par code ou par libelle (francais)
  • Classement par pertinence : correspondance exacte > debut de code > debut de libelle > contient
  • Filtrage par chapitre ICD-11 ou categorie LOINC
  • Lookup direct : obtenir le detail d'un code par son identifiant

Le serveur ne contient aucune donnee patient. C'est un catalogue de reference, equivalent au role d'un "FHIR Terminology Server" dans une architecture de sante reelle.

Alias locaux par structure

Chaque SIH (Structure A et Structure B) possede sa propre table d'alias dans sa base SQLite :

Champ Description
system "icd11" ou "loinc"
code Code standard (ex: BA00, 6690-2)
standard_display Libelle officiel du catalogue
alias Nom local choisi par la structure

Exemples pre-charges :

Code Standard Structure A (jargon) Structure B (formel)
BA00 Hypertension arterielle essentielle HTA Maladie hypertensive
5A11 Diabete sucre de type 2 Diabete T2 Diabete non insulinodependant
6690-2 Leucocytes (GB) GB Numeration leucocytaire
2160-0 Creatinine serique Creat Dosage de la creatinine serique
8310-5 Temperature corporelle Temperature

Point cle : Les alias ne voyagent jamais dans les echanges FHIR. Seul le code standard (BA00, 6690-2) est transmis entre structures. L'alias est purement un confort d'affichage local.

Mode strict de selection

L'application SIH impose un mode strict pour la selection des codes de terminologie :

  • Pas de saisie libre : le clinicien doit rechercher et selectionner un code dans le catalogue officiel
  • Un composant TerminologyPicker offre une recherche avec autocompletion
  • Le code standard est enregistre dans la donnee clinique (consultation, condition, observation)
  • L'alias local (s'il existe) est affiche dans l'interface

Loupe de terminologie (Terminology Lens)

Dans les vues de detail des sejours et du dossier unifie, un selecteur de mode d'affichage permet de basculer entre trois vues :

Mode Affichage Interet pedagogique
Alias Nom local de la structure Ce que voit le clinicien au quotidien
Standard Libelle officiel du catalogue Le nom normalise international
Code Code technique (BA00, 6690-2) Ce qui voyage reellement dans le FHIR

C'est un outil pedagogique puissant : il permet de montrer visuellement que derriere l'abbreviation "HTA" se cache le code BA00 qui est le meme pour toutes les structures.


7. Structure des fichiers

multi_services/
├── README.md                              ← ce fichier
├── docker-compose.yml                     ← orchestration des 11 conteneurs
│
├── shared/
│   └── fhir_utils.py                     ← constructeurs de ressources FHIR (partage)
│
├── terminology_server/                    ← Serveur de Terminologies
│   ├── main.py                           ← API : recherche, lookup, chapitres/categories
│   ├── generate_data.py                  ← Generateur des catalogues ICD-11 & LOINC (JSON)
│   ├── data/
│   │   ├── icd11.json                   ← ~9 600 codes ICD-11 (20 chapitres)
│   │   └── loinc.json                   ← ~5 000 codes LOINC (10 categories)
│   ├── requirements.txt
│   └── Dockerfile
│
├── nni_registry/                          ← Registre National d'Identite
│   ├── main.py                           ← API : POST /api/verify, GET /api/fhir/Patient
│   ├── database.py                       ← Modele : Identity(nni, nom, prenom, ddn, sexe)
│   ├── seed_data.py                      ← 6 identites de demonstration
│   ├── requirements.txt
│   └── Dockerfile
│
├── facility_registry/                     ← Registre des Etablissements (MFL)
│   ├── main.py                           ← API : GET/POST /api/facilities, FHIR Organization
│   ├── database.py                       ← Modele : Facility(uid, nom, type, fhir_endpoint)
│   ├── seed_data.py                      ← 2 structures hospitalieres
│   ├── requirements.txt
│   └── Dockerfile
│
├── hwr_registry/                          ← Registre des Ressources Humaines en Sante
│   ├── main.py                           ← API : GET/POST /api/workers, FHIR Practitioner
│   ├── database.py                       ← Modele : Worker(licence, nom, profession, facility)
│   ├── seed_data.py                      ← 6 professionnels (3 par structure)
│   ├── requirements.txt
│   └── Dockerfile
│
├── central_registry/                      ← Index Central des Patients
│   ├── main.py                           ← API : POST /api/register, GET /api/locate/{nni}
│   ├── database.py                       ← Modele : PatientLocation(nni, facility_uid)
│   ├── seed_data.py                      ← 7 associations patient-etablissement
│   ├── requirements.txt
│   └── Dockerfile
│
├── sih_app/                               ← Application SIH (partagee par les 2 structures)
│   ├── backend/
│   │   ├── main.py                       ← API : admissions, FHIR, dossier unifie, terminologie, alias
│   │   ├── database.py                   ← Patient, Admission, Consultation, Condition, Observation, TerminologyAlias
│   │   ├── interop_client.py             ← Client HTTP inter-services (httpx + logging)
│   │   ├── seed_data.py                  ← Donnees + alias conditionnels selon STRUCTURE_UID
│   │   ├── requirements.txt
│   │   └── Dockerfile
│   └── frontend/
│       ├── src/
│       │   ├── App.jsx                   ← Application principale avec navigation
│       │   ├── api.js                    ← Wrapper fetch avec interception reseau
│       │   ├── NetworkContext.jsx         ← Contexte React pour les logs reseau
│       │   └── components/
│       │       ├── PatientAdmit.jsx      ← Formulaire d'admission (appels inter-services)
│       │       ├── AdmissionsManager.jsx ← Sejours, consultations + Loupe de terminologie
│       │       ├── TerminologyManager.jsx← Navigateur ICD-11/LOINC + gestion des alias
│       │       ├── TerminologyPicker.jsx ← Selecteur strict de codes (autocompletion)
│       │       ├── DossierUnifie.jsx     ← Vue federee multi-structures + Loupe terminologie
│       │       ├── InteropPanel.jsx      ← Journal des appels inter-services
│       │       └── NetworkPanel.jsx      ← Inspecteur reseau (dev tools)
│       ├── nginx.conf.template           ← Proxy nginx (BACKEND_HOST dynamique)
│       ├── package.json
│       └── Dockerfile
│
└── patient_portal/                        ← Portail Patient
    ├── backend/
    │   ├── main.py                       ← Aggregateur : NNI → Central → SIH → fusion
    │   ├── requirements.txt
    │   └── Dockerfile
    └── frontend/
        ├── src/
        │   ├── App.jsx                   ← Application portail
        │   ├── api.js                    ← Client API
        │   └── components/
        │       ├── NNISearch.jsx          ← Recherche par NNI
        │       ├── ArchitectureDiagram.jsx← Schema anime de l'architecture
        │       ├── UnifiedDossier.jsx     ← Dossier unifie avec timeline
        │       └── CallTrace.jsx          ← Cascade des appels inter-services
        ├── nginx.conf
        ├── package.json
        └── Dockerfile

8. Reference API

Terminology Server (port 8005)

Methode Endpoint Description
GET /api/terminology/icd11/search?q={terme}&chapter={num}&limit={n} Recherche ICD-11 par code ou libelle
GET /api/terminology/loinc/search?q={terme}&category={cat}&limit={n} Recherche LOINC par code ou libelle
GET /api/terminology/icd11/{code} Detail d'un code ICD-11
GET /api/terminology/loinc/{code} Detail d'un code LOINC
GET /api/terminology/icd11/chapters Liste des chapitres ICD-11 avec nombre de codes
GET /api/terminology/loinc/categories Liste des categories LOINC avec nombre de codes
GET /api/terminology/stats Statistiques des deux systemes de terminologie
GET /api/logs Journal des requetes recues

Registre NNI (port 8001)

Methode Endpoint Description Corps requete
POST /api/verify Verifier une identite nationale {"nni": "1234567890"}
GET /api/fhir/Patient?identifier={nni} Recherche FHIR Patient
GET /api/identities Lister toutes les identites
GET /api/logs Journal des requetes

Registre des Etablissements (port 8002)

Methode Endpoint Description Corps requete
GET /api/facilities Lister les etablissements
GET /api/facilities/{uid} Detail + FHIR Organization
POST /api/facilities/register Enregistrer un etablissement {"uid", "name", ...}
GET /api/fhir/Organization Bundle FHIR Organization

Registre RHS (port 8003)

Methode Endpoint Description Corps requete
GET /api/workers Lister (filtre: ?facility_uid=)
GET /api/workers/{license} Detail + FHIR Practitioner
POST /api/workers Enregistrer un professionnel {"license_number", ...}
GET /api/fhir/Practitioner Bundle FHIR Practitioner

Registre Central (port 8004)

Methode Endpoint Description Corps requete
POST /api/register Enregistrer patient ↔ etablissement {"patient_nni", "facility_uid"}
GET /api/locate/{nni} Localiser un patient
GET /api/entries Lister toutes les associations

SIH Backend (ports 8010 / 8020)

Methode Endpoint Description
POST /api/patients/admit Admettre un patient (workflow inter-services)
GET /api/patients Lister les patients locaux
GET /api/admissions Lister les admissions
GET /api/admissions/{id} Detail d'une admission (consultations, observations)
POST /api/consultations Creer une consultation (avec codes ICD-11 en mode strict)
POST /api/conditions Enregistrer un diagnostic (SNOMED + ICD-11)
POST /api/observations Enregistrer une observation (code LOINC en mode strict)
GET /api/dossier-unifie/{nni} Dossier Patient Unifie (requete federee)
GET /api/fhir/patient-record?patient_nni={nni} Bundle FHIR du patient local
GET /api/fhir/Patient?identifier={nni} Recherche FHIR Patient
GET /api/terminology/{system}/search?q=... Recherche terminologie (proxy + alias locaux)
GET /api/terminology/{system}/chapters Chapitres/categories (proxy terminology server)
GET /api/aliases?system={icd11|loinc} Lister les alias locaux de cette structure
POST /api/aliases Creer/modifier un alias local
PUT /api/aliases/{id} Modifier un alias existant
DELETE /api/aliases/{id} Supprimer un alias (revient au nom standard)
GET /api/aliases/resolve?system=...&codes=... Resolution par lot (codes → alias locaux)
GET /api/interop-logs Journal des appels inter-services
GET /api/structure-info Informations sur la structure
GET /api/workers Professionnels de sante (via registre RHS)

Portail Patient (port 8040)

Methode Endpoint Description
GET /api/dossier/{nni} Dossier unifie complet + trace des appels
GET /api/architecture Graphe JSON de l'architecture pour le diagramme

9. Scenarios cliniques de demonstration

Patients pre-charges

NNI Nom Structures Scenario
1234567890 Fatima Mint Ahmed A + B Appendicite → transfert → chirurgie
MR444555666 Mariam Diallo A + B Hypertension (A) + cardiologie (B)
MR123456789 Mohamed Ould Sidi A Diabete type 2
MR987654321 Aicha Mint Cheikh A Paludisme
MR111222333 Ousmane Ba B Fracture du poignet
MR777888999 Ibrahim Ould Moulaye aucune Identite seule (pas encore admis)

Scenario principal : Le parcours de Fatima

Ce scenario illustre un transfert inter-hospitalier realiste :

Jour 1 — Hopital National (Structure A)

  1. Fatima arrive aux urgences avec une douleur abdominale aigue
  2. Dr. Ahmed Mohamed (medecin generaliste) l'examine
  3. Analyses de laboratoire :
    • WBC = 15.2 x10^9/L (leucocytes eleves → infection)
    • CRP = 85 mg/L (inflammation severe)
  4. Diagnostic : suspicion d'appendicite aigue
  5. Decision : transfert vers l'Hopital Cheikh Zayed pour chirurgie

Jour 1 — Hopital Cheikh Zayed (Structure B) 6. Fatima est recue en chirurgie comme transfert 7. Dr. Moussa Diallo (chirurgien) confirme l'appendicite par imagerie 8. Appendicectomie laparoscopique realisee avec succes

Jour 2 — Suivi post-operatoire 9. Dr. Moussa note une evolution favorable 10. Condition mise a jour : appendicite → resolue

Consultation du portail 11. Fatima se connecte au portail patient (port 3003) avec son NNI 12. Le portail interroge les deux hopitaux et affiche : - 2 admissions (urgence + transfert) - 3 consultations (initiale + specialiste + suivi) - 2 analyses de laboratoire - 1 diagnostic (appendicite, resolue)

Exercices pratiques pour les etudiants

Exercice 1 — Tracer le parcours de Fatima

  1. Ouvrez le portail (port 3003) et recherchez Fatima (NNI 1234567890)
  2. Identifiez combien de Bundle FHIR ont ete retournes et par quels hopitaux
  3. Comptez les ressources FHIR par type (Patient, Encounter, Condition, Observation)
  4. Retrouvez les codes SNOMED et ICD-11 du diagnostic

Exercice 2 — Comparer les dossiers locaux

  1. Ouvrez le SIH Structure A (port 3001), onglet "Dossier Patient Unifie"
  2. Recherchez Fatima — observez la trace des appels inter-services
  3. Faites la meme chose depuis le SIH Structure B (port 3002)
  4. Question : les traces sont-elles identiques ? Pourquoi ?

Exercice 3 — Admettre un nouveau patient

  1. Ouvrez le SIH Structure A (port 3001), onglet "Patients & Admissions"
  2. Admettez Ibrahim (NNI MR777888999) — c'est son premier sejour
  3. Observez la trace : quels registres ont ete contactes ?
  4. Verifiez dans le Registre Central (port 8004) que l'association a ete creee :
    curl -s http://localhost:8004/api/locate/MR777888999 | python3 -m json.tool

Exercice 4 — Explorer les API directement

  1. Ouvrez le Swagger du registre NNI (http://localhost:8001/docs)
  2. Utilisez POST /api/verify pour verifier l'identite de Mariam (NNI MR444555666)
  3. Notez le resourceType dans la reponse — c'est une ressource FHIR de quel type ?
  4. Utilisez GET /api/fhir/Patient?identifier=MR444555666 — quelle est la difference ?

Exercice 5 — Comprendre l'absence de base partagee

  1. Listez les patients de la Structure A : curl -s http://localhost:8010/api/patients
  2. Listez les patients de la Structure B : curl -s http://localhost:8020/api/patients
  3. Mohamed (MR123456789) n'apparait que dans une structure — laquelle ?
  4. Que retourne le portail pour Mohamed ? Combien de structures ?
    curl -s http://localhost:8040/api/dossier/MR123456789 | python3 -c \
      "import sys,json; d=json.load(sys.stdin); print(f'Structures: {d[\"total_structures\"]}, Ressources: {d[\"total_resources\"]}')"

Exercice 6 — Explorer les terminologies medicales

  1. Ouvrez le SIH Structure A (port 3001), onglet Terminologies > ICD-11 & LOINC
  2. Recherchez "diabete" dans ICD-11 — combien de codes correspondent ?
  3. Recherchez "glucose" dans LOINC — observez les variantes (serique, urinaire, etc.)
  4. Notez le code ICD-11 du diabete type 2 et le code LOINC du glucose sanguin
  5. Verifiez directement via l'API :
    # Recherche ICD-11
    curl -s "http://localhost:8005/api/terminology/icd11/search?q=diabete+type+2&limit=3" \
      | python3 -m json.tool
    
    # Recherche LOINC
    curl -s "http://localhost:8005/api/terminology/loinc/search?q=glucose&limit=5" \
      | python3 -m json.tool
  6. Question : pourquoi un meme concept ("diabete") peut-il avoir plusieurs codes ICD-11 ? (Indice : regardez les suffixes — 5A11, 5A11.0Z, 5A11.1Z...)

Exercice 7 — Comparer les alias entre structures

  1. Ouvrez deux onglets : Structure A (port 3001) et Structure B (port 3002)
  2. Dans chacun, allez dans Terminologies et cherchez le code BA00
  3. Observez l'alias local :
    • Structure A : HTA (jargon medical abrege)
    • Structure B : Maladie hypertensive (appellation formelle)
  4. Comparez les alias LOINC : cherchez 6690-2 (leucocytes)
    • Structure A : GB (globules blancs — abrege)
    • Structure B : Numeration leucocytaire (nom complet de l'analyse)
  5. Verifiez par l'API :
    # Alias de Structure A
    curl -s http://localhost:8010/api/aliases?system=icd11 | python3 -c \
      "import sys,json; [print(f\"{a['code']:8} → {a['alias']}\") for a in json.load(sys.stdin)[:5]]"
    
    # Alias de Structure B
    curl -s http://localhost:8020/api/aliases?system=icd11 | python3 -c \
      "import sys,json; [print(f\"{a['code']:8} → {a['alias']}\") for a in json.load(sys.stdin)[:5]]"
  6. Question : quand Structure A envoie un diagnostic a Structure B via FHIR, est-ce "HTA" ou "BA00" qui est transmis ? Pourquoi ?

Exercice 8 — Creer une consultation avec terminologie stricte

  1. Dans le SIH Structure A, ouvrez un sejour existant
  2. Ajoutez une nouvelle consultation en utilisant le selecteur ICD-11 :
    • Recherchez "paludisme" et selectionnez un code (pas de saisie libre !)
    • Observez que le code standard est enregistre, pas l'alias
  3. Ajoutez une observation en utilisant le selecteur LOINC :
    • Recherchez "hemoglobine" et selectionnez le code LOINC
    • Entrez une valeur et une unite
  4. Basculez la Loupe de terminologie entre les modes Alias / Standard / Code
  5. Question : quel est l'interet du mode strict par rapport a la saisie libre ? (Indice : pensez a l'interoperabilite et aux statistiques de sante)

Exercice 9 — Creer un alias personnalise

  1. Dans le SIH Structure A, allez dans Terminologies
  2. Recherchez un code qui n'a pas encore d'alias (ex: recherchez "fracture")
  3. Ajoutez un alias de votre choix (ex: "Fx" pour le code NA10)
  4. Retournez dans un sejour qui contient ce code et activez la Loupe de terminologie
  5. Verifiez que votre alias apparait en mode "Alias"
  6. Ouvrez le SIH Structure B — votre alias y est-il visible ? Pourquoi ?

10. Aller plus loin

Concepts cles a retenir

Concept Explication Concretisation dans le projet
Interoperabilite Capacite de systemes differents a echanger des donnees Les 11 conteneurs communiquent via HTTP + FHIR
Requete federee Interroger plusieurs sources et fusionner les resultats GET /api/dossier-unifie/{nni}
Registre national Service centralise de reference (identite, etablissements) NNI, MFL, RHS, Index Central
Serveur de terminologie Catalogue centralise des codes medicaux Terminology Server (ICD-11 + LOINC)
Alias local Vocabulaire specifique a un etablissement HTA (Struct.A) vs Maladie hypertensive (Struct.B) pour BA00
Mode strict Selection contrainte dans un referentiel officiel TerminologyPicker — pas de saisie libre
FHIR Bundle Conteneur de ressources FHIR Reponse de /api/fhir/patient-record
CodeableConcept Concept code avec coding[] multi-systeme SNOMED + ICD-11 dans une meme Condition
Double codage Coder un diagnostic dans 2 referentiels SNOMED CT + ICD-11 pour chaque Condition
Idempotence Une operation repetee a le meme effet POST /api/register ne cree pas de doublon
Stateless aggregator Service sans base de donnees propre Le portail patient et le terminology server ne stockent rien
Service mesh Reseau de microservices communicant entre eux Docker network health-net (172.28.0.0/16)

Technologies utilisees

Composant Technologie Role
Backends API FastAPI (Python 3.11) Framework web asynchrone
ORM SQLAlchemy + SQLite Acces base de donnees
FHIR fhir.resources (v7.1) Construction de ressources FHIR R5
HTTP inter-services httpx Client HTTP asynchrone
Frontends React 18 + Tailwind CSS Interfaces web
Proxy nginx (envsubst) Reverse proxy dynamique
Orchestration Docker Compose Gestion des 10 conteneurs
Reseau Docker bridge network Isolation et routage

Liens utiles

About

This repo reproduces a network of health structures, registries that composes an SIH and aims at making understand the FHIR data exchange and standards used

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors