Skip to content

Commit

Permalink
Merge pull request #105 from romgrk/experiments-add-relations
Browse files Browse the repository at this point in the history
experiments: define biosamples & donor
  • Loading branch information
zxenia committed Mar 31, 2020
2 parents 6ad94f3 + f58d2ee commit 11cb03d
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 10 deletions.
3 changes: 3 additions & 0 deletions chord_metadata_service/experiments/descriptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@

"other_fields": "The other fields for the experiment",

"biosample": "Biosamples on which this experiment was done",
"individual": "Donor on which this experiment was done",

**EXTRA_PROPERTIES
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Generated by Django 2.2.11 on 2020-03-31 18:41

import chord_metadata_service.restapi.validators
import django.contrib.postgres.fields
import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('patients', '0004_auto_20200129_1537'),
('phenopackets', '0004_auto_20200129_1537'),
('experiments', '0002_auto_20200327_1728'),
]

operations = [
migrations.AddField(
model_name='experiment',
name='biosample',
field=models.ForeignKey(blank=True, help_text='Biosamples on which this experiment was done', null=True, on_delete=django.db.models.deletion.SET_NULL, to='phenopackets.Biosample'),
),
migrations.AddField(
model_name='experiment',
name='individual',
field=models.ForeignKey(blank=True, help_text='Donor on which this experiment was done', null=True, on_delete=django.db.models.deletion.SET_NULL, to='patients.Individual'),
),
migrations.AlterField(
model_name='experiment',
name='experiment_ontology',
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, help_text='(Ontology: OBI) links to experiment ontology information.', null=True, validators=[chord_metadata_service.restapi.validators.JsonSchemaValidator({'$id': 'ONTOLOGY_CLASS_LIST', '$schema': 'http://json-schema.org/draft-07/schema#', 'description': 'Ontology class list', 'items': {'$id': 'ONTOLOGY_CLASS', '$schema': 'http://json-schema.org/draft-07/schema#', 'additionalProperties': False, 'description': 'todo', 'properties': {'id': {'description': 'CURIE style identifier.', 'type': 'string'}, 'label': {'description': 'Human-readable class name.', 'type': 'string'}}, 'required': ['id', 'label'], 'title': 'Ontology class schema', 'type': 'object'}, 'title': 'Ontology class list', 'type': 'array'})]),
),
migrations.AlterField(
model_name='experiment',
name='molecule',
field=models.CharField(blank=True, choices=[('total RNA', 'total RNA'), ('polyA RNA', 'polyA RNA'), ('cytoplasmic RNA', 'cytoplasmic RNA'), ('nuclear RNA', 'nuclear RNA'), ('small RNA', 'small RNA'), ('genomic DNA', 'genomic DNA'), ('protein', 'protein'), ('other', 'other')], help_text='(Controlled Vocabulary) The type of molecule that was extracted from the biological material. Include one of the following: total RNA, polyA RNA, cytoplasmic RNA, nuclear RNA, small RNA, genomic DNA, protein, or other.', max_length=20, null=True),
),
migrations.AlterField(
model_name='experiment',
name='molecule_ontology',
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, help_text='(Ontology: SO) links to molecule ontology information.', null=True, validators=[chord_metadata_service.restapi.validators.JsonSchemaValidator({'$id': 'ONTOLOGY_CLASS_LIST', '$schema': 'http://json-schema.org/draft-07/schema#', 'description': 'Ontology class list', 'items': {'$id': 'ONTOLOGY_CLASS', '$schema': 'http://json-schema.org/draft-07/schema#', 'additionalProperties': False, 'description': 'todo', 'properties': {'id': {'description': 'CURIE style identifier.', 'type': 'string'}, 'label': {'description': 'Human-readable class name.', 'type': 'string'}}, 'required': ['id', 'label'], 'title': 'Ontology class schema', 'type': 'object'}, 'title': 'Ontology class list', 'type': 'array'})]),
),
migrations.AlterField(
model_name='experiment',
name='other_fields',
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, help_text='The other fields for the experiment', null=True, validators=[chord_metadata_service.restapi.validators.JsonSchemaValidator({'$id': 'KEY_VALUE_OBJECT', '$schema': 'http://json-schema.org/draft-07/schema#', 'additionalProperties': False, 'description': 'The schema represents a key-value object.', 'patternProperties': {'^.*$': {'type': 'string'}}, 'title': 'Key-value object', 'type': 'object'})]),
),
migrations.AlterField(
model_name='experiment',
name='qc_flags',
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(help_text='Any quanlity control observations can be noted here. This field can be omitted if empty', max_length=100), blank=True, default=list, null=True, size=None),
),
migrations.AlterField(
model_name='experiment',
name='reference_registry_id',
field=models.CharField(blank=True, help_text='The IHEC EpiRR ID for this dataset, only for IHEC Reference Epigenome datasets. Otherwise leave empty.', max_length=30, null=True),
),
]
23 changes: 15 additions & 8 deletions chord_metadata_service/experiments/models.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from django.db import models
from django.db.models import CharField
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from django.contrib.postgres.fields import JSONField, ArrayField
from chord_metadata_service.restapi.models import IndexableMixin
from chord_metadata_service.restapi.description_utils import rec_help
from chord_metadata_service.restapi.validators import ontologyListValidator, keyValueValidator
from chord_metadata_service.restapi.schemas import ONTOLOGY_CLASS_LIST, KEY_VALUE_OBJECT
from chord_metadata_service.patients.models import Individual
from chord_metadata_service.phenopackets.models import Biosample
import chord_metadata_service.experiments.descriptions as d

class Experiment(models.Model, IndexableMixin):
Expand Down Expand Up @@ -38,16 +39,22 @@ class Experiment(models.Model, IndexableMixin):

id = CharField(primary_key=True, max_length=200, help_text=rec_help(d.EXPERIMENT, 'id'))

reference_registry_id = CharField(max_length=30, null=True, help_text=rec_help(d.EXPERIMENT, 'reference_registry_id'))
qc_flags = ArrayField(CharField(max_length=100, help_text=rec_help(d.EXPERIMENT, 'qc_flags')), null=True, default=list)
reference_registry_id = CharField(max_length=30, blank=True, null=True, help_text=rec_help(d.EXPERIMENT, 'reference_registry_id'))
qc_flags = ArrayField(CharField(max_length=100, help_text=rec_help(d.EXPERIMENT, 'qc_flags')), null=True, blank=True, default=list)
experiment_type = CharField(max_length=30, help_text=rec_help(d.EXPERIMENT, 'experiment_type'))
experiment_ontology = JSONField(null=True, validators=[ontologyListValidator], help_text=rec_help(d.EXPERIMENT, 'experiment_ontology'))
molecule_ontology = JSONField(null=True, validators=[ontologyListValidator], help_text=rec_help(d.EXPERIMENT, 'molecule_ontology'))
molecule = CharField(choices=MOLECULE, max_length=20, null=True, help_text=rec_help(d.EXPERIMENT, 'molecule'))

experiment_ontology = JSONField(blank=True, null=True, validators=[ontologyListValidator], help_text=rec_help(d.EXPERIMENT, 'experiment_ontology'))
molecule_ontology = JSONField(blank=True, null=True, validators=[ontologyListValidator], help_text=rec_help(d.EXPERIMENT, 'molecule_ontology'))
molecule = CharField(choices=MOLECULE, max_length=20, blank=True, null=True, help_text=rec_help(d.EXPERIMENT, 'molecule'))
library_strategy = CharField(choices=LIBRARY_STRATEGY, max_length=25, help_text=rec_help(d.EXPERIMENT, 'library_strategy'))

other_fields = JSONField(null=True, validators=[keyValueValidator], help_text=rec_help(d.EXPERIMENT, 'other_fields'))
other_fields = JSONField(blank=True, null=True, validators=[keyValueValidator], help_text=rec_help(d.EXPERIMENT, 'other_fields'))

biosample = models.ForeignKey(Biosample, on_delete=models.SET_NULL, blank=True, null=True, help_text=rec_help(d.EXPERIMENT, 'biosample'))
individual = models.ForeignKey(Individual, on_delete=models.SET_NULL, blank=True, null=True, help_text=rec_help(d.EXPERIMENT, 'individual'))

def clean(self):
if not (self.biosample or self.individual):
raise ValidationError('Either Biosamples or Individual must be specified')

def __str__(self):
return str(self.id)
22 changes: 20 additions & 2 deletions chord_metadata_service/experiments/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from django.test import TestCase
from django.core.exceptions import ValidationError
from rest_framework import serializers
from chord_metadata_service.patients.models import Individual
from ..models import Experiment


class ExperimentTest(TestCase):
""" Test module for Experiment model """

def setUp(self):
Individual.objects.create(id='patient:1', sex='FEMALE', age={"age": "P25Y3M2D"})
Experiment.objects.create(
id='experiment:1',
reference_registry_id='',
reference_registry_id='some_id',
qc_flags=['flag 1', 'flag 2'],
experiment_type='Chromatin Accessibility',
experiment_ontology=[{"id": "ontology:1", "label": "Ontology term 1"}],
Expand All @@ -25,23 +28,38 @@ def create(self, **kwargs):
e.save()

def test_validation(self):
individual_one = Individual.objects.get(id='patient:1')

# Invalid experiment_ontology
self.assertRaises(serializers.ValidationError, self.create,
id='experiment:2',
library_strategy='Bisulfite-Seq',
experiment_type='Chromatin Accessibility',
experiment_ontology=["invalid_value"],
individual=individual_one
)

# Invalid molecule_ontology
self.assertRaises(serializers.ValidationError, self.create,
id='experiment:2',
library_strategy='Bisulfite-Seq',
experiment_type='Chromatin Accessibility',
molecule_ontology=[{"id": "some_id"}],
individual=individual_one
)

# Invalid value in other_fields
self.assertRaises(serializers.ValidationError, self.create,
id='experiment:2',
library_strategy='Bisulfite-Seq',
experiment_type='Chromatin Accessibility',
other_fields={"some_field": "value", "invalid_value": 42}
other_fields={"some_field": "value", "invalid_value": 42},
individual=individual_one
)

# Missing individual or biosamples
self.assertRaises(ValidationError, self.create,
id='experiment:2',
library_strategy='Bisulfite-Seq',
experiment_type='Chromatin Accessibility'
)

0 comments on commit 11cb03d

Please sign in to comment.