Skip to content

Commit a82f85d

Browse files
committed
Additional multiomics types
1 parent 14da314 commit a82f85d

5 files changed

Lines changed: 289 additions & 4 deletions

File tree

colocus/api/serializers.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,34 @@ class Meta:
7777
fields = ('ens_id', 'chrom', 'start', 'end')
7878

7979

80+
class ProteinSerializer(drf_serializers.ModelSerializer):
81+
"""
82+
A protein is a biological molecule encoded by a gene. Proteins are represented by Ensembl Protein IDs, which are
83+
stable identifiers from the Ensembl database.
84+
"""
85+
class Meta:
86+
model = models.Protein
87+
fields = ('ens_id', 'chrom', 'start', 'end')
88+
89+
class MethylProbeSerializer(drf_serializers.ModelSerializer):
90+
"""
91+
A methylation probe is a specific site in the genome where DNA methylation is measured. Methylation probes are
92+
represented by their probe IDs, e.g. "cg00000029".
93+
"""
94+
class Meta:
95+
model = models.MethylProbe
96+
fields = ('uuid', 'chrom', 'pos')
97+
98+
99+
class MetaboliteSerializer(drf_serializers.ModelSerializer):
100+
"""
101+
A metabolite is a small molecule that is involved in metabolism. Metabolites are represented by their UUIDs.
102+
"""
103+
class Meta:
104+
model = models.Metabolite
105+
fields = ('uuid', 'name')
106+
107+
80108
class PhenotypeSerializer(drf_serializers.ModelSerializer):
81109
"""
82110
A phenotype is a type of trait. These are usually human diseases or measurements of human traits, such as "BMI" or
@@ -97,10 +125,14 @@ class TraitSerializer(NonNullModelSerializer):
97125
gene = GeneSerializer(read_only=True)
98126
exon = ExonSerializer(read_only=True)
99127
phenotype = PhenotypeSerializer(read_only=True)
128+
metabolite = MetaboliteSerializer(read_only=True)
129+
protein = ProteinSerializer(read_only=True)
130+
methyl_probe = MethylProbeSerializer(read_only=True)
131+
100132

101133
class Meta:
102134
model = models.Trait
103-
fields = ('uuid', 'biomarker_type', 'gene', 'exon', 'phenotype')
135+
fields = ('uuid', 'biomarker_type', 'gene', 'exon', 'phenotype', 'metabolite', 'protein', 'methyl_probe')
104136

105137

106138
class StudySerializer(drf_serializers.ModelSerializer):

colocus/api/views.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ def get_queryset(self):
260260
'signal1__analysis__trait__gene', 'signal2__analysis__trait__gene',
261261
'signal1__analysis__trait__exon', 'signal2__analysis__trait__exon',
262262
'signal1__analysis__trait__phenotype', 'signal2__analysis__trait__phenotype',
263+
'signal1__analysis__trait__metabolite', 'signal2__analysis__trait__metabolite',
264+
'signal1__analysis__trait__protein', 'signal2__analysis__trait__protein',
265+
'signal1__analysis__trait__methyl_probe', 'signal2__analysis__trait__methyl_probe',
263266
'signal1__analysis__study', 'signal2__analysis__study',
264267
'signal1__analysis__publication', 'signal2__analysis__publication',
265268
'signal1__analysis__dataset', 'signal2__analysis__dataset',
@@ -309,6 +312,9 @@ def get_queryset(self):
309312
'signal1__analysis__trait__gene', 'signal2__analysis__trait__gene',
310313
'signal1__analysis__trait__exon', 'signal2__analysis__trait__exon',
311314
'signal1__analysis__trait__phenotype', 'signal2__analysis__trait__phenotype',
315+
'signal1__analysis__trait__metabolite', 'signal2__analysis__trait__metabolite',
316+
'signal1__analysis__trait__protein', 'signal2__analysis__trait__protein',
317+
'signal1__analysis__trait__methyl_probe', 'signal2__analysis__trait__methyl_probe',
312318
'signal1__analysis__study', 'signal2__analysis__study',
313319
'signal1__analysis__publication', 'signal2__analysis__publication',
314320
'signal1__analysis__dataset', 'signal2__analysis__dataset',
@@ -454,6 +460,7 @@ class FinemappedSignalListView(generics.ListAPIView):
454460
"""
455461
queryset = models.FineMappedSignal.objects.select_related(
456462
'analysis', 'analysis__trait', 'analysis__trait__gene', 'analysis__trait__exon', 'analysis__study',
463+
'analysis__trait__metabolite', 'analysis__trait__protein', 'analysis__trait__methyl_probe',
457464
'analysis__ld', 'analysis__dataset', 'analysis__publication', 'analysis__trait__phenotype',
458465
'lead_variant')
459466
serializer_class = serializers.FinemappedSignalSerializer
@@ -592,7 +599,8 @@ class MarginalAnalysisListView(generics.ListAPIView):
592599
"""
593600

594601
queryset = models.MarginalAnalysis.objects.select_related(
595-
'dataset', 'trait', 'trait__gene', 'trait__exon', 'trait__phenotype', 'study', 'publication', 'ld')
602+
'dataset', 'trait', 'trait__gene', 'trait__exon', 'trait__phenotype', 'trait__protein', 'trait__methyl_probe',
603+
'trait__metabolite', 'study', 'publication', 'ld')
596604
serializer_class = serializers.MarginalAnalysisSerializer
597605

598606

@@ -613,13 +621,13 @@ class TraitListView(generics.ListAPIView):
613621
A phenotype may be any non-molecular measured trait, such as height, BMI, T2D affection status, etc.
614622
"""
615623

616-
queryset = models.Trait.objects.select_related('gene', 'exon', 'phenotype')
624+
queryset = models.Trait.objects.select_related('gene', 'exon', 'phenotype', 'metabolite', 'protein', 'methyl_probe')
617625
serializer_class = serializers.TraitSerializer
618626

619627

620628
class TraitDetailView(generics.RetrieveAPIView):
621629
lookup_field = 'uuid'
622-
queryset = models.Trait.objects.select_related('gene', 'exon', 'phenotype')
630+
queryset = models.Trait.objects.select_related('gene', 'exon', 'phenotype', 'metabolite', 'protein', 'methyl_probe')
623631
serializer_class = serializers.TraitSerializer
624632

625633

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# Generated by Django 4.2.9 on 2025-11-06 15:55
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
dependencies = [
9+
("core", "0007_colocresultwithorphans"),
10+
]
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name="Metabolite",
15+
fields=[
16+
(
17+
"id",
18+
models.BigAutoField(
19+
auto_created=True,
20+
primary_key=True,
21+
serialize=False,
22+
verbose_name="ID",
23+
),
24+
),
25+
(
26+
"uuid",
27+
models.TextField(
28+
db_index=True,
29+
help_text="ID of the metabolite, e.g. HMDB0000122 or CHEBI:17234",
30+
unique=True,
31+
),
32+
),
33+
(
34+
"name",
35+
models.TextField(
36+
help_text="Name of the metabolite, e.g. '2,6-Diethylpyrazine'"
37+
),
38+
),
39+
],
40+
),
41+
migrations.CreateModel(
42+
name="MethylProbe",
43+
fields=[
44+
(
45+
"id",
46+
models.BigAutoField(
47+
auto_created=True,
48+
primary_key=True,
49+
serialize=False,
50+
verbose_name="ID",
51+
),
52+
),
53+
(
54+
"uuid",
55+
models.TextField(
56+
db_index=True,
57+
help_text="ID of the methylation probe, e.g. cg00000029",
58+
unique=True,
59+
),
60+
),
61+
("chrom", models.TextField(help_text="Chromosome", null=True)),
62+
(
63+
"pos",
64+
models.PositiveIntegerField(
65+
help_text="Position of the methylation probe", null=True
66+
),
67+
),
68+
],
69+
),
70+
migrations.AlterField(
71+
model_name="dataset",
72+
name="analysis_type",
73+
field=models.TextField(
74+
choices=[
75+
("GWAS", "GWAS"),
76+
("eQTL", "eQTL"),
77+
("mQTL", "mQTL"),
78+
("metabQTL", "metabQTL"),
79+
("pQTL", "pQTL"),
80+
],
81+
help_text="Type of association analysis - GWAS, eQTL, pQTL, ATAC-seq, methylation, etc.",
82+
),
83+
),
84+
migrations.AlterField(
85+
model_name="marginalanalysis",
86+
name="analysis_type",
87+
field=models.TextField(
88+
choices=[
89+
("GWAS", "GWAS"),
90+
("eQTL", "eQTL"),
91+
("mQTL", "mQTL"),
92+
("metabQTL", "metabQTL"),
93+
("pQTL", "pQTL"),
94+
],
95+
help_text="Type of association analysis - GWAS, eQTL, pQTL, ATAC-seq, methylation, etc.",
96+
),
97+
),
98+
migrations.CreateModel(
99+
name="Protein",
100+
fields=[
101+
(
102+
"id",
103+
models.BigAutoField(
104+
auto_created=True,
105+
primary_key=True,
106+
serialize=False,
107+
verbose_name="ID",
108+
),
109+
),
110+
(
111+
"ens_id",
112+
models.TextField(
113+
db_index=True, help_text="Ensembl Protein ID", unique=True
114+
),
115+
),
116+
("chrom", models.TextField(help_text="Chromosome", null=True)),
117+
(
118+
"start",
119+
models.PositiveIntegerField(
120+
help_text="Start position of the protein sequence", null=True
121+
),
122+
),
123+
(
124+
"end",
125+
models.PositiveIntegerField(
126+
help_text="End position of the protein sequence", null=True
127+
),
128+
),
129+
(
130+
"gene",
131+
models.ForeignKey(
132+
help_text="The gene encoding this protein",
133+
on_delete=django.db.models.deletion.CASCADE,
134+
related_name="proteins",
135+
to="core.gene",
136+
),
137+
),
138+
],
139+
),
140+
migrations.AddField(
141+
model_name="trait",
142+
name="metabolite",
143+
field=models.ForeignKey(
144+
null=True,
145+
on_delete=django.db.models.deletion.CASCADE,
146+
related_name="trait",
147+
to="core.metabolite",
148+
),
149+
),
150+
migrations.AddField(
151+
model_name="trait",
152+
name="methyl_probe",
153+
field=models.ForeignKey(
154+
null=True,
155+
on_delete=django.db.models.deletion.CASCADE,
156+
related_name="trait",
157+
to="core.methylprobe",
158+
),
159+
),
160+
migrations.AddField(
161+
model_name="trait",
162+
name="protein",
163+
field=models.ForeignKey(
164+
null=True,
165+
on_delete=django.db.models.deletion.CASCADE,
166+
related_name="trait",
167+
to="core.protein",
168+
),
169+
),
170+
]

colocus/core/models.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,37 @@ class Exon(models.Model):
314314
end = models.PositiveIntegerField(null=True, blank=False, help_text="End position of the exon")
315315

316316

317+
class MethylProbe(models.Model):
318+
uuid = models.TextField(
319+
null=False, blank=False, db_index=True, unique=True,
320+
help_text="ID of the methylation probe, e.g. cg00000029")
321+
322+
chrom = models.TextField(null=True, blank=False, help_text="Chromosome")
323+
pos = models.PositiveIntegerField(null=True, blank=False, help_text="Position of the methylation probe")
324+
325+
326+
class Protein(models.Model):
327+
ens_id = models.TextField(null=False, blank=False, db_index=True, unique=True, help_text="Ensembl Protein ID")
328+
329+
gene = models.ForeignKey(
330+
Gene, on_delete=models.CASCADE,
331+
help_text="The gene encoding this protein",
332+
related_name="proteins")
333+
334+
chrom = models.TextField(null=True, blank=False, help_text="Chromosome")
335+
start = models.PositiveIntegerField(null=True, blank=False, help_text="Start position of the protein sequence")
336+
end = models.PositiveIntegerField(null=True, blank=False, help_text="End position of the protein sequence")
337+
338+
class Metabolite(models.Model):
339+
uuid = models.TextField(
340+
null=False, blank=False, db_index=True, unique=True,
341+
help_text="ID of the metabolite, e.g. HMDB0000122 or CHEBI:17234")
342+
343+
name = models.TextField(
344+
null=False, blank=False,
345+
help_text="Name of the metabolite, e.g. '2,6-Diethylpyrazine'")
346+
347+
317348
class Trait(models.Model):
318349
"""
319350
A trait is a phenotype or other biological property that has been studied in one or more analyses. This could be a
@@ -347,6 +378,27 @@ class Trait(models.Model):
347378
null=True
348379
)
349380

381+
protein = models.ForeignKey(
382+
Protein,
383+
on_delete=models.CASCADE,
384+
related_name='trait',
385+
null=True
386+
)
387+
388+
methyl_probe = models.ForeignKey(
389+
MethylProbe,
390+
on_delete=models.CASCADE,
391+
related_name='trait',
392+
null=True
393+
)
394+
395+
metabolite = models.ForeignKey(
396+
Metabolite,
397+
on_delete=models.CASCADE,
398+
related_name='trait',
399+
null=True
400+
)
401+
350402
phenotype = models.ForeignKey(
351403
Phenotype,
352404
on_delete=models.CASCADE,

scripts/load_dataset.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@
4949
LDStats,
5050
LeadVariant,
5151
MarginalAnalysis,
52+
Metabolite,
53+
MethylProbe,
5254
Person,
5355
Phenotype,
56+
Protein,
5457
Publication,
5558
Study,
5659
Trait,
@@ -466,6 +469,9 @@ def load_one_marginal(
466469
gene = v.pop('gene', None)
467470
exon = v.pop('exon', None)
468471
pheno = v.pop('phenotype', None)
472+
metabolite = v.pop('metabolite', None)
473+
methyl_probe = v.pop('probe', None)
474+
protein = v.pop('protein', None)
469475

470476
# trait, created = Trait.objects.get_or_create(**v)
471477
trait = get_by_id_or_create(Trait, {'uuid': v['uuid']}, v)
@@ -481,13 +487,30 @@ def load_one_marginal(
481487
exon = get_by_id_or_create(Exon, {'ens_id': exon['ens_id']}, exon)
482488
trait.exon = exon
483489

490+
if protein:
491+
protein["gene"] = gene
492+
protein = get_by_id_or_create(Protein, {'ens_id': v['uuid']}, protein)
493+
trait.protein = protein
494+
484495
if pheno:
485496
# pheno, created = Phenotype.objects.get_or_create(**pheno)
486497
if "uuid" not in pheno:
487498
pheno["uuid"] = v["uuid"]
488499
pheno = get_by_id_or_create(Phenotype, {'uuid': pheno['uuid']}, pheno)
489500
trait.phenotype = pheno
490501

502+
if metabolite:
503+
if "uuid" not in metabolite:
504+
metabolite["uuid"] = v["uuid"]
505+
metabolite = get_by_id_or_create(Metabolite, {'uuid': metabolite['uuid']}, metabolite)
506+
trait.metabolite = metabolite
507+
508+
if methyl_probe:
509+
if "uuid" not in methyl_probe:
510+
methyl_probe["uuid"] = v["uuid"]
511+
methyl_probe = get_by_id_or_create(MethylProbe, {'uuid': methyl_probe['uuid']}, methyl_probe)
512+
trait.methyl_probe = methyl_probe
513+
491514
trait.save()
492515
marginal.trait = trait
493516
else:

0 commit comments

Comments
 (0)