# Consolidation des outils
Le but de ce document est de servir de base à l’évolution des outils vers plus de stabilité et de simplicité.
Chaque étape envisagée ici répond à un problème constaté par l’équipe IT au sens large.
## Modèles de données et stockage
Voir le package `earlytracks.types` pour les différents types. En résumé on aura:
- `Document`: une classe qui contient des sections, avec le texte d’un document, et des informations telles que la date du document, son auteur etc…
- `Entity`: une classe qui contient tout ce qu’il y a à savoir sur une entité
- `Patient`: une classe qui contient les informations sur un patient
- `Encounter`: une classe qui contient les informations relative à une interraction patient / hôpital
- `Group` = `Condition`: une classe qui regroupe pour un patient une liste d’entités selon des critères déterminés par le `group_head` de la codification. Un patient aura presque toujours plusieurs groupes.
Référence:
`5. Transversal/5.B.CustomerDocumentation/5.B.2.TechnicalDocumentationToClients/201116 - Metadata tables.xlsx`
Chacune de ces classes de base aura son propre index dans ElasticSearch, avec des liens vers les autres via les ids (`document_id`, `patient_id`, `encounter_id`, `entity_id`). À noter que même si ElasticSearch n’est pas forcément le meilleur outil pour maintenir des relations, il est capable de le faire assez bien. Ce qui nous intéresse particulièrement dans ES, c’est sa capacité à effectuer des requêtes complexes rapidement, et la structure envisagée tend à maximiser la performance et la flexibilité.
Pour des versions ultérieures, on pourra envisager d’autres moyens de stockages, mais en évitant au maximum la fragmentation des stockages hyper-spécialisés, qui posent alors des problèmes de duplication des données, de maintient des outils, de complexification des mesures de sécurité (GDPR) et de place sur le disque.
Pour `Group`, Brice envisage la fusion et le split de groupes (ok avec la proposition actuelle), mais aussi que des groupes aient des «relations» avec d’autres groupes, et que ces relations soient parfois directionnelles. Il a notamment parlé de «sous-groupes».
## ems-ui, resumed, browser et ems-retro
`resumed` et `browser` possèdent une série de webservices qui sont utilisés en tant que backend de `patientgraph`.
`ems-retro`, un service temporaire qui existe uniquement pour des raisons de développement, contient aussi des webservices pour `patientgraph`
### Situation actuelle
Déjà dans `ems-ui`:
- GET `/v1/encounter/<encounter_id>`: retrouver un encounter en particulier
- GET `/v1/encounter/patient/<patient_id>/list`: liste d’encounters pour un patient
- GET `/v1/patient/<patient_id>`: informations sur un patient
Dans `ems-retro`:
- POST `/v1/normalizer/codification`: retrouver le code `{sct|icd10}` pour un terme
- POST `/v1/normalizer/visualize/sct/description`: retrouver la description d’un code `sct`
- POST `/v1/normalizer/visualize/icd/description`: retourver la description d’un code `icd10`
Dans `browser`:
- POST `/browser/document/simple/query`: retrouver les documents pour un utilisateur (ne pas se laisser tromper par le mot «simple»)
Dans `resumed`
- POST `/config/token`: login via login / password
- GET `/entities/<patient_id>/groups`: retrouver les groupes pour un patient
- POST `/entities/<patient_id>/groups/<group_id>`: sauver un groupe pour un patient
### Modifications nécessaires
Tous les WS doivent se retrouver dans `ems-ui`. On n’est pas bridés par une rétrocompatibilité, il est facile de modifier les appels faits par `patientgraph` (dans `src/js/Libs/Services`).
Voici ce à quoi on devrait arriver dans `ems-ui`:
- GET `/v1/encounter?patient_id=<patient_id>`: liste d’encounters pour un patient
- GET `/v1/codification/code?term=xxx&termino={sct|icd10}&language={fr|nl}`: retrouver le code {sct\|icd10} pour un terme
- GET `/v1/codification/description?code=xxx&termino={sct|icd10}&language={fr|nl}`: retrouver la description pour un code `{sct|icd10}`
- GET `/v1/document?patient_id=<patient_id>&size=20&page=0`: retrouver les documents pour un patient (avec paging)
- GET `/v1/document/<doc_id>`: retrouver le document par son ID
- GET `/v1/group?patient_id=<patient_id>`: retrouver les groupes pour un patient
- POST `/v1/group`: sauver un groupe (le `patient_id` est déjà dedans)
- POST `/v1/login`: se logguer et recevoir un token en réponse
## ems-nlp et lib earlytracks
### NLP
Il faut une fonction de génération et de sauvegarde des groupes, pour remplacer ce qui se trouve actuellement dans resumed. Cette fonction sera appelée (ou pas selon les options) dans `earlytracks.orchester.Client.annotate()`.
Il serait sans doute judicieux d’effectuer un renommage dans la lib:
`earlytracks.orchester.Client.annotate()` -> `earlytracks.nlp.Client.nlp()`?
Le nom `orchester` renvoie à un service qui n’existe plus, et ce qu’on veut faire c’est du NLP avec différentes options (nettoyage, ner, codification, création de groupes et indexation en bonus).
Pour l’API REST exposée par `ems-nlp`, on aura:
- POST `/v1/process`: envoi d’un document pour annotation en direct, pas de stockage
- POST `/v1/push`: envoi d’un document pour annotation et stockage en direct (ce que faisait l’orchester)
- POST `/v1/batch`: envoi d’une liste de documents pour annotation et stockage en asynchrone, réponse `202 Accepted`
- GET `/v1/document/<doc_id>`: récupération d’un document indexé
- GET `/v1/document/<doc_id>/status`: récupération du statut d’un document (`received`, `processing`, `error`, `completed`)
- POST `/v1/patient`: envoi d’un patient
- GET `/v1/patient/<patient_id>`: récupération des données d’un patient
- POST `/v1/encounter`: envoi d’un encounter
- GET `/v1/encounter/<encounter_id>`: récupération des données d’un encounter
Système de queue à reprendre du `warehouser`, en l’adaptant à ElasticSearch.
Proposition de ce qu’on envoie à `push` et `batch`:
```json
{
"document": {"obligatoire":""},
"patient": {"facultatif":""},
"encounter": {"facultatif":""}
}
```
Si on ne reçoit pas de `patient`, on a toujours le `patient_id` dans le document (c’est obligatoire), donc on peut avoir un objet `patient` dans l’index, même s’il n’y a que le `patient_id`. On demande toujours un `patient_id` dans le document et on ne génère jamais nous-même de `patient_id`.
Si on ne reçoit pas d’encounter, on a l’encounter_id dans le document, et si l’encounter n’existe pas encore, on en crée un vide avec juste un id, et un patient_id.
### Utilisateurs et tokens
Il faut que les API REST soient protégées. On peut stocker les utilisateurs dans l’index, avec un hash de mot de passe fort, de préférence [argon2id](https://en.wikipedia.org/wiki/Argon2). On peut aussi déléguer cette partie au LDAP ou ActiveDirectory du client.
Pour les tokens, on utilisera [JWT](https://jwt.io/) afin de se libérer de la contrainte de stocker les tokens.
Sur ces bases, il sera assez facile de faire un middleware pour FastApi qu’on pourra utiliser aussi bien dans `ems-nlp` que dans `ems-ui`.
Il faudra penser à la façon de stocker les tokens et de les transmettre lors des requêtes. Pour les appels, on attendra une requête avec comme header:
```
Authorization: Bearer <token>
```
Idéalement, il faudrait que le token soit stocké dans un cookie lors de l’appel au WS de login, afin d’éviter comme actuellement de le stocker dans le localstorage dans `patientgraph`.
### Intégration zorgi
Des trucs (CPL?) peuvent être validés dans `patientgraph`, et envoyés vers le DPI (zorgi). Une fois envoyés, ça ne doit plus pouvoir être renvoyé une deuxième fois.
Faire un module `earlytracks.zorgi` qui se charge de faire un objet `ZorgiExport` et de l’envoyer vers zorgi. Et dans `ems-ui`, une config zorgi qui se charge d’exposer l’envoi vers zorgi à `patientgraph` + bouton d’envoi dans `patientgraph`, qui se grise ou disparaît si l’action n’est plus possible.
On peut imaginer à l’avenir, quelques modules de ce genre, puisqu’il est probable qu’on doive s’intégrer avec différents DPIs. Le tout est de garder les vraies fonctionnalités dans le module (format d’export, moyen d’export etc…) et de ne fournir dans `patientgraph` et `ems-ui` que des points très précis où on peut activer une ou l’autre fonctionnalité au besoin.
## Déploiement
À priori, le `blueprint-compose` tel qu’il existe actuellement est suffisant pour nos besoins. Il y aura sans doute moyen de le simplifier avec la disparition des flows ou de certaines DBs, mais la base restera `./configure.sh <client>` avec des `docker-compose pull` et `docker-compose up -d --remove-orphans`.
À noter l’apparition d’une config `debug-full` qui donne des points de montages pour du code local dans un `docker-compose.override`. Cette config se base sur le fait qu’on a un répertoire (peu importe où) qui contient tous les projets `ems` (`earlytracks`, `blueprint-compose`, `patientgraph`, `resumed`) et donc que depuis `blueprint-compose`, on peut monter le code de patientgraph depuis `../patientgraph/dist` etc… Cette config est très importante pour un workflow de développement fluide, sans devoir commiter, pusher, attendre une image, puis la puller et la lancer. On change le code, on `restart` le container, et on voit le résultat. Pour `patientgraph`, il faut lancer `npm run watch` pour qu’il refasse le répertoire `dist` à chaque changement de code. Pour tous les services, on peut lancer le restart des containers avec [`entr`](https://eradman.com/entrproject/) par exemple pour `ems-ui`:
```bash
# from 'blueprint-compose' folder
find ../earlytracks/earlytracks ../earlytracks/ui -type f -name '*.py' | \
SHELL=/bin/bash \# needed if you use fish, otherwise ignore this
entr -s -cr "docker-compose restart ems-ui && docker-compose logs -f --tail 100 ems-ui"
```
On surveille tous les fichiers `.py` dans les répertoires (et sous-répertoires) `../earlytracks/earlytracks` (la lib) et `../earlytracks/ui` (ems-ui), et quand un de ces fichiers change, on lance le restart du container et les logs.
### À garder:
__proxy__
- haproxy
__db__
- elasticsearch-index
__NLP__
- elasticsearch-codif
- event-server-fr
- event-server-nl
- ems-nlp
- cleaner_server _(kill it later)_
__UI__
- patientgraph
- ems-ui
### À virer
- couchdb
- browser
- resumed
- orchester
- indexer
- ems-retro
- warehouser
- mongo
- redis
- keys_manager