Linkr
Home Resources Tools Documentation Blog Demo
FR
  • What is Linkr?
  • Deployment modes
  • Quick start
  • Local install
  • With Docker
  • Manual install
  • Client-only
  • Your first project
  • Linkr in a clinical data warehouse
  • Workspaces and projects
  • The data pipeline
  • Entities and sharing
  • Versioning and collaboration
  • Overview
  • Projects
  • Wiki
  • Plugins
  • Members and roles
  • Settings
  • Schemas
  • Getting and exploring
  • Mapping
  • Databases
  • Derived sub-databases
  • Data quality
  • Data catalog
  • Build and publish
  • Anonymize
  • SQL script collections
  • ETL pipelines
  • Building and running
  • Generating the scripts
  • Overview
  • Mapping projects
  • Global view
  • Target concepts
  • Mapping editor
  • Suggestions
  • AI agent
  • Evaluation
  • Export
  • Overview
  • Databases
  • Concepts
  • Cohorts
  • Building
  • Results, SQL and report
  • Patient data
  • Pipeline
  • Datasets
  • IDE
  • Web apps
  • Versioning
  • Overview
  • Tabs and widgets
  • Built-in widgets
  • Analysis widgets
  • Control charts (SPC)
  • Surveys and eCRF
  • R and Python code
  • Filters, settings and export
  • Overview
  • Presentation mode
  • Exporting a report
  • Agents
  • MCP server
  • Skills
  • Import and export
  • Git versioning
  • Community catalog
  • Publishing content
  • Production install
  • Configuration
  • Authentication and permissions
  • Files on the server
  • Backup and restore
  • Contributing code
  • Glossary
  • Keyboard shortcuts
  • Release notes
Documentation Data warehouse Generating the scripts

Generating the scripts

Letting Linkr write an ETL pipeline's scripts: the OMOP load scripts worked out from the source's and the target's schema mappings, and the vocabulary scripts drawn from a concept mapping project.

Summary

Two generators write most of an ETL pipeline to OMOP. Generate from the schemas works out the load scripts — patients, stays, events — from the source’s schema mapping and the target’s. The Vocabulary tab draws from a mapping project the scripts that load the translation of your local codes. Both produce ordinary scripts, which you can edit: Linkr spots the ones you touched before regenerating them.

Client Available in client-only mode — runs entirely in the browser, no backend. Backend Available with the FastAPI backend.

Two generators, one chain

Writing by hand the conversion of a patient record system to OMOP means hundreds of lines of SQL that repeat what Linkr already knows: the source database’s schema mapping says where its patients, stays and events are, the target database’s says where OMOP puts them, and a mapping project says which standard concept each of your codes stands for.

Each generator writes its files into the pipeline’s Scripts tab, numbered to interleave: the vocabulary first, then patients, stays, events, and the vocabulary prune last. A hand-written script slots in between by its number.

The pipeline's scripts, in run order

  1. 00_vocabulary.sqlLoads the mapped vocabularyVocabulary tab
  2. 10_person.sqlPatients and deathsGenerate from the schemas
  3. 20_visit_occurrence.sqlHospital stays and care sitesGenerate from the schemas
  4. 30_visit_detail.sqlUnit staysGenerate from the schemas
  5. 35_fix_units.sqlLocal unit fixWritten by hand
  6. 50_measurement.sqlMeasurementsGenerate from the schemas
  7. 51_drug_exposure.sqlDrugsGenerate from the schemas
  8. 99_prune_vocabulary.sqlDrops unused conceptsVocabulary tab

Generating the OMOP load scripts

What you need first

Both of the pipeline’s databases need a schema mapping:

  • the source, so Linkr knows how to read its patients, stays and event tables;
  • the target, so it knows where to write them. An empty OMOP database created with New from schema has one from the start — see Databases.

Without either, the dialog says so (“The source database has no schema mapping”, “The target database has no schema mapping: create it from an OMOP schema”) and offers nothing.

Opening the generator

In the Scripts tab, the file explorer’s bar has three buttons: New file, Upload files, and the wand Generate from the schemas. It opens the Generate the OMOP load scripts dialog.

demo.linkr.interhop.org
Generate from the schemas

Generate the OMOP load scripts

One script per class of the source database, written from its schema mapping and the target's. The scripts run as they are and stay editable.

Concepts
Type concept (32817 = EHR)32817
Event and drug tables
Laboratory
Vital signs
AdministrationsDrug
Scores
Scripts
10_person.sqlregenerated
20_visit_occurrence.sqlregenerated
30_visit_detail.sqledited — overwrite
50_measurement.sqlregenerated
51_measurement.sqlnew
52_drug_exposure.sqlnew
  • Table Scores is not loaded: pick a target table.
CancelWrite 5 scripts
The “Generate the OMOP load scripts” dialog. Pick a target table for “Scores”, or “Do not load” for another row: the script list and the button's count follow. Tick “edited — overwrite” to include the hand-edited script.

The dialog reads top to bottom: two settings, the event tables to place, then the list of scripts to be written. Write N scripts creates them in the Scripts tab, without running anything.

The settings

  • Concepts — how a source code becomes an OMOP concept_id:
OptionWhat the script doesWhen to choose it
Pipeline vocabulary (Maps to)Looks the code up in the target’s concept, then follows the Maps to relationship in concept_relationship. A code mapped to two standard concepts gives two rows, as the OMOP conventions ask.The vocabulary is loaded as CONCEPT + CONCEPT_RELATIONSHIP — the current OMOP representation.
Pipeline vocabulary (source_to_concept_map)Looks the code up in the target’s source_to_concept_map.The vocabulary is loaded as SOURCE_TO_CONCEPT_MAP.
Source ids, already OMOPTakes the source’s ids as they are.The source is already coded in the OMOP vocabularies; no mapping project needed.

The first two options read what the vocabulary script wrote into the target (see below): that is why 00_vocabulary.sql comes first. A code with no match gets concept 0, which is how OMOP says “unmapped”. The setting offered at first is Source ids, already OMOP when no mapping project is attached to the pipeline, the pipeline vocabulary otherwise: check that it matches the representation chosen in the Vocabulary tab.

  • Type concept — the value written into every *_type_concept_id column, which says where the record comes from. 32817 means EHR, the electronic health record.

  • Event and drug tables — one row per event table of the source, as its mapping names it (“Laboratory”, “Administrations”…), with the OMOP table to load it into. Linkr offers one: the target table bearing the same name in the target’s mapping, or drug_exposure for a table marked Drug. Failing that, the row starts on Do not load, which leaves it out; a warning says so under the script list.

What each script produces

One script per class of the source, in the order OMOP expects them:

ScriptContents
10_person.sqlThe patients, with gender translated into the target’s codes; deaths into death when the source has a date of death.
20_visit_occurrence.sqlThe hospitalizations, and the services they name into care_site.
30_visit_detail.sqlThe unit stays.
40_note.sqlThe text documents, if the source has any.
50_…, 51_…One event table each, numbered in the order of the list.

Each script first empties its table (TRUNCATE), then fills it with a single INSERT … SELECT reading the source’s standard relation — the one its mapping defines, copied at the head of the query. Re-running the pipeline therefore reloads the table, with no duplicates.

-- Generated by Linkr from the source schema "ICU hospital — 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 (
  -- the source mapping's "stays" relation
)
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;

Along the way, the generator applies the OMOP rules that are easy to forget:

  • dates — a *_date column is taken from its *_datetime; an unknown end date becomes the start date, when the column is required;
  • required columns — a *_concept_id nothing fills gets 0; any other NOT NULL column the source cannot fill gets NULL and a -- TODO(etl): … comment saying so, rather than an invented value;
  • ids — an event with no id of its own in the source is numbered on each run, and a TODO points out that those keys will change from one load to the next.

Read the TODOs before running

A TODO(etl) marks what the generator could not decide. The script runs anyway, but a required column left NULL makes the insert fail if the database enforces the constraint. Complete the source or the script, then run again.

Under the script list, orange warnings flag what could not be written: a table missing from the target’s DDL (only the columns its mapping names are then written), a class of the source the target’s mapping places nowhere, an event table left on Do not load.

Regenerating without losing your edits

A schema evolves, a mapping gets corrected: you regenerate. Every generated script carries in its header a -- linkr-generated: line followed by a fingerprint of its contents, which lets Linkr know whether anyone edited it since. The script list says what will happen to each:

  • new — the file does not exist yet, it will be created;
  • regenerated — it exists and has not been touched since it was generated: it will be replaced;
  • edited — overwrite — it was edited by hand, or a hand-written script already has that name. It is left alone, unless you tick the box.

Adjusting a generated script is therefore safe: your changes survive regeneration as long as you leave the box unticked.

Generating the vocabulary from a mapping project

Aligning local codes onto standard concepts happens in a concept mapping project — an interface built for it, with suggestions and evaluation. The pipeline’s Vocabulary tab can read that work and turn it into SQL that loads those correspondences into the target database.

demo.linkr.interhop.org
From a concept mapping projectICU — local codes

Select statuses to export

/
At least one approvalMore approvals than rejectionsNo rejections
Include all source concepts158Also loads the source concepts no selected mapping covers, as concepts with no 'Maps to'. Without them an unmapped local code has no source_concept_id to write into the CDM.

Total to export: 1,362 mappings

Vocabulary representation

Files to write

Mapping export (CSV)mapping/concept.csv + mapping/concept_relationship.csv
Vocabulary script00_vocabulary.sqlDiffers from what would be generated — tick to overwrite
Prune script99_prune_vocabulary.sqlAlready up to date — tick to rewrite anyway
Generate
The Vocabulary tab: on the left the mapping project and the statuses kept, on the right the representation and the files to write. Switch the representation: the CSV files to write change with it.

On the left, what goes in:

  • From a concept mapping project — the project whose correspondences to read.
  • Select statuses to export — the mappings kept by status (Approved, Unchecked…), with the rule to apply to approved ones: At least one approval, More approvals than rejections or No rejections.
  • Include all source concepts — also loads the local codes no kept mapping covers. Without them, an unmapped code has no source_concept_id to write into OMOP.

On the right, what comes out:

  • Vocabulary representation — CONCEPT + CONCEPT_RELATIONSHIP, the current OMOP representation; SOURCE_TO_CONCEPT_MAP, the legacy one; or both.
  • Files to write — the CSV mapping export, the vocabulary script 00_vocabulary.sql, which copies the reference vocabulary and your local concepts into the target, and the prune script 99_prune_vocabulary.sql, which slims it down afterwards: it keeps only the concepts the OMOP tables actually use, their ancestors and what relates to them.

Under each file already present, a line says where it stands: Already up to date when it is identical to what would be generated, Differs from what would be generated when it was edited since — either way, you tick it to rewrite it. That way an adjustment is not overwritten unnoticed.

The mapping project needs its reference vocabulary

With no ATHENA vocabulary database imported in the mapping project’s Target concepts tab, there is nothing to translate. See Target concepts.

The CSV is regenerated, the scripts are versioned

The vocabulary scripts are pipeline files like any other, versioned with it. The CSV mapping export, on the other hand, is derived data, excluded from versioning by default: after a clone, the tab says it is missing and Generate rebuilds it — unless you mark it for versioning, so it travels with the pipeline.

Going further

  • ETL pipelines — what a pipeline is, and the source, target and vocabulary roles.
  • Building and running a pipeline — running the generated scripts, then checking what was loaded.
  • Schemas — the mappings the generator draws the load scripts from.
  • Concept mapping — producing the correspondences the vocabulary consumes.
PreviousBuilding and runningNextOverview

Product

  • Home
  • Demo

Resources

  • Documentation
  • Resources
  • Tools
  • Blog

Community

  • Framagit source code
  • Github source code

About

  • InterHop.org
  • Contact

2021–2026 InterHop — CC BY-NC-SA 4.0 (site) · GPLv3 (software)