En résumé
Deux générateurs écrivent l’essentiel d’un pipeline ETL vers OMOP. Générer depuis les schémas déduit les scripts de chargement — patients, séjours, événements — du mapping de schéma de la source et de celui de la cible. L’onglet Vocabulaire tire d’un projet d’alignement les scripts qui chargent la traduction de vos codes locaux. Les deux produisent des scripts ordinaires, que vous pouvez modifier : Linkr repère ceux que vous avez retouchés avant de les régénérer.
Deux générateurs, une seule chaîne
Écrire à la main la conversion d’un dossier patient vers OMOP, c’est des centaines de lignes de SQL qui répètent ce que Linkr sait déjà : le mapping de schéma de la base source dit où sont ses patients, ses séjours et ses événements, celui de la base cible dit où OMOP les range, et un projet d’alignement dit à quel concept standard correspond chacun de vos codes.
Chaque générateur écrit ses fichiers dans l’onglet Scripts du pipeline, numérotés pour s’intercaler : le vocabulaire d’abord, puis les patients, les séjours, les événements, et l’élagage du vocabulaire en dernier. Un script écrit à la main s’insère entre eux par son numéro.
Scripts du pipeline, dans l'ordre d'exécution
- 00_vocabulary.sqlCharge le vocabulaire alignéOnglet Vocabulaire
- 10_person.sqlPatients et décèsGénérer depuis les schémas
- 20_visit_occurrence.sqlHospitalisations et lieux de soinsGénérer depuis les schémas
- 30_visit_detail.sqlSéjours en unitéGénérer depuis les schémas
- 35_fix_units.sqlCorrection locale des unitésÉcrit à la main
- 50_measurement.sqlMesuresGénérer depuis les schémas
- 51_drug_exposure.sqlMédicamentsGénérer depuis les schémas
- 99_prune_vocabulary.sqlRetire les concepts inutilisésOnglet Vocabulaire
Générer les scripts de chargement OMOP
Ce qu’il faut avant
Les deux bases du pipeline doivent avoir un mapping de schéma :
- la source, pour que Linkr sache lire ses patients, ses séjours et ses tables d’événements ;
- la cible, pour qu’il sache où les écrire. Une base OMOP vide créée avec Nouveau depuis un schéma l’a d’office — voir Bases de données.
Sans l’un ou l’autre, la fenêtre le dit (« La base source n’a pas de mapping de schéma », « La base cible n’a pas de mapping de schéma : créez-la depuis un schéma OMOP ») et ne propose rien.
Ouvrir le générateur
Dans l’onglet Scripts, la barre de l’explorateur de fichiers a trois boutons : Nouveau fichier, Importer des fichiers, et la baguette Générer depuis les schémas. Elle ouvre la fenêtre Générer les scripts de chargement OMOP.
Générer les scripts de chargement OMOP
Un script par classe de la base source, écrit à partir de son mapping de schéma et de celui de la cible. Les scripts s'exécutent tels quels et restent modifiables.
- La table Scores n'est pas chargée : choisissez une table cible.
La fenêtre se lit de haut en bas : deux réglages, les tables d’événements à répartir, puis la liste des scripts qui seront écrits. Écrire N scripts les crée dans l’onglet Scripts, sans rien exécuter.
Les réglages
- Concepts — comment un code source devient un
concept_idOMOP :
| Option | Ce que fait le script | Quand la choisir |
|---|---|---|
| Vocabulaire du pipeline (Maps to) | Cherche le code dans concept de la cible, puis suit la relation Maps to de concept_relationship. Un code aligné sur deux concepts standard donne deux lignes, comme le demandent les conventions OMOP. | Le vocabulaire est chargé sous la forme CONCEPT + CONCEPT_RELATIONSHIP — la représentation OMOP actuelle. |
| Vocabulaire du pipeline (source_to_concept_map) | Cherche le code dans source_to_concept_map de la cible. | Le vocabulaire est chargé sous la forme SOURCE_TO_CONCEPT_MAP. |
| Identifiants source, déjà OMOP | Reprend tels quels les identifiants de la source. | La source est déjà codée dans les vocabulaires OMOP ; pas besoin de projet d’alignement. |
Les deux premières options lisent ce que le script de vocabulaire a écrit dans la cible (voir plus bas) : c’est pourquoi 00_vocabulary.sql passe en premier. Un code qui ne trouve pas de correspondance reçoit le concept 0, comme le veut OMOP pour « non aligné ». Le réglage proposé d’emblée est Identifiants source, déjà OMOP quand aucun projet d’alignement n’est rattaché au pipeline, le vocabulaire du pipeline sinon : vérifiez qu’il correspond à la représentation choisie dans l’onglet Vocabulaire.
-
Concept de type — la valeur écrite dans chaque colonne
*_type_concept_id, qui dit d’où vient l’enregistrement.32817signifie EHR, le dossier patient informatisé. -
Tables d’événements et de médicaments — une ligne par table d’événements de la source, telle que son mapping la nomme (« Biologie », « Administrations »…), avec la table OMOP où la charger. Linkr en propose une : la table cible qui porte le même nom dans le mapping de la cible, ou
drug_exposurepour une table marquée Médicament. À défaut, la ligne démarre sur Ne pas charger, qui l’écarte ; un avertissement le rappelle sous la liste des scripts.
Ce que produit chaque script
Un script par classe de la source, dans l’ordre où OMOP les attend :
| Script | Contenu |
|---|---|
10_person.sql | Les patients, avec le sexe traduit dans les codes de la cible ; les décès dans death quand la source a une date de décès. |
20_visit_occurrence.sql | Les hospitalisations, et les services qu’elles citent dans care_site. |
30_visit_detail.sql | Les séjours en unité. |
40_note.sql | Les documents textuels, si la source en a. |
50_…, 51_… | Une table d’événements chacun, numérotés dans l’ordre de la liste. |
Chaque script vide d’abord sa table (TRUNCATE), puis la remplit d’un seul INSERT … SELECT qui lit la relation standard de la source — celle que son mapping définit, recopiée en tête de requête. Relancer le pipeline recharge donc la table, sans doublons.
-- Generated by Linkr from the source schema "Réa CHU — export": visit → visit_occurrence.
-- Edit freely: regenerating asks before overwriting an edited script.
-- linkr-generated: 5c1e09a4
TRUNCATE target.visit_occurrence;
INSERT INTO target.visit_occurrence (visit_occurrence_id, person_id, visit_concept_id, visit_start_date, visit_start_datetime, visit_end_date, visit_end_datetime, visit_type_concept_id, care_site_id)
WITH linkr_visit AS NOT MATERIALIZED (
-- la relation « séjours » du mapping de la source
)
SELECT
s.visit_id AS visit_occurrence_id,
s.patient_id AS person_id,
0 AS visit_concept_id,
CAST(s.start_datetime AS DATE) AS visit_start_date,
s.start_datetime AS visit_start_datetime,
COALESCE(CAST(s.end_datetime AS DATE), CAST(s.start_datetime AS DATE)) AS visit_end_date,
s.end_datetime AS visit_end_datetime,
32817 AS visit_type_concept_id,
s.care_site_id AS care_site_id
FROM linkr_visit s;
Au passage, le générateur applique les règles d’OMOP que l’on oublie facilement :
- les dates — une colonne
*_dateest tirée de son*_datetime; une date de fin inconnue devient la date de début, quand la colonne est obligatoire ; - les colonnes obligatoires — un
*_concept_idque rien ne remplit reçoit0; toute autre colonneNOT NULLque la source ne sait pas remplir reçoitNULLet un commentaire-- TODO(etl): …qui le signale, plutôt qu’une valeur inventée ; - les identifiants — un événement sans identifiant propre dans la source est numéroté à chaque exécution, et un
TODOrappelle que ces clés changeront d’un chargement à l’autre.
Relisez les TODO avant d'exécuter
Un TODO(etl) marque ce que le générateur n’a pas pu décider. Le script s’exécute quand même, mais une colonne obligatoire à NULL fera échouer l’insertion si la base applique la contrainte. Complétez la source ou le script, puis relancez.
Sous la liste des scripts, des avertissements en orange signalent ce qui n’a pas pu être écrit : une table absente de la DDL de la cible (seules les colonnes que son mapping nomme sont alors écrites), une classe de la source que le mapping de la cible ne place nulle part, une table d’événements laissée à Ne pas charger.
Régénérer sans perdre ses modifications
Un schéma évolue, un mapping se corrige : on régénère. Chaque script généré porte dans son en-tête une ligne -- linkr-generated: suivie d’une empreinte de son contenu, qui permet à Linkr de savoir si quelqu’un l’a modifié depuis. La liste des scripts dit ce qui va se passer pour chacun :
- nouveau — le fichier n’existe pas encore, il sera créé ;
- régénéré — il existe et n’a pas été touché depuis sa génération : il sera remplacé ;
- modifié — écraser — il a été modifié à la main, ou un script écrit à la main porte déjà ce nom. Il n’est pas touché, sauf si vous cochez la case.
Retoucher un script généré est donc sans risque : vos ajustements survivent à la régénération tant que vous ne cochez pas la case.
Générer le vocabulaire depuis un projet d’alignement
Aligner des codes locaux sur des concepts standard se fait dans un projet d’alignement de concepts — une interface faite pour ça, avec suggestions et évaluation. L’onglet Vocabulaire du pipeline sait lire ce travail et le transformer en SQL qui charge ces correspondances dans la base cible.
Sélectionner les statuts à exporter
/Total à exporter: 1 362 alignements
Fichiers à écrire
À gauche, ce qui entre :
- Depuis un projet d’alignement de concepts — le projet dont lire les correspondances.
- Sélectionner les statuts à exporter — les alignements retenus selon leur statut (Approuvé, Non vérifié…), avec pour les approuvés la règle à appliquer : Au moins une approbation, Plus d’approbations que de rejets ou Aucun rejet.
- Inclure tous les concepts sources — charge aussi les codes locaux qu’aucun alignement retenu ne couvre. Sans eux, un code non aligné n’a pas de
source_concept_idà écrire dans OMOP.
À droite, ce qui sort :
- Représentation du vocabulaire —
CONCEPT+CONCEPT_RELATIONSHIP, la représentation OMOP actuelle ;SOURCE_TO_CONCEPT_MAP, l’ancienne ; ou les deux. - Fichiers à écrire — l’export des alignements en CSV, le script de vocabulaire
00_vocabulary.sql, qui copie le vocabulaire de référence et vos concepts locaux dans la cible, et le script d’élagage99_prune_vocabulary.sql, qui l’allège ensuite : il ne garde que les concepts que les tables OMOP utilisent vraiment, leurs ancêtres et ce qui s’y rattache.
Sous chaque fichier déjà présent, une ligne dit où il en est : Déjà à jour s’il est identique à ce qui serait généré, Diffère de ce qui serait généré s’il a été modifié depuis — dans les deux cas, il faut cocher pour le réécrire. On évite ainsi d’écraser un ajustement sans s’en apercevoir.
Le projet d'alignement doit avoir son vocabulaire de référence
Sans base de vocabulaire ATHENA importée dans l’onglet Concepts cibles du projet d’alignement, il n’y a rien à traduire. Voir Concepts cibles.
Le CSV se régénère, les scripts se versionnent
Les scripts de vocabulaire sont des fichiers du pipeline comme les autres, versionnés avec lui. L’export CSV des alignements, lui, est une donnée dérivée, exclue du versioning par défaut : après un clonage, l’onglet signale qu’il manque et Générer le reconstruit — sauf à le marquer pour le versioning, pour qu’il voyage avec le pipeline.
Pour aller plus loin
- Pipelines ETL — ce qu’est un pipeline, et les rôles source, cible et vocabulaire.
- Construire et exécuter un pipeline — exécuter les scripts générés, puis contrôler ce qui a été chargé.
- Schémas — les mappings dont le générateur tire les scripts de chargement.
- Alignement de concepts — produire les correspondances que le vocabulaire consomme.