Skip to content

Commit 58331fb

Browse files
authored
docs: omop investigation (#1919)
1 parent 6db9888 commit 58331fb

File tree

10 files changed

+1681
-103
lines changed

10 files changed

+1681
-103
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Generated by Django 5.1.8 on 2026-02-09 14:32
2+
3+
import datetime
4+
import django.contrib.postgres.indexes
5+
import django.db.models.deletion
6+
from django.db import migrations, models
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
('clinicalcode', '0133_omop_codes'),
13+
]
14+
15+
operations = [
16+
migrations.AlterField(
17+
model_name='omop_codes',
18+
name='valid_end_date',
19+
field=models.DateField(blank=True, default=datetime.date(2099, 12, 31)),
20+
),
21+
migrations.CreateModel(
22+
name='OMOPRelationships',
23+
fields=[
24+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
25+
('relationship', models.CharField(max_length=64)),
26+
('valid_start_date', models.DateField(auto_now_add=True)),
27+
('valid_end_date', models.DateField(blank=True, default=datetime.date(2099, 12, 31))),
28+
('invalid_reason', models.CharField(blank=True, choices=[('D', 'Deprecated'), ('U', 'Upgraded')], default=None, max_length=1, null=True)),
29+
('code0', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='source_relationships', to='clinicalcode.omop_codes', to_field='code')),
30+
('code1', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='destination_relationships', to='clinicalcode.omop_codes', to_field='code')),
31+
],
32+
options={
33+
'indexes': [models.Index(fields=['code0'], name='clinicalcod_code0_i_9f4db1_idx'), models.Index(fields=['code1'], name='clinicalcod_code1_i_fbd524_idx'), models.Index(fields=['code0', 'code1'], name='clinicalcod_code0_i_634ab7_idx'), models.Index(fields=['code0', 'relationship', 'invalid_reason'], name='clinicalcod_code0_i_bea734_idx'), django.contrib.postgres.indexes.GinIndex(fields=['code0'], name='omop_e0_trgm_idx', opclasses=['gin_trgm_ops']), django.contrib.postgres.indexes.GinIndex(fields=['code0', 'relationship', 'invalid_reason'], name='omop_e0t_tgbt_idx', opclasses=['', 'gin_trgm_ops', ''])],
34+
'unique_together': {('code0', 'code1', 'relationship')},
35+
},
36+
),
37+
]

CodeListLibrary_project/clinicalcode/models/OMOP_CODES.py

Lines changed: 180 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from django.utils.translation import gettext_lazy as _
33
from django.contrib.postgres.indexes import GinIndex
44

5+
import datetime
6+
57
class StandardFlag(models.TextChoices):
68
"""
79
See `standard_concept` column of `CONCEPT` table found in `OHDSI docs`_.
@@ -11,6 +13,7 @@ class StandardFlag(models.TextChoices):
1113
STANDARD = 'S', _('Standard Concept')
1214
CLASSIFICATION = 'C', _('Classification Concept')
1315

16+
1417
class InvalidFlag(models.TextChoices):
1518
"""
1619
See `invalid_reason` column of `CONCEPT` table found in `OHDSI docs`_.
@@ -20,106 +23,181 @@ class InvalidFlag(models.TextChoices):
2023
DEPRECATED = 'D', _('Deprecated')
2124
UPGRADED = 'U', _('Upgraded')
2225

26+
2327
class OMOP_CODES(models.Model):
24-
"""
25-
Represents a standardised `OMOP`_ code from its Common Data Model, related to :model:`clinicalcode.CodingSystem`.
26-
27-
Version
28-
-------
29-
Vocabulary version: `v20250827`
30-
31-
Reference
32-
---------
33-
See `OHDSI Single-page docs`_.
34-
35-
Mapping
36-
----------
37-
| Attribute | Table->Column |
38-
|:---------------------|:---------------------------------|
39-
| `code` | `CONCEPT->concept_id` |
40-
| `description` | `CONCEPT->concept_name` |
41-
| `is_code` | `N/A` |
42-
| `is_valid` | `N/A` |
43-
| `standard_concept` | `CONCEPT->standard_concept` |
44-
| `coding_name` | `N/A` |
45-
| `coding_system_id` | `N/A` |
46-
| `domain_name` | `CONCEPT->domain_id` |
47-
| `class_name` | `CONCEPT->concept_class_id` |
48-
| `vocabulary_name` | `CONCEPT->vocabulary_id` |
49-
| `vocabulary_code` | `CONCEPT->concept_code` |
50-
| `vocabulary_version` | `VOCABULARY->vocabulary_version` |
51-
| `valid_start_date` | `CONCEPT->valid_start_date` |
52-
| `valid_end_date` | `CONCEPT->valid_end_date` |
53-
| `invalid_reason` | `CONCEPT->invalid_reason` |
54-
| `created` | `N/A` |
55-
| `modified` | `N/A` |
56-
57-
.. _OMOP: https://www.ohdsi.org/data-standardization/
58-
.. _OHDSI Single-page docs: https://www.ohdsi.org/web/wiki/doku.php?id=documentation:cdm:single-page
59-
"""
60-
id = models.BigAutoField(auto_created=True, primary_key=True)
61-
code = models.CharField(max_length=64, null=True, blank=True, unique=True, default='')
62-
description = models.CharField(max_length=256, null=True, blank=True, default='')
63-
is_code = models.BooleanField(null=False, default=True)
64-
is_valid = models.BooleanField(null=False, default=True)
65-
standard_concept = models.CharField(
66-
# 'S', 'C' or NULL
67-
null=True,
68-
blank=True,
69-
choices=StandardFlag.choices,
70-
default=None,
71-
max_length=1,
72-
)
73-
coding_name = models.CharField(max_length=256, null=True, blank=True, default='')
74-
coding_system = models.ForeignKey(
75-
'clinicalcode.CodingSystem',
76-
on_delete=models.SET_NULL,
77-
null=True,
78-
blank=True,
79-
default=None,
80-
related_name='omop_code'
81-
)
82-
domain_name = models.CharField(max_length=256, null=True, blank=True, default='')
83-
class_name = models.CharField(max_length=256, null=True, blank=True, default='')
84-
vocabulary_name = models.CharField(max_length=64, null=True, blank=True, default='')
85-
vocabulary_code = models.CharField(max_length=64, null=True, blank=True, default='')
86-
vocabulary_version = models.CharField(max_length=256, null=True, blank=True, default='')
87-
valid_start_date = models.DateField(null=True, blank=True)
88-
valid_end_date = models.DateField(null=True, blank=True)
89-
invalid_reason = models.CharField(
90-
# 'D', 'U' or NULL
91-
null=True,
92-
blank=True,
93-
choices=InvalidFlag.choices,
94-
default=None,
95-
max_length=1,
96-
)
97-
created = models.DateTimeField(auto_now_add=True, editable=True)
98-
modified = models.DateTimeField(auto_now_add=True, editable=True)
99-
100-
class Meta:
101-
ordering = ('id',)
102-
indexes = [
103-
models.Index(fields=['id']),
104-
models.Index(fields=['created']),
105-
GinIndex(
106-
name='omop_cd_trgm_idx',
107-
fields=['code'],
108-
opclasses=['gin_trgm_ops']
109-
),
110-
GinIndex(
111-
name='omop_desc_trgm_idx',
112-
fields=['description'],
113-
opclasses=['gin_trgm_ops']
114-
),
115-
GinIndex(
116-
name='omop_cs_trgm_idx',
117-
fields=['coding_name'],
118-
opclasses=['gin_trgm_ops']
119-
),
120-
GinIndex(
121-
name='omop_vscd_trgm_idx',
122-
fields=['vocabulary_code'],
123-
opclasses=['gin_trgm_ops']
124-
),
125-
]
28+
"""
29+
Represents a standardised `OMOP`_ code from its Common Data Model, related to :model:`clinicalcode.CodingSystem`.
30+
31+
Version
32+
-------
33+
Vocabulary version: `v20250827`
34+
35+
Reference
36+
---------
37+
See `OHDSI Single-page docs`_.
38+
39+
Mapping
40+
-------
41+
| Attribute | Table->Column |
42+
|:---------------------|:---------------------------------|
43+
| `code` | `CONCEPT->concept_id` |
44+
| `description` | `CONCEPT->concept_name` |
45+
| `is_code` | `N/A` |
46+
| `is_valid` | `N/A` |
47+
| `standard_concept` | `CONCEPT->standard_concept` |
48+
| `coding_name` | `N/A` |
49+
| `coding_system_id` | `N/A` |
50+
| `domain_name` | `CONCEPT->domain_id` |
51+
| `class_name` | `CONCEPT->concept_class_id` |
52+
| `vocabulary_name` | `CONCEPT->vocabulary_id` |
53+
| `vocabulary_code` | `CONCEPT->concept_code` |
54+
| `vocabulary_version` | `VOCABULARY->vocabulary_version` |
55+
| `valid_start_date` | `CONCEPT->valid_start_date` |
56+
| `valid_end_date` | `CONCEPT->valid_end_date` |
57+
| `invalid_reason` | `CONCEPT->invalid_reason` |
58+
| `created` | `N/A` |
59+
| `modified` | `N/A` |
60+
61+
.. _OMOP: https://www.ohdsi.org/data-standardization/
62+
.. _OHDSI Single-page docs: https://www.ohdsi.org/web/wiki/doku.php?id=documentation:cdm:single-page
63+
"""
64+
id = models.BigAutoField(auto_created=True, primary_key=True)
65+
code = models.CharField(max_length=64, null=True, blank=True, unique=True, default='')
66+
description = models.CharField(max_length=256, null=True, blank=True, default='')
67+
is_code = models.BooleanField(null=False, default=True)
68+
is_valid = models.BooleanField(null=False, default=True)
69+
standard_concept = models.CharField(
70+
# 'S', 'C' or NULL
71+
null=True,
72+
blank=True,
73+
choices=StandardFlag.choices,
74+
default=None,
75+
max_length=1,
76+
)
77+
coding_name = models.CharField(max_length=256, null=True, blank=True, default='')
78+
coding_system = models.ForeignKey(
79+
'clinicalcode.CodingSystem',
80+
on_delete=models.SET_NULL,
81+
null=True,
82+
blank=True,
83+
default=None,
84+
related_name='omop_code'
85+
)
86+
domain_name = models.CharField(max_length=256, null=True, blank=True, default='')
87+
class_name = models.CharField(max_length=256, null=True, blank=True, default='')
88+
vocabulary_name = models.CharField(max_length=64, null=True, blank=True, default='')
89+
vocabulary_code = models.CharField(max_length=64, null=True, blank=True, default='')
90+
vocabulary_version = models.CharField(max_length=256, null=True, blank=True, default='')
91+
valid_start_date = models.DateField(blank=True, null=True)
92+
valid_end_date = models.DateField(blank=True, default=datetime.date(2099,12,31))
93+
invalid_reason = models.CharField(
94+
# 'D', 'U' or NULL
95+
null=True,
96+
blank=True,
97+
choices=InvalidFlag.choices,
98+
default=None,
99+
max_length=1,
100+
)
101+
created = models.DateTimeField(auto_now_add=True, editable=True)
102+
modified = models.DateTimeField(auto_now_add=True, editable=True)
103+
104+
class Meta:
105+
ordering = ('id',)
106+
indexes = [
107+
models.Index(fields=['id']),
108+
models.Index(fields=['created']),
109+
GinIndex(
110+
name='omop_cd_trgm_idx',
111+
fields=['code'],
112+
opclasses=['gin_trgm_ops']
113+
),
114+
GinIndex(
115+
name='omop_desc_trgm_idx',
116+
fields=['description'],
117+
opclasses=['gin_trgm_ops']
118+
),
119+
GinIndex(
120+
name='omop_cs_trgm_idx',
121+
fields=['coding_name'],
122+
opclasses=['gin_trgm_ops']
123+
),
124+
GinIndex(
125+
name='omop_vscd_trgm_idx',
126+
fields=['vocabulary_code'],
127+
opclasses=['gin_trgm_ops']
128+
),
129+
]
130+
131+
132+
class OMOPRelationships(models.Model):
133+
"""
134+
Represents the edges, or `relationships`_, between OMOP concepts stored in :model:`clinicalcode.OMOP_CODES`.
135+
136+
Version
137+
-------
138+
Vocabulary version: `v20250827`
139+
140+
Reference
141+
---------
142+
See:
143+
- `Concept Relationship Data Model Conventions`_.
144+
- `CONCEPT_RELATIONSHIP table`_.
145+
146+
Mapping
147+
-------
148+
| Attribute | Table->Column |
149+
|:---------------------|:-----------------------------------------|
150+
| `code0_id` | `CONCEPT_RELATIONSHIP->concept_id_1` |
151+
| `code1_id` | `CONCEPT_RELATIONSHIP->concept_id_2` |
152+
| `relationship` | `CONCEPT_RELATIONSHIP->relationship_id` |
153+
| `valid_start_date` | `CONCEPT_RELATIONSHIP->valid_start_date` |
154+
| `valid_end_date` | `CONCEPT_RELATIONSHIP->valid_end_date` |
155+
| `invalid_reason` | `CONCEPT_RELATIONSHIP->invalid_reason` |
156+
157+
.. _relationships: https://www.ohdsi.org/web/wiki/doku.php?id=documentation:cdm:concept_relationship
158+
.. _Concept Relationship Data Model Conventions: https://ohdsi.github.io/CommonDataModel/dataModelConventions.html#Concept_Relationships
159+
.. _CONCEPT_RELATIONSHIP table: https://www.ohdsi.org/web/wiki/doku.php?id=documentation:cdm:concept_relationship
160+
"""
161+
id = models.BigAutoField(auto_created=True, primary_key=True)
162+
code0 = models.ForeignKey(
163+
to='OMOP_CODES',
164+
to_field='code',
165+
related_name=f'source_relationships',
166+
on_delete=models.CASCADE,
167+
)
168+
code1 = models.ForeignKey(
169+
to='OMOP_CODES',
170+
to_field='code',
171+
related_name=f'destination_relationships',
172+
on_delete=models.CASCADE,
173+
)
174+
relationship = models.CharField(max_length=64)
175+
valid_start_date = models.DateField(blank=True, auto_now_add=True)
176+
valid_end_date = models.DateField(blank=True, default=datetime.date(2099,12,31))
177+
invalid_reason = models.CharField(
178+
# 'D', 'U' or NULL
179+
null=True,
180+
blank=True,
181+
choices=InvalidFlag.choices,
182+
default=None,
183+
max_length=1,
184+
)
185+
186+
class Meta:
187+
unique_together = ('code0', 'code1', 'relationship',)
188+
indexes = [
189+
models.Index(fields=['code0']),
190+
models.Index(fields=['code1']),
191+
models.Index(fields=['code0', 'code1']),
192+
models.Index(fields=['code0', 'relationship', 'invalid_reason']),
193+
GinIndex(
194+
name='omop_e0_trgm_idx',
195+
fields=['code0'],
196+
opclasses=['gin_trgm_ops']
197+
),
198+
GinIndex(
199+
name='omop_e0t_tgbt_idx',
200+
fields=['code0', 'relationship', 'invalid_reason'],
201+
opclasses=['', 'gin_trgm_ops', '']
202+
),
203+
]

CodeListLibrary_project/clinicalcode/models/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
from .ATCDDD_CODES import ATCDDD_CODES
5757
from .ICD10CA_CODES import ICD10CA_CODES
5858
from .ICD10CM_CODES import ICD10CM_CODES
59-
from .OMOP_CODES import OMOP_CODES
59+
from .OMOP_CODES import OMOP_CODES, OMOPRelationships
6060

6161
# need to restore EMIS/Vision when deploy. to prod.
6262
#from .EMIS_CODES import EMIS_CODES

0 commit comments

Comments
 (0)